From 735ce57cda24e2e2f41418d9a9ad34944fefdfb9 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 17 Aug 2012 11:07:33 +0100 Subject: guestfs_read_lines: Reimplement to avoid protocol limits. This also makes a larger test suite for this command. --- TODO | 1 - daemon/file.c | 48 ------------------------------ generator/generator_actions.ml | 67 ++++++++++++++++++++++++++++-------------- src/file.c | 66 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 71 deletions(-) diff --git a/TODO b/TODO index 451ca656..73cccf69 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/daemon/file.c b/daemon/file.c index 42c13c9c..0b197412 100644 --- a/daemon/file.c +++ b/daemon/file.c @@ -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) { diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index 4fe6ae1a..4f920902 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -2059,6 +2059,51 @@ buffer. Unlike C, 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. + +The file contents are returned as a list of lines. Trailing +C and C character sequences are I 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 +function and split the buffer into lines yourself." }; + ] (* daemon_functions are any functions which cause some action @@ -2332,28 +2377,6 @@ of the L command. The \"full\" version includes all fields." }; List all the logical volumes detected. This is the equivalent of the L 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. - -The file contents are returned as a list of lines. Trailing -C and C character sequences are I 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 -function which has a more complex interface." }; - { defaults with name = "aug_init"; style = RErr, [Pathname "root"; Int "flags"], []; diff --git a/src/file.c b/src/file.c index 84edc403..13f08cd6 100644 --- a/src/file.c +++ b/src/file.c @@ -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) { -- cgit