summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--daemon/file.c4
-rw-r--r--generator/generator_actions.ml88
-rw-r--r--src/file.c79
4 files changed, 150 insertions, 24 deletions
diff --git a/TODO b/TODO
index 73cccf69..1f84fcc8 100644
--- a/TODO
+++ b/TODO
@@ -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 = "\
diff --git a/src/file.c b/src/file.c
index 13f08cd6..cd2ea516 100644
--- a/src/file.c
+++ b/src/file.c
@@ -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);
+}