From aba6ec639f61311841c75af83920c08750d2091b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Tue, 3 Sep 2019 19:21:50 +0200 Subject: [PATCH] qrexec-lib/unpack: option to wait for free disk space Add an option to wait for disk space before extracting a file from qfile archive. This will allow extracting backup archive using much smaller temporary space (only enough to hold few chunks, not the whole archive). Technically, libqubes-rpc-filecopy library got a set_wait_for_space() function, accepting a margin (in bytes) to be kept free. QubesOS/qubes-issues#4791 --- qrexec-lib/libqubes-rpc-filecopy.h | 5 +++++ qrexec-lib/unpack.c | 31 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/qrexec-lib/libqubes-rpc-filecopy.h b/qrexec-lib/libqubes-rpc-filecopy.h index 2b18b395..bbc8e9b6 100644 --- a/qrexec-lib/libqubes-rpc-filecopy.h +++ b/qrexec-lib/libqubes-rpc-filecopy.h @@ -74,6 +74,11 @@ int copy_file(int outfd, int infd, long long size, unsigned long *crc32); const char *copy_file_status_to_str(int status); void set_size_limit(unsigned long long new_bytes_limit, unsigned long long new_files_limit); void set_verbose(int value); +/* + * Delay extracting a file if there is no enough space for it - wait for space + * for this file, plus a given margin. + */ +void set_wait_for_space(unsigned long margin); /* register open fd to /proc/PID/fd of this process */ void set_procfs_fd(int value); int write_all(int fd, const void *buf, int size); diff --git a/qrexec-lib/unpack.c b/qrexec-lib/unpack.c index 62ccd5b5..a0f02f14 100644 --- a/qrexec-lib/unpack.c +++ b/qrexec-lib/unpack.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,11 @@ unsigned long long files_limit = 0; unsigned long long total_bytes = 0; unsigned long long total_files = 0; int verbose = 0; +/* + * If positive, wait for disk space before extracting a file, + * keeping this much extra space (in bytes). + */ +unsigned long opt_wait_for_space_margin; int use_tmpfile = 0; int procdir_fd = -1; @@ -51,12 +57,33 @@ void set_verbose(int value) verbose = value; } +void set_wait_for_space(unsigned long value) +{ + opt_wait_for_space_margin = value; +} + void set_procfs_fd(int value) { procdir_fd = value; use_tmpfile = 1; } +int wait_for_space(int fd, unsigned long how_much) { + int counter = 0; + struct statvfs fs_space; + do { + if (fstatvfs(fd, &fs_space) == -1) { + perror("fstatvfs"); + return -1; + } + // TODO: timeout? + if (counter > 0) + usleep(1000000); + counter++; + } while (fs_space.f_bsize * fs_space.f_bavail < how_much); + return 0; +} + unsigned long crc32_sum = 0; int read_all_with_crc(int fd, void *buf, int size) { int ret; @@ -129,6 +156,10 @@ void process_one_file_reg(struct file_header *untrusted_hdr, do_exit(EDQUOT, untrusted_name); if (bytes_limit && total_bytes > bytes_limit - untrusted_hdr->filelen) do_exit(EDQUOT, untrusted_name); + if (opt_wait_for_space_margin) { + wait_for_space(fdout, + untrusted_hdr->filelen + opt_wait_for_space_margin); + } total_bytes += untrusted_hdr->filelen; ret = copy_file(fdout, 0, untrusted_hdr->filelen, &crc32_sum); if (ret != COPY_FILE_OK) {