chiark / gitweb /
import-raw: when downloading raw images, generate sparse files if we can
authorLennart Poettering <lennart@poettering.net>
Sat, 17 Jan 2015 01:36:23 +0000 (02:36 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 19 Jan 2015 19:24:09 +0000 (20:24 +0100)
src/import/import-raw.c
src/shared/util.c
src/shared/util.h
src/test/test-util.c

index f830ba47ff8950a4b2944cbae9c29928a96592e4..c82d263787f996ed0a508c5b011fd9534077b84f 100644 (file)
@@ -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;
index 8f6d5e660c657356a429d115c17823f3412b2945..fd54023660c7df2a94b0452566bd66afbf7bfad8 100644 (file)
@@ -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;
+}
index 8a3e95a17aa11d55ed13e26e0d110264dde341c0..2e662c9d2d6b1fd18a8f645a6c709322a01e8b6e 100644 (file)
@@ -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);
index 4bb51545be471eab0c5da73547de847029aace6a..0c0d2f67f84accd1c7f4c13f75056864a4a570ce 100644 (file)
@@ -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;
 }