mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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:
2
TODO
2
TODO
@@ -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
|
||||
|
||||
175
cat/virt-ls.c
175
cat/virt-ls.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
|
||||
@@ -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 = "\
|
||||
|
||||
125
src/file.c
125
src/file.c
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user