guestfs_read_lines: Reimplement to avoid protocol limits.

This also makes a larger test suite for this command.
This commit is contained in:
Richard W.M. Jones
2012-08-17 11:07:33 +01:00
parent 96d3ac28d6
commit 735ce57cda
4 changed files with 111 additions and 71 deletions

1
TODO
View File

@@ -567,7 +567,6 @@ the p.o.v of the API and ABI.
- guestfs_lstatlist
- guestfs_lxattrlist
- guestfs_read_lines
- guestfs_readlinklist
- guestfs_write
- guestfs_write_append

View File

@@ -85,54 +85,6 @@ do_touch (const char *path)
return 0;
}
char **
do_read_lines (const char *path)
{
DECLARE_STRINGSBUF (r);
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t n;
CHROOT_IN;
fp = fopen (path, "r");
CHROOT_OUT;
if (!fp) {
reply_with_perror ("fopen: %s", path);
return NULL;
}
while ((n = getline (&line, &len, fp)) != -1) {
/* Remove either LF or CRLF. */
if (n >= 2 && line[n-2] == '\r' && line[n-1] == '\n')
line[n-2] = '\0';
else if (n >= 1 && line[n-1] == '\n')
line[n-1] = '\0';
if (add_string (&r, line) == -1) {
free (line);
fclose (fp);
return NULL;
}
}
free (line);
if (end_stringsbuf (&r) == -1) {
fclose (fp);
return NULL;
}
if (fclose (fp) == EOF) {
reply_with_perror ("fclose: %s", path);
free_stringslen (r.argv, r.size);
return NULL;
}
return r.argv;
}
int
do_rm (const char *path)
{

View File

@@ -2059,6 +2059,51 @@ buffer.
Unlike C<guestfs_cat>, this function can correctly
handle files that contain embedded ASCII NUL characters." };
{ defaults with
name = "read_lines";
style = RStringList "lines", [Pathname "path"], [];
tests = [
InitISOFS, Always, TestOutputList (
[["read_lines"; "/known-4"]], ["abc"; "def"; "ghi"]);
InitISOFS, Always, TestOutputList (
[["read_lines"; "/empty"]], []);
InitScratchFS, Always, TestOutputList (
[["write"; "/read_lines1"; "\n"];
["read_lines"; "/read_lines1"]], [""]);
InitScratchFS, Always, TestOutputList (
[["write"; "/read_lines2"; "\r\n"];
["read_lines"; "/read_lines2"]], [""]);
InitScratchFS, Always, TestOutputList (
[["write"; "/read_lines3"; "\n\r\n"];
["read_lines"; "/read_lines3"]], [""; ""]);
InitScratchFS, Always, TestOutputList (
[["write"; "/read_lines4"; "a"];
["read_lines"; "/read_lines4"]], ["a"]);
InitScratchFS, Always, TestOutputList (
[["write"; "/read_lines5"; "a\nb"];
["read_lines"; "/read_lines5"]], ["a"; "b"]);
InitScratchFS, Always, TestOutputList (
[["write"; "/read_lines6"; "a\nb\n"];
["read_lines"; "/read_lines6"]], ["a"; "b"]);
InitScratchFS, Always, TestOutputList (
[["write"; "/read_lines7"; "a\nb\r\n"];
["read_lines"; "/read_lines7"]], ["a"; "b"]);
InitScratchFS, Always, TestOutputList (
[["write"; "/read_lines8"; "a\nb\r\n\n"];
["read_lines"; "/read_lines8"]], ["a"; "b"; ""]);
];
shortdesc = "read file as lines";
longdesc = "\
Return the contents of the file named C<path>.
The file contents are returned as a list of lines. Trailing
C<LF> and C<CRLF> character sequences are I<not> returned.
Note that this function cannot correctly handle binary files
(specifically, files containing C<\\0> character which is treated
as end of string). For those you need to use the C<guestfs_read_file>
function and split the buffer into lines yourself." };
]
(* daemon_functions are any functions which cause some action
@@ -2332,28 +2377,6 @@ of the L<vgs(8)> command. The \"full\" version includes all fields." };
List all the logical volumes detected. This is the equivalent
of the L<lvs(8)> command. The \"full\" version includes all fields." };
{ defaults with
name = "read_lines";
style = RStringList "lines", [Pathname "path"], [];
proc_nr = Some 15;
tests = [
InitISOFS, Always, TestOutputList (
[["read_lines"; "/known-4"]], ["abc"; "def"; "ghi"]);
InitISOFS, Always, TestOutputList (
[["read_lines"; "/empty"]], [])
];
shortdesc = "read file as lines";
longdesc = "\
Return the contents of the file named C<path>.
The file contents are returned as a list of lines. Trailing
C<LF> and C<CRLF> character sequences are I<not> returned.
Note that this function cannot correctly handle binary files
(specifically, files containing C<\\0> character which is treated
as end of line). For those you need to use the C<guestfs_read_file>
function which has a more complex interface." };
{ defaults with
name = "aug_init";
style = RErr, [Pathname "root"; Int "flags"], [];

View File

@@ -122,6 +122,72 @@ guestfs__read_file (guestfs_h *g, const char *path, size_t *size_r)
return NULL;
}
char **
guestfs__read_lines (guestfs_h *g, const char *file)
{
size_t i, count, size, len;
char *buf = NULL;
char **ret = NULL;
/* Read the whole file into memory. */
buf = guestfs__read_file (g, file, &size);
if (buf == NULL)
return NULL;
/* 'buf' contains the list of strings, separated by LF or CRLF
* characters. Convert this to a list of lines. Note we have to
* handle the cases where the buffer is zero length and where the
* final string is not terminated.
*/
count = 0;
for (i = 0; i < size; ++i)
if (buf[i] == '\n')
count++;
if (size > 0 && buf[size-1] != '\n')
count++;
ret = malloc ((count + 1) * sizeof (char *));
if (!ret) {
perrorf (g, "malloc");
goto err;
}
count = 0;
if (size > 0) {
ret[count++] = buf;
for (i = 0; i < size; ++i) {
if (buf[i] == '\n') {
buf[i] = '\0';
if (i+1 < size)
ret[count++] = &buf[i+1];
}
}
}
ret[count] = NULL;
/* Duplicate the strings, and remove the trailing \r characters if any. */
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;
}
len = strlen (ret[i]);
if (len > 0 && ret[i][len-1] == '\r')
ret[i][len-1] = '\0';
}
free (buf);
return ret;
err:
free (buf);
free (ret);
return NULL;
}
char **
guestfs__find (guestfs_h *g, const char *directory)
{