mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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:
3
TODO
3
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 = "\
|
||||
|
||||
79
src/file.c
79
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user