guestfs_lstatlist, guestfs_lxattrlist: Reimplement to avoid protocol limits.

Note that the code to do this was already in virt-ls, so this is
change is mostly just moving the code into the core library.
This commit is contained in:
Richard W.M. Jones
2012-08-17 12:21:37 +01:00
parent 118932fbea
commit dc66dd32c2
6 changed files with 184 additions and 174 deletions

2
TODO
View File

@@ -565,6 +565,4 @@ with the non-daemon versions implemented using guestfs_upload and
guestfs_download (and others). This change should be transparent from
the p.o.v of the API and ABI.
- guestfs_lstatlist
- guestfs_lxattrlist
- guestfs_readlinklist

View File

@@ -86,9 +86,7 @@ static int is_fifo (int64_t mode);
static int is_lnk (int64_t mode);
static int is_sock (int64_t mode);
static size_t count_strings (char **);
static void free_strings (char **);
static char **take_strings (char **, size_t n, char ***);
static inline char *
bad_cast (char const *s)
@@ -467,8 +465,6 @@ do_ls_R (const char *dir)
https://rwmj.wordpress.com/2010/12/15/tip-audit-virtual-machine-for-setuid-files/
*/
static char *full_path (const char *dir, const char *name);
static struct guestfs_stat_list *lstatlist (const char *dir, char **names);
static struct guestfs_xattr_list *lxattrlist (const char *dir, char **names);
static int show_file (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs);
typedef int (*visitor_function) (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs);
@@ -514,11 +510,11 @@ visit (int depth, const char *dir, visitor_function f)
if (names == NULL)
goto out;
stats = lstatlist (dir, names);
stats = guestfs_lstatlist (g, dir, names);
if (stats == NULL)
goto out;
xattrs = lxattrlist (dir, names);
xattrs = guestfs_lxattrlist (g, dir, names);
if (xattrs == NULL)
goto out;
@@ -538,8 +534,12 @@ visit (int depth, const char *dir, visitor_function f)
program_name, dir, names[i]);
goto out;
}
/* lxattrlist function made sure attrval was \0-terminated, so we can do */
if (sscanf (xattrs->val[xattrp].attrval, "%zu", &nr_xattrs) != 1) {
/* attrval is not \0-terminated. */
char attrval[xattrs->val[xattrp].attrval_len+1];
memcpy (attrval, xattrs->val[xattrp].attrval,
xattrs->val[xattrp].attrval_len);
attrval[xattrs->val[xattrp].attrval_len] = '\0';
if (sscanf (attrval, "%zu", &nr_xattrs) != 1) {
fprintf (stderr, _("%s: error: cannot parse xattr count for %s %s\n"),
program_name, dir, names[i]);
goto out;
@@ -596,129 +596,6 @@ full_path (const char *dir, const char *name)
return path;
}
/* This calls guestfs_lstatlist, but it splits the names list up so that we
* don't overrun the libguestfs protocol limit.
*/
#define LSTATLIST_MAX 1000
static struct guestfs_stat_list *
lstatlist (const char *dir, char **names)
{
size_t len = count_strings (names);
char **first;
size_t old_len;
struct guestfs_stat_list *ret, *stats;
ret = malloc (sizeof *ret);
if (ret == NULL) {
perror ("malloc");
exit (EXIT_FAILURE);
}
ret->len = 0;
ret->val = NULL;
while (len > 0) {
first = take_strings (names, LSTATLIST_MAX, &names);
len = len <= LSTATLIST_MAX ? 0 : len - LSTATLIST_MAX;
stats = guestfs_lstatlist (g, dir, first);
/* Note we don't need to free up the strings because take_strings
* does not do a deep copy.
*/
free (first);
if (stats == NULL) {
free (ret);
return NULL;
}
/* Append stats to ret. */
old_len = ret->len;
ret->len += stats->len;
ret->val = realloc (ret->val, ret->len * sizeof (struct guestfs_stat));
if (ret->val == NULL) {
perror ("realloc");
exit (EXIT_FAILURE);
}
memcpy (&ret->val[old_len], stats->val,
stats->len * sizeof (struct guestfs_stat));
guestfs_free_stat_list (stats);
}
return ret;
}
/* Same as above, for lxattrlist. Note the rather peculiar format
* used to return the list of extended attributes (see
* guestfs_lxattrlist documentation).
*/
#define LXATTRLIST_MAX 1000
static struct guestfs_xattr_list *
lxattrlist (const char *dir, char **names)
{
size_t len = count_strings (names);
char **first;
size_t i, old_len;
struct guestfs_xattr_list *ret, *xattrs;
ret = malloc (sizeof *ret);
if (ret == NULL) {
perror ("malloc");
exit (EXIT_FAILURE);
}
ret->len = 0;
ret->val = NULL;
while (len > 0) {
first = take_strings (names, LXATTRLIST_MAX, &names);
len = len <= LXATTRLIST_MAX ? 0 : len - LXATTRLIST_MAX;
xattrs = guestfs_lxattrlist (g, dir, first);
/* Note we don't need to free up the strings because take_strings
* does not do a deep copy.
*/
free (first);
if (xattrs == NULL) {
free (ret);
return NULL;
}
/* Append xattrs to ret. */
old_len = ret->len;
ret->len += xattrs->len;
ret->val = realloc (ret->val, ret->len * sizeof (struct guestfs_xattr));
if (ret->val == NULL) {
perror ("realloc");
exit (EXIT_FAILURE);
}
for (i = 0; i < xattrs->len; ++i, ++old_len) {
/* We have to make a deep copy of the attribute name and value.
* The attrval contains 8 bit data. However make sure also that
* it is \0-terminated, because that makes the calling code
* simpler.
*/
ret->val[old_len].attrname = strdup (xattrs->val[i].attrname);
ret->val[old_len].attrval = malloc (xattrs->val[i].attrval_len + 1);
if (ret->val[old_len].attrname == NULL ||
ret->val[old_len].attrval == NULL) {
perror ("malloc");
exit (EXIT_FAILURE);
}
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);
ret->val[i].attrval[ret->val[i].attrval_len] = '\0';
}
guestfs_free_xattr_list (xattrs);
}
return ret;
}
static int
do_ls_lR (const char *dir)
{
@@ -1095,16 +972,6 @@ is_sock (int64_t mode)
}
/* String functions. */
static size_t
count_strings (char **names)
{
size_t ret = 0;
while (names[ret] != NULL)
ret++;
return ret;
}
static void
free_strings (char **names)
{
@@ -1114,29 +981,3 @@ free_strings (char **names)
free (names[i]);
free (names);
}
/* Take the first 'n' names, returning a newly allocated list. The
* strings themselves are not duplicated. If 'lastp' is not NULL,
* then it is updated with the pointer to the list of remaining names.
*/
static char **
take_strings (char **names, size_t n, char ***lastp)
{
size_t i;
char **ret = malloc ((n+1) * sizeof (char *));
if (ret == NULL) {
perror ("malloc");
exit (EXIT_FAILURE);
}
for (i = 0; names[i] != NULL && i < n; ++i)
ret[i] = names[i];
ret[i] = NULL;
if (lastp)
*lastp = &names[i];
return ret;
}

