Пример #1
0
/* 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;
}
Пример #2
0
/* 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;
}
Пример #3
0
static void
xac_free (void *x)
{
  if (x) {
    struct xac_entry *p = x;

    guestfs_free_xattr_list (p->xattrs);
    lsc_free (x);
  }
}
Пример #4
0
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);
}
Пример #5
0
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;
}
Пример #6
0
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;
}
Пример #7
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;
}
Пример #8
0
/* 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;
}