guestfs_readdir(): minimize the number of send_file_write() calls

In guestfs_readdir(), the daemon currently sends each XDR-encoded
"guestfs_int_dirent" to the library with a separate send_file_write()
call.

Determine the largest encoded size (from the longest filename that a
"guestfs_int_dirent" could carry, from readdir()'s "struct dirent"), and
batch up the XDR encodings until the next encoding might not fit in
GUESTFS_MAX_CHUNK_SIZE. Call send_file_write() only then.

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1674392
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20220502085601.15012-3-lersek@redhat.com>
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
(cherry picked from commit 4864d21cb8)
This commit is contained in:
Laszlo Ersek
2022-05-02 10:56:01 +02:00
committed by Richard W.M. Jones
parent b97b90779d
commit 62cd6c9d2d

View File

@@ -35,6 +35,9 @@ do_internal_readdir (const char *dir)
DIR *dirstream;
void *xdr_buf;
XDR xdr;
struct dirent fill;
guestfs_int_dirent v;
unsigned max_encoded;
/* Prepare to fail. */
ret = -1;
@@ -55,6 +58,20 @@ do_internal_readdir (const char *dir)
}
xdrmem_create (&xdr, xdr_buf, GUESTFS_MAX_CHUNK_SIZE, XDR_ENCODE);
/* Calculate the max number of bytes a "guestfs_int_dirent" can be encoded to.
*/
memset (fill.d_name, 'a', sizeof fill.d_name - 1);
fill.d_name[sizeof fill.d_name - 1] = '\0';
v.ino = INT64_MAX;
v.ftyp = '?';
v.name = fill.d_name;
if (!xdr_guestfs_int_dirent (&xdr, &v)) {
fprintf (stderr, "xdr_guestfs_int_dirent failed\n");
goto release_xdr;
}
max_encoded = xdr_getpos (&xdr);
xdr_setpos (&xdr, 0);
/* Send an "OK" reply, before starting the file transfer. */
reply (NULL, NULL);
@@ -63,7 +80,6 @@ do_internal_readdir (const char *dir)
*/
for (;;) {
struct dirent *d;
guestfs_int_dirent v;
errno = 0;
d = readdir (dirstream);
@@ -94,22 +110,32 @@ do_internal_readdir (const char *dir)
v.ftyp = 'u';
#endif
/* Flush "xdr_buf" if we may not have enough room for encoding "v". */
if (GUESTFS_MAX_CHUNK_SIZE - xdr_getpos (&xdr) < max_encoded) {
if (send_file_write (xdr_buf, xdr_getpos (&xdr)) != 0)
break;
xdr_setpos (&xdr, 0);
}
if (!xdr_guestfs_int_dirent (&xdr, &v)) {
fprintf (stderr, "xdr_guestfs_int_dirent failed\n");
break;
}
if (send_file_write (xdr_buf, xdr_getpos (&xdr)) != 0)
break;
xdr_setpos (&xdr, 0);
}
/* Flush "xdr_buf" if the loop completed successfully and "xdr_buf" is not
* empty. */
if (ret == 0 && xdr_getpos (&xdr) > 0 &&
send_file_write (xdr_buf, xdr_getpos (&xdr)) != 0)
ret = -1;
/* Finish or cancel the transfer. Note that if (ret == -1) because the library
* canceled, we still need to cancel back!
*/
send_file_end (ret == -1);
release_xdr:
xdr_destroy (&xdr);
free (xdr_buf);