guestfs_write, guestfs_write_append: Reimplement to avoid protocol limits.

Note that we keep the old daemon calls, but rename them as
"internal_write" and "internal_write_append".  This lets us implement
the new library-side calls more efficiently in the common case when
the uploaded content is smaller than the message buffer.  In most
cases the new calls won't end up using a temporary file.
This commit is contained in:
Richard W.M. Jones
2012-08-17 11:47:49 +01:00
parent 735ce57cda
commit 118932fbea
4 changed files with 150 additions and 24 deletions

3
TODO
View File

@@ -568,6 +568,3 @@ the p.o.v of the API and ABI.
- guestfs_lstatlist - guestfs_lstatlist
- guestfs_lxattrlist - guestfs_lxattrlist
- guestfs_readlinklist - guestfs_readlinklist
- guestfs_write
- guestfs_write_append
- guestfs_write_file

View File

@@ -211,7 +211,7 @@ do_write_file (const char *path, const char *content, int size)
} }
int 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; int fd;
@@ -239,7 +239,7 @@ do_write (const char *path, const char *content, size_t size)
} }
int 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; int fd;

View File

@@ -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> as end of string). For those you need to use the C<guestfs_read_file>
function and split the buffer into lines yourself." }; 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 (* 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." }; to ensure the length of the file is exactly C<len> bytes." };
{ defaults with { defaults with
name = "write"; name = "internal_write";
style = RErr, [Pathname "path"; BufferIn "content"], []; style = RErr, [Pathname "path"; BufferIn "content"], [];
proc_nr = Some 246; proc_nr = Some 246;
in_fish = false; in_docs = false;
protocol_limit_warning = true; protocol_limit_warning = true;
tests = [ tests = [
InitScratchFS, Always, TestOutput ( InitScratchFS, Always, TestOutput (
[["write"; "/write"; "new file contents"]; [["internal_write"; "/internal_write"; "new file contents"];
["cat"; "/write"]], "new file contents"); ["cat"; "/internal_write"]], "new file contents");
InitScratchFS, Always, TestOutput ( InitScratchFS, Always, TestOutput (
[["write"; "/write2"; "\nnew file contents\n"]; [["internal_write"; "/internal_write2"; "\nnew file contents\n"];
["cat"; "/write2"]], "\nnew file contents\n"); ["cat"; "/internal_write2"]], "\nnew file contents\n");
InitScratchFS, Always, TestOutput ( InitScratchFS, Always, TestOutput (
[["write"; "/write3"; "\n\n"]; [["internal_write"; "/internal_write3"; "\n\n"];
["cat"; "/write3"]], "\n\n"); ["cat"; "/internal_write3"]], "\n\n");
InitScratchFS, Always, TestOutput ( InitScratchFS, Always, TestOutput (
[["write"; "/write4"; ""]; [["internal_write"; "/internal_write4"; ""];
["cat"; "/write4"]], ""); ["cat"; "/internal_write4"]], "");
InitScratchFS, Always, TestOutput ( InitScratchFS, Always, TestOutput (
[["write"; "/write5"; "\n\n\n"]; [["internal_write"; "/internal_write5"; "\n\n\n"];
["cat"; "/write5"]], "\n\n\n"); ["cat"; "/internal_write5"]], "\n\n\n");
InitScratchFS, Always, TestOutput ( InitScratchFS, Always, TestOutput (
[["write"; "/write6"; "\n"]; [["internal_write"; "/internal_write6"; "\n"];
["cat"; "/write6"]], "\n") ["cat"; "/internal_write6"]], "\n")
]; ];
shortdesc = "create a new file"; shortdesc = "create a new file";
longdesc = "\ longdesc = "\
@@ -7943,17 +7992,18 @@ is resized to the maximum size.
See also L<btrfs(8)>." }; See also L<btrfs(8)>." };
{ defaults with { defaults with
name = "write_append"; name = "internal_write_append";
style = RErr, [Pathname "path"; BufferIn "content"], []; style = RErr, [Pathname "path"; BufferIn "content"], [];
proc_nr = Some 290; proc_nr = Some 290;
in_fish = false; in_docs = false;
protocol_limit_warning = true; protocol_limit_warning = true;
tests = [ tests = [
InitScratchFS, Always, TestOutput ( InitScratchFS, Always, TestOutput (
[["write"; "/write_append"; "line1\n"]; [["write"; "/internal_write_append"; "line1\n"];
["write_append"; "/write_append"; "line2\n"]; ["internal_write_append"; "/internal_write_append"; "line2\n"];
["write_append"; "/write_append"; "line3a"]; ["internal_write_append"; "/internal_write_append"; "line3a"];
["write_append"; "/write_append"; "line3b\n"]; ["internal_write_append"; "/internal_write_append"; "line3b\n"];
["cat"; "/write_append"]], "line1\nline2\nline3aline3b\n") ["cat"; "/internal_write_append"]], "line1\nline2\nline3aline3b\n")
]; ];
shortdesc = "append content to end of file"; shortdesc = "append content to end of file";
longdesc = "\ longdesc = "\

View File

@@ -28,6 +28,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include "full-read.h" #include "full-read.h"
#include "full-write.h"
#include "guestfs.h" #include "guestfs.h"
#include "guestfs-internal.h" #include "guestfs-internal.h"
@@ -290,3 +291,81 @@ guestfs__find (guestfs_h *g, const char *directory)
} }
return NULL; 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);
}