diff options
-rw-r--r-- | src/import/import-raw.c | 10 | ||||
-rw-r--r-- | src/shared/util.c | 62 | ||||
-rw-r--r-- | src/shared/util.h | 2 | ||||
-rw-r--r-- | src/test/test-util.c | 37 |
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; } |