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_lstatlist
|
||||||
- guestfs_lxattrlist
|
- guestfs_lxattrlist
|
||||||
- guestfs_readlinklist
|
- 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
|
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;
|
||||||
|
|
||||||
|
|||||||
@@ -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 = "\
|
||||||
|
|||||||
79
src/file.c
79
src/file.c
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user