#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
return ret; /* caller frees */
}
+char **
+do_readlinklist (const char *path, char *const *names)
+{
+ int fd_cwd;
+ size_t i;
+ ssize_t r;
+ char link[PATH_MAX];
+ const char *str;
+ char **ret = NULL;
+ int size = 0, alloc = 0;
+
+ CHROOT_IN;
+ fd_cwd = open (path, O_RDONLY | O_DIRECTORY);
+ CHROOT_OUT;
+
+ if (fd_cwd == -1) {
+ reply_with_perror ("readlinklist: %s", path);
+ return NULL;
+ }
+
+ for (i = 0; names[i] != NULL; ++i) {
+ r = readlinkat (fd_cwd, names[i], link, sizeof link);
+ if (r >= PATH_MAX) {
+ reply_with_perror ("readlinkat: returned link is too long");
+ free_strings (ret);
+ close (fd_cwd);
+ return NULL;
+ }
+ /* Because of the way this function is intended to be used,
+ * we actually expect to see errors here, and they are not fatal.
+ */
+ if (r >= 0) {
+ link[r] = '\0';
+ str = link;
+ } else
+ str = "";
+ if (add_string (&ret, &size, &alloc, str) == -1) {
+ close (fd_cwd);
+ return NULL;
+ }
+ }
+
+ close (fd_cwd);
+
+ if (add_string (&ret, &size, &alloc, NULL) == -1)
+ return NULL;
+
+ return ret;
+}
+
static int
_link (const char *flag, int symbolic, const char *target, const char *linkname)
{
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
+#include <fcntl.h>
#include <unistd.h>
#include "../src/guestfs_protocol.h"
return ret;
}
+guestfs_int_stat_list *
+do_lstatlist (const char *path, char *const *names)
+{
+ int path_fd;
+ guestfs_int_stat_list *ret;
+ size_t i, nr_names;
+
+ nr_names = count_strings (names);
+
+ ret = malloc (sizeof *ret);
+ if (!ret) {
+ reply_with_perror ("malloc");
+ return NULL;
+ }
+ ret->guestfs_int_stat_list_len = nr_names;
+ ret->guestfs_int_stat_list_val = calloc (nr_names, sizeof (guestfs_int_stat));
+ if (ret->guestfs_int_stat_list_val == NULL) {
+ reply_with_perror ("malloc");
+ free (ret);
+ return NULL;
+ }
+
+ CHROOT_IN;
+ path_fd = open (path, O_RDONLY | O_DIRECTORY);
+ CHROOT_OUT;
+
+ if (path_fd == -1) {
+ reply_with_perror ("lstatlist: %s", path);
+ free (ret->guestfs_int_stat_list_val);
+ free (ret);
+ return NULL;
+ }
+
+ for (i = 0; names[i] != NULL; ++i) {
+ int r;
+ struct stat statbuf;
+
+ r = fstatat (path_fd, names[i], &statbuf, AT_SYMLINK_NOFOLLOW);
+ if (r == -1)
+ ret->guestfs_int_stat_list_val[i].ino = -1;
+ else {
+ ret->guestfs_int_stat_list_val[i].dev = statbuf.st_dev;
+ ret->guestfs_int_stat_list_val[i].ino = statbuf.st_ino;
+ ret->guestfs_int_stat_list_val[i].mode = statbuf.st_mode;
+ ret->guestfs_int_stat_list_val[i].nlink = statbuf.st_nlink;
+ ret->guestfs_int_stat_list_val[i].uid = statbuf.st_uid;
+ ret->guestfs_int_stat_list_val[i].gid = statbuf.st_gid;
+ ret->guestfs_int_stat_list_val[i].rdev = statbuf.st_rdev;
+ ret->guestfs_int_stat_list_val[i].size = statbuf.st_size;
+ ret->guestfs_int_stat_list_val[i].blksize = statbuf.st_blksize;
+ ret->guestfs_int_stat_list_val[i].blocks = statbuf.st_blocks;
+ ret->guestfs_int_stat_list_val[i].atime = statbuf.st_atime;
+ ret->guestfs_int_stat_list_val[i].mtime = statbuf.st_mtime;
+ ret->guestfs_int_stat_list_val[i].ctime = statbuf.st_ctime;
+ }
+ }
+
+ if (close (path_fd) == -1) {
+ reply_with_perror ("close: %s", path);
+ free (ret->guestfs_int_stat_list_val);
+ free (ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
guestfs_int_statvfs *
do_statvfs (const char *path)
{
return 0;
}
+guestfs_int_xattr_list *
+do_lxattrlist (const char *path, char *const *names)
+{
+#if defined(HAVE_LLISTXATTR) && defined(HAVE_LGETXATTR)
+ /* XXX This would be easier if the kernel had lgetxattrat. In the
+ * meantime we use this buffer to store the whole path name.
+ */
+ char pathname[PATH_MAX];
+ size_t path_len = strlen (path);
+ guestfs_int_xattr_list *ret = NULL;
+ int i, j;
+ size_t k, m, nr_attrs;
+ ssize_t len, vlen;
+ char *buf = NULL;
+
+ if (path_len >= PATH_MAX) {
+ reply_with_perror ("lxattrlist: path longer than PATH_MAX");
+ goto error;
+ }
+
+ strcpy (pathname, path);
+
+ ret = malloc (sizeof (*ret));
+ if (ret == NULL) {
+ reply_with_perror ("malloc");
+ goto error;
+ }
+
+ ret->guestfs_int_xattr_list_len = 0;
+ ret->guestfs_int_xattr_list_val = NULL;
+
+ for (k = 0; names[k] != NULL; ++k) {
+ /* Be careful in here about which errors cause the whole call
+ * to abort, and which errors allow us to continue processing
+ * the call, recording a special "error attribute" in the
+ * outgoing struct list.
+ */
+ if (path_len + strlen (names[k]) + 2 > PATH_MAX) {
+ reply_with_perror ("lxattrlist: path and name longer than PATH_MAX");
+ goto error;
+ }
+ pathname[path_len] = '/';
+ strcpy (&pathname[path_len+1], names[k]);
+
+ /* Reserve space for the special attribute. */
+ void *newptr =
+ realloc (ret->guestfs_int_xattr_list_val,
+ (ret->guestfs_int_xattr_list_len+1)*sizeof (guestfs_int_xattr));
+ if (newptr == NULL) {
+ reply_with_perror ("realloc");
+ goto error;
+ }
+ ret->guestfs_int_xattr_list_val = newptr;
+ ret->guestfs_int_xattr_list_len++;
+
+ guestfs_int_xattr *entry =
+ &ret->guestfs_int_xattr_list_val[ret->guestfs_int_xattr_list_len-1];
+ entry->attrname = NULL;
+ entry->attrval.attrval_len = 0;
+ entry->attrval.attrval_val = NULL;
+
+ entry->attrname = strdup ("");
+ if (entry->attrname == NULL) {
+ reply_with_perror ("strdup");
+ goto error;
+ }
+
+ CHROOT_IN;
+ len = llistxattr (pathname, NULL, 0);
+ CHROOT_OUT;
+ if (len == -1)
+ continue; /* not fatal */
+
+ buf = malloc (len);
+ if (buf == NULL) {
+ reply_with_perror ("malloc");
+ goto error;
+ }
+
+ CHROOT_IN;
+ len = llistxattr (pathname, buf, len);
+ CHROOT_OUT;
+ if (len == -1)
+ continue; /* not fatal */
+
+ /* What we get from the kernel is a string "foo\0bar\0baz" of length
+ * len. First count the strings.
+ */
+ nr_attrs = 0;
+ for (i = 0; i < len; i += strlen (&buf[i]) + 1)
+ nr_attrs++;
+
+ newptr =
+ realloc (ret->guestfs_int_xattr_list_val,
+ (ret->guestfs_int_xattr_list_len+nr_attrs) *
+ sizeof (guestfs_int_xattr));
+ if (newptr == NULL) {
+ reply_with_perror ("realloc");
+ goto error;
+ }
+ ret->guestfs_int_xattr_list_val = newptr;
+ ret->guestfs_int_xattr_list_len += nr_attrs;
+
+ /* entry[0] is the special attribute,
+ * entry[1..nr_attrs] are the attributes.
+ */
+ entry = &ret->guestfs_int_xattr_list_val[ret->guestfs_int_xattr_list_len-nr_attrs-1];
+ for (m = 1; m <= nr_attrs; ++m) {
+ entry[m].attrname = NULL;
+ entry[m].attrval.attrval_len = 0;
+ entry[m].attrval.attrval_val = NULL;
+ }
+
+ for (i = 0, j = 0; i < len; i += strlen (&buf[i]) + 1, ++j) {
+ CHROOT_IN;
+ vlen = lgetxattr (pathname, &buf[i], NULL, 0);
+ CHROOT_OUT;
+ if (vlen == -1) {
+ reply_with_perror ("getxattr");
+ goto error;
+ }
+
+ entry[j+1].attrname = strdup (&buf[i]);
+ entry[j+1].attrval.attrval_val = malloc (vlen);
+ entry[j+1].attrval.attrval_len = vlen;
+
+ if (entry[j+1].attrname == NULL ||
+ entry[j+1].attrval.attrval_val == NULL) {
+ reply_with_perror ("malloc");
+ goto error;
+ }
+
+ CHROOT_IN;
+ vlen = lgetxattr (pathname, &buf[i],
+ entry[j+1].attrval.attrval_val, vlen);
+ CHROOT_OUT;
+ if (vlen == -1) {
+ reply_with_perror ("getxattr");
+ goto error;
+ }
+ }
+
+ free (buf); buf = NULL;
+
+ char num[16];
+ snprintf (num, sizeof num, "%zu", nr_attrs);
+ entry[0].attrval.attrval_len = strlen (num) + 1;
+ entry[0].attrval.attrval_val = strdup (num);
+
+ if (entry[0].attrval.attrval_val == NULL) {
+ reply_with_perror ("strdup");
+ goto error;
+ }
+ }
+
+ /* If verbose, debug what we're about to send back. */
+ if (verbose) {
+ fprintf (stderr, "lxattrlist: returning: [\n");
+ for (k = 0; k < ret->guestfs_int_xattr_list_len; ++k) {
+ const guestfs_int_xattr *entry = &ret->guestfs_int_xattr_list_val[k];
+ if (strcmp (entry[0].attrname, "") != 0) {
+ fprintf (stderr, "ERROR: expecting empty attrname at k = %zu\n", k);
+ break;
+ }
+ fprintf (stderr, " %zu: special attrval = %s\n",
+ k, entry[0].attrval.attrval_val);
+ for (i = 1; k+i < ret->guestfs_int_xattr_list_len; ++i) {
+ if (strcmp (entry[i].attrname, "") == 0)
+ break;
+ fprintf (stderr, " name %s, value length %d\n",
+ entry[i].attrname, entry[i].attrval.attrval_len);
+ }
+ k += i-1;
+ }
+ fprintf (stderr, "]\n");
+ }
+
+ return ret;
+
+ error:
+ free (buf);
+ if (ret) {
+ if (ret->guestfs_int_xattr_list_val) {
+ for (k = 0; k < ret->guestfs_int_xattr_list_len; ++k) {
+ free (ret->guestfs_int_xattr_list_val[k].attrname);
+ free (ret->guestfs_int_xattr_list_val[k].attrval.attrval_val);
+ }
+ free (ret->guestfs_int_xattr_list_val);
+ }
+ free (ret);
+ }
+ return NULL;
+#else
+ reply_with_error ("lxattrlist: no support for llistxattr and lgetxattr");
+ return NULL;
+#endif
+}
+
#else /* no xattr.h */
guestfs_int_xattr_list *
return -1;
}
+guestfs_int_xattr_list *
+do_lxattrlist (const char *path, char *const *names)
+{
+ reply_with_error ("lxattrlist: no support for xattrs");
+ return NULL;
+}
+
#endif /* no xattr.h */
names, you will need to locate and parse the password file
yourself (Augeas support makes this relatively easy).");
+ ("lstatlist", (RStructList ("statbufs", "stat"), [Pathname "path"; StringList "names"]), 204, [],
+ [], (* XXX *)
+ "lstat on multiple files",
+ "\
+This call allows you to perform the C<guestfs_lstat> operation
+on multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a list of stat structs, with a one-to-one
+correspondence to the C<names> list. If any name did not exist
+or could not be lstat'd, then the C<ino> field of that structure
+is set to C<-1>.
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+See also C<guestfs_lxattrlist> for a similarly efficient call
+for getting extended attributes. Very long directory listings
+might cause the protocol message size to be exceeded, causing
+this call to fail. The caller must split up such requests
+into smaller groups of names.");
+
+ ("lxattrlist", (RStructList ("xattrs", "xattr"), [Pathname "path"; StringList "names"]), 205, [],
+ [], (* XXX *)
+ "lgetxattr on multiple files",
+ "\
+This call allows you to get the extended attributes
+of multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a flat list of xattr structs which must be
+interpreted sequentially. The first xattr struct always has a zero-length
+C<attrname>. C<attrval> in this struct is zero-length
+to indicate there was an error doing C<lgetxattr> for this
+file, I<or> is a C string which is a decimal number
+(the number of following attributes for this file, which could
+be C<\"0\">). Then after the first xattr struct are the
+zero or more attributes for the first named file.
+This repeats for the second and subsequent files.
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+See also C<guestfs_lstatlist> for a similarly efficient call
+for getting standard stats. Very long directory listings
+might cause the protocol message size to be exceeded, causing
+this call to fail. The caller must split up such requests
+into smaller groups of names.");
+
+ ("readlinklist", (RStringList "links", [Pathname "path"; StringList "names"]), 206, [],
+ [], (* XXX *)
+ "readlink on multiple files",
+ "\
+This call allows you to do a C<readlink> operation
+on multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a list of strings, with a one-to-one
+correspondence to the C<names> list. Each string is the
+value of the symbol link.
+
+If the C<readlink(2)> operation fails on any name, then
+the corresponding result string is the empty string C<\"\">.
+However the whole operation is completed even if there
+were C<readlink(2)> errors, and so you can call this
+function with names where you don't know if they are
+symbolic links already (albeit slightly less efficient).
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+Very long directory listings might cause the protocol
+message size to be exceeded, causing
+this call to fail. The caller must split up such requests
+into smaller groups of names.");
+
]
let all_functions = non_daemon_functions @ daemon_functions