diff options
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | daemon/file.c | 4 | ||||
-rw-r--r-- | generator/generator_actions.ml | 88 | ||||
-rw-r--r-- | src/file.c | 79 |
4 files changed, 150 insertions, 24 deletions
@@ -568,6 +568,3 @@ the p.o.v of the API and ABI. - guestfs_lstatlist - guestfs_lxattrlist - guestfs_readlinklist - - guestfs_write - - guestfs_write_append - - guestfs_write_file diff --git a/daemon/file.c b/daemon/file.c index 0b197412..27567558 100644 --- a/daemon/file.c +++ b/daemon/file.c @@ -211,7 +211,7 @@ do_write_file (const char *path, const char *content, int size) } int -do_write (const char *path, const char *content, size_t size) +do_internal_write (const char *path, const char *content, size_t size) { int fd; @@ -239,7 +239,7 @@ do_write (const char *path, const char *content, size_t size) } int -do_write_append (const char *path, const char *content, size_t size) +do_internal_write_append (const char *path, const char *content, size_t size) { int fd; diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index 4f920902..d614e75c 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -2104,6 +2104,54 @@ Note that this function cannot correctly handle binary files as end of string). For those you need to use the C<guestfs_read_file> function and split the buffer into lines yourself." }; + { defaults with + name = "write"; + style = RErr, [Pathname "path"; BufferIn "content"], []; + tests = [ + InitScratchFS, Always, TestOutput ( + [["write"; "/write"; "new file contents"]; + ["cat"; "/write"]], "new file contents"); + InitScratchFS, Always, TestOutput ( + [["write"; "/write2"; "\nnew file contents\n"]; + ["cat"; "/write2"]], "\nnew file contents\n"); + InitScratchFS, Always, TestOutput ( + [["write"; "/write3"; "\n\n"]; + ["cat"; "/write3"]], "\n\n"); + InitScratchFS, Always, TestOutput ( + [["write"; "/write4"; ""]; + ["cat"; "/write4"]], ""); + InitScratchFS, Always, TestOutput ( + [["write"; "/write5"; "\n\n\n"]; + ["cat"; "/write5"]], "\n\n\n"); + InitScratchFS, Always, TestOutput ( + [["write"; "/write6"; "\n"]; + ["cat"; "/write6"]], "\n") + ]; + shortdesc = "create a new file"; + longdesc = "\ +This call creates a file called C<path>. The content of the +file is the string C<content> (which can contain any 8 bit data). + +See also C<guestfs_write_append>." }; + + { defaults with + name = "write_append"; + style = RErr, [Pathname "path"; BufferIn "content"], []; + tests = [ + InitScratchFS, Always, TestOutput ( + [["write"; "/write_append"; "line1\n"]; + ["write_append"; "/write_append"; "line2\n"]; + ["write_append"; "/write_append"; "line3a"]; + ["write_append"; "/write_append"; "line3b\n"]; + ["cat"; "/write_append"]], "line1\nline2\nline3aline3b\n") + ]; + shortdesc = "append content to end of file"; + longdesc = "\ +This call appends C<content> to the end of file C<path>. If +C<path> does not exist, then a new file is created. + +See also C<guestfs_write>." }; + ] (* daemon_functions are any functions which cause some action @@ -7082,29 +7130,30 @@ of bytes in C<pattern>. The pattern is truncated if necessary to ensure the length of the file is exactly C<len> bytes." }; { defaults with - name = "write"; + name = "internal_write"; style = RErr, [Pathname "path"; BufferIn "content"], []; proc_nr = Some 246; + in_fish = false; in_docs = false; protocol_limit_warning = true; tests = [ InitScratchFS, Always, TestOutput ( - [["write"; "/write"; "new file contents"]; - ["cat"; "/write"]], "new file contents"); + [["internal_write"; "/internal_write"; "new file contents"]; + ["cat"; "/internal_write"]], "new file contents"); InitScratchFS, Always, TestOutput ( - [["write"; "/write2"; "\nnew file contents\n"]; - ["cat"; "/write2"]], "\nnew file contents\n"); + [["internal_write"; "/internal_write2"; "\nnew file contents\n"]; + ["cat"; "/internal_write2"]], "\nnew file contents\n"); InitScratchFS, Always, TestOutput ( - [["write"; "/write3"; "\n\n"]; - ["cat"; "/write3"]], "\n\n"); + [["internal_write"; "/internal_write3"; "\n\n"]; + ["cat"; "/internal_write3"]], "\n\n"); InitScratchFS, Always, TestOutput ( - [["write"; "/write4"; ""]; - ["cat"; "/write4"]], ""); + [["internal_write"; "/internal_write4"; ""]; + ["cat"; "/internal_write4"]], ""); InitScratchFS, Always, TestOutput ( - [["write"; "/write5"; "\n\n\n"]; - ["cat"; "/write5"]], "\n\n\n"); + [["internal_write"; "/internal_write5"; "\n\n\n"]; + ["cat"; "/internal_write5"]], "\n\n\n"); InitScratchFS, Always, TestOutput ( - [["write"; "/write6"; "\n"]; - ["cat"; "/write6"]], "\n") + [["internal_write"; "/internal_write6"; "\n"]; + ["cat"; "/internal_write6"]], "\n") ]; shortdesc = "create a new file"; longdesc = "\ @@ -7943,17 +7992,18 @@ is resized to the maximum size. See also L<btrfs(8)>." }; { defaults with - name = "write_append"; + name = "internal_write_append"; style = RErr, [Pathname "path"; BufferIn "content"], []; proc_nr = Some 290; + in_fish = false; in_docs = false; protocol_limit_warning = true; tests = [ InitScratchFS, Always, TestOutput ( - [["write"; "/write_append"; "line1\n"]; - ["write_append"; "/write_append"; "line2\n"]; - ["write_append"; "/write_append"; "line3a"]; - ["write_append"; "/write_append"; "line3b\n"]; - ["cat"; "/write_append"]], "line1\nline2\nline3aline3b\n") + [["write"; "/internal_write_append"; "line1\n"]; + ["internal_write_append"; "/internal_write_append"; "line2\n"]; + ["internal_write_append"; "/internal_write_append"; "line3a"]; + ["internal_write_append"; "/internal_write_append"; "line3b\n"]; + ["cat"; "/internal_write_append"]], "line1\nline2\nline3aline3b\n") ]; shortdesc = "append content to end of file"; longdesc = "\ @@ -28,6 +28,7 @@ #include <sys/stat.h> #include "full-read.h" +#include "full-write.h" #include "guestfs.h" #include "guestfs-internal.h" @@ -290,3 +291,81 @@ guestfs__find (guestfs_h *g, const char *directory) } return NULL; } + +static int +write_or_append (guestfs_h *g, const char *path, + const char *content, size_t size, + int append) +{ + char *tmpfile = NULL; + int fd = -1; + int64_t filesize; + + /* If the content is small enough, use guestfs_internal_write{,_append} + * since that call is more efficient. + */ + if (size <= 2*1024*1024) + return + (!append ? guestfs_internal_write : guestfs_internal_write_append) + (g, path, content, size); + + /* Write the content out to a temporary file. */ + tmpfile = safe_asprintf (g, "%s/write%d", g->tmpdir, ++g->unique); + + fd = open (tmpfile, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); + if (fd == -1) { + perrorf (g, "open: %s", tmpfile); + goto err; + } + + if (full_write (fd, content, size) != size) { + perrorf (g, "write: %s", tmpfile); + goto err; + } + + if (close (fd) == -1) { + perrorf (g, "close: %s", tmpfile); + goto err; + } + fd = -1; + + if (!append) { + if (guestfs_upload (g, tmpfile, path) == -1) + goto err; + } + else { + /* XXX Should have an 'upload-append' call to make this atomic. */ + filesize = guestfs_filesize (g, path); + if (filesize == -1) + goto err; + if (guestfs_upload_offset (g, tmpfile, path, filesize) == -1) + goto err; + } + + unlink (tmpfile); + free (tmpfile); + return 0; + + err: + if (fd >= 0) + close (fd); + if (tmpfile) { + unlink (tmpfile); + free (tmpfile); + } + return -1; +} + +int +guestfs__write (guestfs_h *g, const char *path, + const char *content, size_t size) +{ + return write_or_append (g, path, content, size, 0); +} + +int +guestfs__write_append (guestfs_h *g, const char *path, + const char *content, size_t size) +{ + return write_or_append (g, path, content, size, 1); +} |