guestfs_ls: Reimplement to avoid protocol limits.

This commit is contained in:
Richard W.M. Jones
2012-08-17 14:11:21 +01:00
parent 1b4d8e2cca
commit a69f44f56f
3 changed files with 119 additions and 64 deletions

View File

@@ -93,49 +93,6 @@ do_ls0 (const char *path)
return 0;
}
char **
do_ls (const char *path)
{
DECLARE_STRINGSBUF (ret);
DIR *dir;
struct dirent *d;
CHROOT_IN;
dir = opendir (path);
CHROOT_OUT;
if (!dir) {
reply_with_perror ("opendir: %s", path);
return NULL;
}
while ((d = readdir (dir)) != NULL) {
if (STREQ (d->d_name, ".") || STREQ (d->d_name, ".."))
continue;
if (add_string (&ret, d->d_name) == -1) {
closedir (dir);
return NULL;
}
}
if (ret.size > 0)
sort_strings (ret.argv, ret.size);
if (end_stringsbuf (&ret) == -1) {
closedir (dir);
return NULL;
}
if (closedir (dir) == -1) {
reply_with_perror ("closedir: %s", path);
free_stringslen (ret.argv, ret.size);
return NULL;
}
return ret.argv;
}
/* Because we can't chroot and run the ls command (since 'ls' won't
* necessarily exist in the chroot), this command can be used to escape
* from the sysroot (eg. 'll /..'). This command is not meant for

View File

@@ -2219,6 +2219,23 @@ 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." };
{ defaults with
name = "ls";
style = RStringList "listing", [Pathname "directory"], [];
tests = [
InitScratchFS, Always, TestOutputList (
[["mkdir"; "/ls"];
["touch"; "/ls/new"];
["touch"; "/ls/newer"];
["touch"; "/ls/newest"];
["ls"; "/ls"]], ["new"; "newer"; "newest"])
];
shortdesc = "list the files in a directory";
longdesc = "\
List the files in C<directory> (relative to the root directory,
there is no cwd). The '.' and '..' entries are not returned, but
hidden files are shown." };
]
(* daemon_functions are any functions which cause some action
@@ -2309,27 +2326,6 @@ there is no cwd) in the format of 'ls -la'.
This command is mostly useful for interactive sessions. It
is I<not> intended that you try to parse the output string." };
{ defaults with
name = "ls";
style = RStringList "listing", [Pathname "directory"], [];
proc_nr = Some 6;
tests = [
InitScratchFS, Always, TestOutputList (
[["mkdir"; "/ls"];
["touch"; "/ls/new"];
["touch"; "/ls/newer"];
["touch"; "/ls/newest"];
["ls"; "/ls"]], ["new"; "newer"; "newest"])
];
shortdesc = "list the files in a directory";
longdesc = "\
List the files in C<directory> (relative to the root directory,
there is no cwd). The '.' and '..' entries are not returned, but
hidden files are shown.
This command is mostly useful for interactive sessions. Programs
should probably use C<guestfs_readdir> instead." };
{ defaults with
name = "list_devices";
style = RStringList "devices", [], [];

View File

@@ -535,3 +535,105 @@ guestfs__readlinklist (guestfs_h *g, const char *dir, char *const *names)
return ret;
}
char **
guestfs__ls (guestfs_h *g, const char *directory)
{
int fd = -1;
struct stat statbuf;
char *tmpfile = NULL, *buf = NULL;
char **ret = NULL;
size_t i, count, size;
tmpfile = safe_asprintf (g, "%s/ls%d", g->tmpdir, ++g->unique);
if (guestfs_ls0 (g, directory, tmpfile) == -1)
goto err;
fd = open (tmpfile, O_RDONLY|O_CLOEXEC);
if (fd == -1) {
perrorf (g, "open: %s", tmpfile);
goto err;
}
unlink (tmpfile);
free (tmpfile);
tmpfile = NULL;
/* Read the whole file into memory. */
if (fstat (fd, &statbuf) == -1) {
perrorf (g, "stat: %s", tmpfile);
goto err;
}
/* Don't use safe_malloc, because we want to return an errno to the caller. */
size = statbuf.st_size;
buf = malloc (size);
if (!buf) {
perrorf (g, "malloc: %zu bytes", size);
goto err;
}
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;
/* 'buf' contains the list of strings, separated (and terminated) by
* '\0' characters. Convert this to a list of lines. Note we
* handle the case where buf is completely empty (size == 0).
*/
count = 0;
for (i = 0; i < size; ++i)
if (buf[i] == '\0')
count++;
ret = malloc ((count + 1) * sizeof (char *));
if (!ret) {
perrorf (g, "malloc");
goto err;
}
count = 0;
ret[count++] = buf;
for (i = 0; i < size; ++i) {
if (buf[i] == '\0')
ret[count++] = &buf[i+1];
}
ret[--count] = NULL;
/* Finally we have to duplicate and sort the strings, since that's
* what the caller is expecting.
*/
for (i = 0; ret[i] != NULL; ++i) {
ret[i] = strdup (ret[i]);
if (ret[i] == NULL) {
perrorf (g, "strdup");
while (i > 0)
free (ret[--i]);
goto err;
}
}
free (buf);
sort_strings (ret, count);
return ret; /* caller frees */
err:
free (buf);
free (ret);
if (fd >= 0)
close (fd);
if (tmpfile) {
unlink (tmpfile);
free (tmpfile);
}
return NULL;
}