View File

@@ -125,7 +125,7 @@ do_lstat (const char *path)
}
guestfs_int_stat_list *
do_lstatlist (const char *path, char *const *names)
do_internal_lstatlist (const char *path, char *const *names)
{
int path_fd;
guestfs_int_stat_list *ret;

View File

@@ -263,7 +263,7 @@ _removexattr (const char *xattr, const char *path,
}
guestfs_int_xattr_list *
do_lxattrlist (const char *path, char *const *names)
do_internal_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
@@ -588,7 +588,7 @@ do_lremovexattr (const char *xattr, const char *path)
}
guestfs_int_xattr_list *
do_lxattrlist (const char *path, char *const *names)
do_internal_lxattrlist (const char *path, char *const *names)
{
abort ();
}

View File

@@ -2152,6 +2152,50 @@ C<path> does not exist, then a new file is created.
See also C<guestfs_write>." };
{ defaults with
name = "lstatlist";
style = RStructList ("statbufs", "stat"), [Pathname "path"; StringList "names"], [];
shortdesc = "lstat on multiple files";
longdesc = "\
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." };
{ defaults with
name = "lxattrlist";
style = RStructList ("xattrs", "xattr"), [Pathname "path"; StringList "names"], [];
optional = Some "linuxxattrs";
shortdesc = "lgetxattr on multiple files";
longdesc = "\
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." };
]
(* daemon_functions are any functions which cause some action
@@ -6281,9 +6325,10 @@ names, you will need to locate and parse the password file
yourself (Augeas support makes this relatively easy)." };
{ defaults with
name = "lstatlist";
name = "internal_lstatlist";
style = RStructList ("statbufs", "stat"), [Pathname "path"; StringList "names"], [];
proc_nr = Some 204;
in_docs = false; in_fish = false;
shortdesc = "lstat on multiple files";
longdesc = "\
This call allows you to perform the C<guestfs_lstat> operation
@@ -6304,9 +6349,10 @@ this call to fail. The caller must split up such requests
into smaller groups of names." };
{ defaults with
name = "lxattrlist";
name = "internal_lxattrlist";
style = RStructList ("xattrs", "xattr"), [Pathname "path"; StringList "names"], [];
proc_nr = Some 205;
in_docs = false; in_fish = false;
optional = Some "linuxxattrs";
shortdesc = "lgetxattr on multiple files";
longdesc = "\

View File

@@ -49,6 +49,38 @@ sort_strings (char **argv, size_t len)
qsort (argv, len, sizeof (char *), compare);
}
/* Take the first 'n' names, returning a newly allocated list. The
* strings themselves are not duplicated. If 'lastp' is not NULL,
* then it is updated with the pointer to the list of remaining names.
*/
static char **
take_strings (guestfs_h *g, char *const *names, size_t n, char *const **lastp)
{
size_t i;
char **ret = safe_malloc (g, (n+1) * sizeof (char *));
for (i = 0; names[i] != NULL && i < n; ++i)
ret[i] = names[i];
ret[i] = NULL;
if (lastp)
*lastp = &names[i];
return ret;
}
static size_t
count_strings (char * const*names)
{
size_t ret = 0;
while (names[ret] != NULL)
ret++;
return ret;
}
char *
guestfs__cat (guestfs_h *g, const char *path)
{
@@ -369,3 +401,96 @@ guestfs__write_append (guestfs_h *g, const char *path,
{
return write_or_append (g, path, content, size, 1);
}
#define LSTATLIST_MAX 1000
struct guestfs_stat_list *
guestfs__lstatlist (guestfs_h *g, const char *dir, char * const*names)
{
size_t len = count_strings (names);
char **first;
size_t old_len;
struct guestfs_stat_list *ret, *stats;
ret = safe_malloc (g, sizeof *ret);
ret->len = 0;
ret->val = NULL;
while (len > 0) {
first = take_strings (g, names, LSTATLIST_MAX, &names);
len = len <= LSTATLIST_MAX ? 0 : len - LSTATLIST_MAX;
stats = guestfs_internal_lstatlist (g, dir, first);
/* Note we don't need to free up the strings because take_strings
* does not do a deep copy.
*/
free (first);
if (stats == NULL) {
guestfs_free_stat_list (stats);
return NULL;
}
/* Append stats to ret. */
old_len = ret->len;
ret->len += stats->len;
ret->val = safe_realloc (g, ret->val,
ret->len * sizeof (struct guestfs_stat));
memcpy (&ret->val[old_len], stats->val,
stats->len * sizeof (struct guestfs_stat));
guestfs_free_stat_list (stats);
}
return ret;
}
#define LXATTRLIST_MAX 1000
struct guestfs_xattr_list *
guestfs__lxattrlist (guestfs_h *g, const char *dir, char *const *names)
{
size_t len = count_strings (names);
char **first;
size_t i, old_len;
struct guestfs_xattr_list *ret, *xattrs;
ret = safe_malloc (g, sizeof *ret);
ret->len = 0;
ret->val = NULL;
while (len > 0) {
first = take_strings (g, names, LXATTRLIST_MAX, &names);
len = len <= LXATTRLIST_MAX ? 0 : len - LXATTRLIST_MAX;
xattrs = guestfs_internal_lxattrlist (g, dir, first);
/* Note we don't need to free up the strings because take_strings
* does not do a deep copy.
*/
free (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);
}
guestfs_free_xattr_list (xattrs);
}
return ret;
}