summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/import/import-raw.c10
-rw-r--r--src/shared/util.c62
-rw-r--r--src/shared/util.h2
-rw-r--r--src/test/test-util.c37
4 files changed, 110 insertions, 1 deletions
diff --git a/src/import/import-raw.c b/src/import/import-raw.c
index f830ba47ff..c82d263787 100644
--- a/src/import/import-raw.c
+++ b/src/import/import-raw.c
@@ -332,6 +332,14 @@ static void raw_import_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result
goto fail;
}
+ /* Make sure the file size is right, in case the file was
+ * sparse and we just seeked for the last part */
+ if (ftruncate(f->disk_fd, f->written_uncompressed) < 0) {
+ log_error_errno(errno, "Failed to truncate file: %m");
+ r = -errno;
+ goto fail;
+ }
+
r = raw_import_maybe_convert_qcow2(f);
if (r < 0)
goto fail;
@@ -427,7 +435,7 @@ static int raw_import_file_write_uncompressed(RawImportFile *f, void *p, size_t
return -EFBIG;
}
- n = write(f->disk_fd, p, sz);
+ n = sparse_write(f->disk_fd, p, sz, 64);
if (n < 0) {
log_error_errno(errno, "Failed to write file: %m");
return -errno;
diff --git a/src/shared/util.c b/src/shared/util.c
index 8f6d5e660c..fd54023660 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -7931,3 +7931,65 @@ void release_lock_file(LockFile *f) {
f->fd = safe_close(f->fd);
f->operation = 0;
}
+
+static size_t nul_length(const uint8_t *p, size_t sz) {
+ size_t n = 0;
+
+ while (sz > 0) {
+ if (*p != 0)
+ break;
+
+ n++;
+ p++;
+ sz--;
+ }
+
+ return n;
+}
+
+ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
+ const uint8_t *q, *w, *e;
+ ssize_t l;
+
+ q = w = p;
+ e = q + sz;
+ while (q < e) {
+ size_t n;
+
+ n = nul_length(q, e - q);
+
+ /* If there are more than the specified run length of
+ * NUL bytes, or if this is the beginning or the end
+ * of the buffer, then seek instead of write */
+ if ((n > run_length) ||
+ (n > 0 && q == p) ||
+ (n > 0 && q + n >= e)) {
+ if (q > w) {
+ l = write(fd, w, q - w);
+ if (l < 0)
+ return -errno;
+ if (l != q -w)
+ return -EIO;
+ }
+
+ if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
+ return -errno;
+
+ q += n;
+ w = q;
+ } else if (n > 0)
+ q += n;
+ else
+ q ++;
+ }
+
+ if (q > w) {
+ l = write(fd, w, q - w);
+ if (l < 0)
+ return -errno;
+ if (l != q - w)
+ return -EIO;
+ }
+
+ return q - (const uint8_t*) p;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index 8a3e95a17a..2e662c9d2d 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -1062,3 +1062,5 @@ void release_lock_file(LockFile *f);
#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
+
+ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 4bb51545be..0c0d2f67f8 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -1464,6 +1464,42 @@ static void test_uid_ptr(void) {
assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000);
}
+static void test_sparse_write_one(int fd, const char *buffer, size_t n) {
+ char check[n];
+
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(ftruncate(fd, 0) >= 0);
+ assert_se(sparse_write(fd, buffer, n, 4) == (ssize_t) n);
+
+ assert_se(lseek(fd, 0, SEEK_CUR) == (off_t) n);
+ assert_se(ftruncate(fd, n) >= 0);
+
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(read(fd, check, n) == (ssize_t) n);
+
+ assert_se(memcmp(buffer, check, n) == 0);
+}
+
+static void test_sparse_write(void) {
+ const char test_a[] = "test";
+ const char test_b[] = "\0\0\0\0test\0\0\0\0";
+ const char test_c[] = "\0\0test\0\0\0\0";
+ const char test_d[] = "\0\0test\0\0\0test\0\0\0\0test\0\0\0\0\0test\0\0\0test\0\0\0\0test\0\0\0\0\0\0\0\0";
+ const char test_e[] = "test\0\0\0\0test";
+ _cleanup_close_ int fd = -1;
+ char fn[] = "/tmp/sparseXXXXXX";
+
+ fd = mkostemp(fn, O_CLOEXEC);
+ assert_se(fd >= 0);
+ unlink(fn);
+
+ test_sparse_write_one(fd, test_a, sizeof(test_a));
+ test_sparse_write_one(fd, test_b, sizeof(test_b));
+ test_sparse_write_one(fd, test_c, sizeof(test_c));
+ test_sparse_write_one(fd, test_d, sizeof(test_d));
+ test_sparse_write_one(fd, test_e, sizeof(test_e));
+}
+
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
@@ -1540,6 +1576,7 @@ int main(int argc, char *argv[]) {
test_raw_clone();
test_same_fd();
test_uid_ptr();
+ test_sparse_write();
return 0;
}