/* Ditto as above. */ static int mount_local_listxattr (const char *path, char *list, size_t size) { DECL_G (); DEBUG_CALL ("%s, %p, %zu", path, list, size); const struct guestfs_xattr_list *xattrs; int free_attrs = 0; xattrs = xac_lookup (g, path); if (xattrs == NULL) { xattrs = guestfs_lgetxattrs (g, path); if (xattrs == NULL) RETURN_ERRNO; free_attrs = 1; } /* Calculate how much space is required to hold the result. */ size_t space = 0; size_t len; size_t i; for (i = 0; i < xattrs->len; ++i) { len = strlen (xattrs->val[i].attrname) + 1; space += len; } /* The listxattr man page is unclear, but if list == NULL then we * return the space required (the caller then makes a second syscall * after allocating the required amount of space). If list != NULL * then it's not clear what we should do, but it appears we should * copy as much as possible and return -ERANGE if there's not enough * space in the buffer. */ ssize_t r; if (list == NULL) { r = space; goto out; } r = 0; for (i = 0; i < xattrs->len; ++i) { len = strlen (xattrs->val[i].attrname) + 1; if (size >= len) { memcpy (list, xattrs->val[i].attrname, len); size -= len; list += len; r += len; } else { r = -ERANGE; break; } } out: if (free_attrs) guestfs_free_xattr_list ((struct guestfs_xattr_list *) xattrs); return r; }
/* The guestfs(3) API for getting xattrs is much easier to use * than the real syscall. Unfortunately we now have to emulate * the real syscall using that API :-( */ static int mount_local_getxattr (const char *path, const char *name, char *value, size_t size) { DECL_G (); DEBUG_CALL ("%s, %s, %p, %zu", path, name, value, size); const struct guestfs_xattr_list *xattrs; int free_attrs = 0; xattrs = xac_lookup (g, path); if (xattrs == NULL) { xattrs = guestfs_lgetxattrs (g, path); if (xattrs == NULL) RETURN_ERRNO; free_attrs = 1; } /* Find the matching attribute (index in 'i'). */ ssize_t r; size_t i; for (i = 0; i < xattrs->len; ++i) { if (STREQ (xattrs->val[i].attrname, name)) break; } if (i == xattrs->len) { /* not found */ r = -ENOATTR; goto out; } /* The getxattr man page is unclear, but if value == NULL then we * return the space required (the caller then makes a second syscall * after allocating the required amount of space). If value != NULL * then it's not clear what we should do, but it appears we should * copy as much as possible and return -ERANGE if there's not enough * space in the buffer. */ size_t sz = xattrs->val[i].attrval_len; if (value == NULL) { r = sz; goto out; } if (sz <= size) r = sz; else { r = -ERANGE; sz = size; } memcpy (value, xattrs->val[i].attrval, sz); out: if (free_attrs) guestfs_free_xattr_list ((struct guestfs_xattr_list *) xattrs); return r; }
static void xac_free (void *x) { if (x) { struct xac_entry *p = x; guestfs_free_xattr_list (p->xattrs); lsc_free (x); } }
static void free_tree (struct tree *t) { size_t i; for (i = 0; i < t->nr_files; ++i) { free (t->files[i].path); guestfs_free_statns (t->files[i].stat); guestfs_free_xattr_list (t->files[i].xattrs); free (t->files[i].csum); } free (t->files); free (t); }
struct guestfs_xattr_list * guestfs_impl_lxattrlist (guestfs_h *g, const char *dir, char *const *names) { size_t len = guestfs_int_count_strings (names); size_t i, old_len; struct guestfs_xattr_list *ret; ret = safe_malloc (g, sizeof *ret); ret->len = 0; ret->val = NULL; while (len > 0) { CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL; /* Note we don't need to free up the strings because take_strings * does not do a deep copy. */ CLEANUP_FREE char **first = take_strings (g, names, LXATTRLIST_MAX, &names); len = len <= LXATTRLIST_MAX ? 0 : len - LXATTRLIST_MAX; xattrs = guestfs_internal_lxattrlist (g, dir, first); if (xattrs == NULL) { guestfs_free_xattr_list (ret); return NULL; } /* Append xattrs to ret. */ old_len = ret->len; ret->len += xattrs->len; ret->val = safe_realloc (g, ret->val, ret->len * sizeof (struct guestfs_xattr)); for (i = 0; i < xattrs->len; ++i, ++old_len) { /* We have to make a deep copy of the attribute name and value. */ ret->val[old_len].attrname = safe_strdup (g, xattrs->val[i].attrname); ret->val[old_len].attrval = safe_malloc (g, xattrs->val[i].attrval_len); ret->val[old_len].attrval_len = xattrs->val[i].attrval_len; memcpy (ret->val[old_len].attrval, xattrs->val[i].attrval, xattrs->val[i].attrval_len); } } return ret; }
static int mount_local_readdir (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { DECL_G (); DEBUG_CALL ("%s, %p, %ld", path, buf, (long) offset); time_t now; time (&now); dir_cache_remove_all_expired (g, now); struct guestfs_dirent_list *ents; ents = guestfs_readdir (g, path); if (ents == NULL) RETURN_ERRNO; size_t i; for (i = 0; i < ents->len; ++i) { struct stat stat; memset (&stat, 0, sizeof stat); stat.st_ino = ents->val[i].ino; switch (ents->val[i].ftyp) { case 'b': stat.st_mode = S_IFBLK; break; case 'c': stat.st_mode = S_IFCHR; break; case 'd': stat.st_mode = S_IFDIR; break; case 'f': stat.st_mode = S_IFIFO; break; case 'l': stat.st_mode = S_IFLNK; break; case 'r': stat.st_mode = S_IFREG; break; case 's': stat.st_mode = S_IFSOCK; break; case 'u': case '?': default: stat.st_mode = 0; } /* Copied from the example, which also ignores 'offset'. I'm * not quite sure how this is ever supposed to work on large * directories. XXX */ if (filler (buf, ents->val[i].name, &stat, 0)) break; } /* Now prepopulate the directory caches. This step is just an * optimization, don't worry if it fails. */ char **names = malloc ((ents->len + 1) * sizeof (char *)); if (names) { for (i = 0; i < ents->len; ++i) names[i] = ents->val[i].name; names[i] = NULL; struct guestfs_stat_list *ss = guestfs_lstatlist (g, path, names); if (ss) { for (i = 0; i < ss->len; ++i) { if (ss->val[i].ino >= 0) { struct stat statbuf; statbuf.st_dev = ss->val[i].dev; statbuf.st_ino = ss->val[i].ino; statbuf.st_mode = ss->val[i].mode; statbuf.st_nlink = ss->val[i].nlink; statbuf.st_uid = ss->val[i].uid; statbuf.st_gid = ss->val[i].gid; statbuf.st_rdev = ss->val[i].rdev; statbuf.st_size = ss->val[i].size; statbuf.st_blksize = ss->val[i].blksize; statbuf.st_blocks = ss->val[i].blocks; statbuf.st_atime = ss->val[i].atime; statbuf.st_mtime = ss->val[i].mtime; statbuf.st_ctime = ss->val[i].ctime; lsc_insert (g, path, names[i], now, &statbuf); } } guestfs_free_stat_list (ss); } struct guestfs_xattr_list *xattrs = guestfs_lxattrlist (g, path, names); if (xattrs) { size_t ni, num; struct guestfs_xattr *first; struct guestfs_xattr_list *copy; for (i = 0, ni = 0; i < xattrs->len; ++i, ++ni) { /* assert (strlen (xattrs->val[i].attrname) == 0); */ if (xattrs->val[i].attrval_len > 0) { ++i; first = &xattrs->val[i]; num = 0; for (; i < xattrs->len && strlen (xattrs->val[i].attrname) > 0; ++i) num++; copy = copy_xattr_list (first, num); if (copy) xac_insert (g, path, names[ni], now, copy); i--; } } guestfs_free_xattr_list (xattrs); } char **links = guestfs_readlinklist (g, path, names); if (links) { for (i = 0; names[i] != NULL; ++i) { if (links[i][0]) /* Note that rlc_insert owns the string links[i] after this, */ rlc_insert (g, path, names[i], now, links[i]); else /* which is why we have to free links[i] here. */ free (links[i]); } free (links); /* free the array, not the strings */ } free (names); } guestfs_free_dirent_list (ents); return 0; }
/* Visit each directory/file/etc entry in the tree. This just stores * the data in the tree. Note we don't store file content, but we * keep the guestfs handle open so we can pull that out later if we * need to. */ static int visit_entry (const char *dir, const char *name, const struct guestfs_statns *stat_orig, const struct guestfs_xattr_list *xattrs_orig, void *vt) { struct tree *t = vt; char *path = NULL, *csum = NULL; struct guestfs_statns *stat = NULL; struct guestfs_xattr_list *xattrs = NULL; size_t i; path = full_path (dir, name); /* Copy the stats and xattrs because the visit function will * free them after we return. */ stat = guestfs_copy_statns (stat_orig); if (stat == NULL) { perror ("guestfs_copy_stat"); goto error; } xattrs = guestfs_copy_xattr_list (xattrs_orig); if (xattrs == NULL) { perror ("guestfs_copy_xattr_list"); goto error; } if (checksum && is_reg (stat->st_mode)) { csum = guestfs_checksum (t->g, checksum, path); if (!csum) goto error; } /* If --atime option was NOT passed, flatten the atime field. */ if (!atime) stat->st_atime_sec = stat->st_atime_nsec = 0; /* If --dir-links option was NOT passed, flatten nlink field in * directories. */ if (!dir_links && is_dir (stat->st_mode)) stat->st_nlink = 0; /* If --dir-times option was NOT passed, flatten time fields in * directories. */ if (!dir_times && is_dir (stat->st_mode)) stat->st_atime_sec = stat->st_mtime_sec = stat->st_ctime_sec = stat->st_atime_nsec = stat->st_mtime_nsec = stat->st_ctime_nsec = 0; /* Add the pathname and stats to the list. */ i = t->nr_files++; if (i >= t->allocated) { struct file *old_files = t->files; size_t old_allocated = t->allocated; /* Number of entries in an F15 guest was 111524, and in a * Windows guest was 10709. */ if (old_allocated == 0) t->allocated = 1024; else t->allocated = old_allocated * 2; t->files = realloc (old_files, t->allocated * sizeof (struct file)); if (t->files == NULL) { perror ("realloc"); t->files = old_files; t->allocated = old_allocated; goto error; } } t->files[i].path = path; t->files[i].stat = stat; t->files[i].xattrs = xattrs; t->files[i].csum = csum; return 0; error: free (path); free (csum); guestfs_free_statns (stat); guestfs_free_xattr_list (xattrs); return -1; }
/* This is implemented library-side in order to get around potential * protocol limits. * * A journal record can contain an arbitrarily large amount of data * (stuff like core dumps in particular). To save the user from * having to deal with it, the implementation uses an internal * function that downloads to a FileOut, and we reconstruct the * hashtable entries from that. */ struct guestfs_xattr_list * guestfs__journal_get (guestfs_h *g) { CLEANUP_UNLINK_FREE char *tmpfile = NULL; CLEANUP_FREE char *buf = NULL; struct stat statbuf; struct guestfs_xattr_list *ret = NULL; char *p, *eofield, *eobuf; int fd = -1; size_t i, j, size; uint64_t len; if (guestfs___lazy_make_tmpdir (g) == -1) goto err; tmpfile = safe_asprintf (g, "%s/journal%d", g->tmpdir, ++g->unique); if (guestfs_internal_journal_get (g, tmpfile) == -1) goto err; fd = open (tmpfile, O_RDONLY|O_CLOEXEC); if (fd == -1) { perrorf (g, "open: %s", tmpfile); goto err; } /* Read the whole file into memory. */ if (fstat (fd, &statbuf) == -1) { perrorf (g, "stat: %s", tmpfile); goto err; } /* Don't use safe_malloc. Want to return an errno to the caller. */ size = statbuf.st_size; buf = malloc (size+1); if (!buf) { perrorf (g, "malloc: %zu bytes", size); goto err; } eobuf = &buf[size]; *eobuf = '\0'; /* Makes strchr etc safe. */ if (full_read (fd, buf, size) != size) { perrorf (g, "full-read: %s: %zu bytes", tmpfile, size); goto err; } if (close (fd) == -1) { perrorf (g, "close: %s", tmpfile); goto err; } fd = -1; j = 0; ret = safe_malloc (g, sizeof *ret); ret->len = 0; ret->val = NULL; /* There is a simple, private protocol employed here (note: it may * be changed at any time), where fields are sent using a big-endian * 64 bit length field followed by N bytes of 'field=data' binary * data. */ for (i = 0; i < size; ) { if (i+8 > size) { error (g, "invalid data from guestfs_internal_journal_get: " "truncated: " "size=%zu, i=%zu", size, i); goto err; } memcpy(&len, &buf[i], sizeof(len)); len = be64toh (len); i += 8; eofield = &buf[i+len]; if (eofield > eobuf) { error (g, "invalid data from guestfs_internal_journal_get: " "length field is too large: " "size=%zu, i=%zu, len=%" PRIu64, size, i, len); goto err; } p = strchr (&buf[i], '='); if (!p || p >= eofield) { error (g, "invalid data from guestfs_internal_journal_get: " "no '=' found separating field name and data: " "size=%zu, i=%zu, p=%p", size, i, p); goto err; } *p = '\0'; j++; ret->val = safe_realloc (g, ret->val, j * sizeof (struct guestfs_xattr)); ret->val[j-1].attrname = safe_strdup (g, &buf[i]); ret->val[j-1].attrval_len = eofield - (p+1); ret->val[j-1].attrval = safe_memdup (g, p+1, eofield - (p+1)); ret->len = j; i += len; } return ret; /* caller frees */ err: guestfs_free_xattr_list (ret); if (fd >= 0) close (fd); return NULL; }