diff options
-rw-r--r-- | daemon/link.c | 51 | ||||
-rw-r--r-- | daemon/stat.c | 68 | ||||
-rw-r--r-- | daemon/xattr.c | 205 | ||||
-rw-r--r-- | src/MAX_PROC_NR | 2 | ||||
-rwxr-xr-x | src/generator.ml | 73 |
5 files changed, 398 insertions, 1 deletions
diff --git a/daemon/link.c b/daemon/link.c index a77c2e8a..5ea0d39d 100644 --- a/daemon/link.c +++ b/daemon/link.c @@ -21,6 +21,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <fcntl.h> #include <unistd.h> #include <limits.h> @@ -51,6 +52,56 @@ do_readlink (const char *path) 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) { diff --git a/daemon/stat.c b/daemon/stat.c index 4ff27113..2441c9fe 100644 --- a/daemon/stat.c +++ b/daemon/stat.c @@ -24,6 +24,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/statvfs.h> +#include <fcntl.h> #include <unistd.h> #include "../src/guestfs_protocol.h" @@ -108,6 +109,73 @@ do_lstat (const char *path) 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) { diff --git a/daemon/xattr.c b/daemon/xattr.c index 1531070d..d16939fc 100644 --- a/daemon/xattr.c +++ b/daemon/xattr.c @@ -243,6 +243,204 @@ _removexattr (const char *xattr, 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 * @@ -287,4 +485,11 @@ do_lremovexattr (const char *xattr, const char *path) 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 */ diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index bb2ee191..b35cfafd 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -203 +206 diff --git a/src/generator.ml b/src/generator.ml index b8add4c0..001d0217 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -3783,6 +3783,79 @@ Only numeric uid and gid are supported. If you want to use 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 |