[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] Add sparseness flag to qcow-create.



# HG changeset patch
# User Julian Chesterfield <julian@xxxxxxxxxxxxx>
# Node ID a05fefbeb19f065d0ed9d5b73d7572e44326fd79
# Parent  bd102b60c43bc4be403117b27ad41eeecf4c857d
Add sparseness flag to qcow-create.

For environments where space must be guaranteed in advance
use the -p flag to remove sparseness from the qcow file.

Signed-off-by: Julian Chesterfield <julian@xxxxxxxxxxxxx>
---
 tools/blktap/drivers/block-qcow.c  |  120 ++++++++++++++++++++++++++-----------
 tools/blktap/drivers/qcow-create.c |   71 ++++++++++++++++-----
 2 files changed, 141 insertions(+), 50 deletions(-)

diff -r bd102b60c43b -r a05fefbeb19f tools/blktap/drivers/block-qcow.c
--- a/tools/blktap/drivers/block-qcow.c Thu Dec 14 18:24:41 2006 +0000
+++ b/tools/blktap/drivers/block-qcow.c Thu Dec 14 20:23:07 2006 +0000
@@ -74,8 +74,9 @@ struct pending_aio {
 #define XEN_MAGIC  (('X' << 24) | ('E' << 16) | ('N' << 8) | 0xfb)
 #define QCOW_VERSION 1
 
-#define QCOW_CRYPT_NONE 0
-#define QCOW_CRYPT_AES  1
+#define QCOW_CRYPT_NONE 0x00
+#define QCOW_CRYPT_AES  0x01
+#define QCOW_SPARSE_FILE 0x02
 
 #define QCOW_OFLAG_COMPRESSED (1LL << 63)
 
@@ -101,6 +102,7 @@ typedef struct QCowHeader_ext {
         uint32_t xmagic;
         uint32_t cksum;
         uint32_t min_cluster_alloc;
+        uint32_t flags;
 } QCowHeader_ext;
 
 #define L2_CACHE_SIZE 16  /*Fixed allocation in Qemu*/
@@ -119,6 +121,7 @@ struct tdqcow_state {
        int cluster_alloc;             /*Blktap fix for allocating full 
                                        *extents*/
        int min_cluster_alloc;         /*Blktap historical extent alloc*/
+       int sparse;                    /*Indicates whether to preserve 
sparseness*/
        int l2_bits;                   /*Size of L2 table entry*/
        int l2_size;                   /*Full table size*/
        int l1_size;                   /*L1 table size*/
@@ -413,6 +416,37 @@ static void encrypt_sectors(struct tdqco
        }
 }
 
+static int qtruncate(int fd, off_t length, int sparse)
+{
+       int current, ret, i; 
+       int sectors = length/DEFAULT_SECTOR_SIZE;
+       struct stat st;
+       char buf[DEFAULT_SECTOR_SIZE];
+
+       /* If length is greater than the current file len
+        * we synchronously write zeroes to the end of the 
+        * file, otherwise we truncate the length down
+        */
+       memset(buf, 0x00, DEFAULT_SECTOR_SIZE);
+       ret = fstat(fd, &st);
+       if((ret == -1) || S_ISBLK(st.st_mode))
+               return -1;
+
+       if(st.st_size < length) {
+               /*We are extending the file*/
+               lseek(fd, 0, SEEK_END);
+               for (i = 0; i < sectors; i++ ) {
+                       ret = write(fd, buf, DEFAULT_SECTOR_SIZE);
+                       if (ret != DEFAULT_SECTOR_SIZE)
+                               return -1;
+               }
+               
+       } else if(sparse && (st.st_size > length))
+               ftruncate(fd, length);
+
+       return 1;
+}
+
 
 /* 'allocate' is:
  *
@@ -463,8 +497,8 @@ static uint64_t get_cluster_offset(struc
                
                /*Truncate file for L2 table 
                 *(initialised to zero in case we crash)*/
-               ftruncate(s->fd, l2_offset + (s->l2_size * sizeof(uint64_t)));
-               s->fd_end += (s->l2_size * sizeof(uint64_t));
+               qtruncate(s->fd, l2_offset + (s->l2_size * sizeof(uint64_t)), 
s->sparse);
+               s->fd_end = l2_offset + (s->l2_size * sizeof(uint64_t));
 
                /*Update the L1 table entry on disk
                  * (for O_DIRECT we write 4KByte blocks)*/
@@ -483,7 +517,7 @@ static uint64_t get_cluster_offset(struc
                 */
                lseek(s->fd, s->l1_table_offset + (l1_sector << 12), SEEK_SET);
                if (write(s->fd, tmp_ptr, 4096) != 4096)
-                       return 0;
+                       return 0;
                free(tmp_ptr);
 
                new_l2_table = 1;
@@ -530,8 +564,8 @@ cache_miss:
                                (s->l2_size * sizeof(uint64_t));
                        cluster_offset = (cluster_offset + s->cluster_size - 1)
                                & ~(s->cluster_size - 1);
-                       ftruncate(s->fd, cluster_offset + 
-                                 (s->cluster_size * s->l2_size));
+                       qtruncate(s->fd, cluster_offset + 
+                                 (s->cluster_size * s->l2_size), s->sparse);
                        s->fd_end = cluster_offset + 
                                (s->cluster_size * s->l2_size);
                        for (i = 0; i < s->l2_size; i++) {
@@ -542,7 +576,7 @@ cache_miss:
 
                lseek(s->fd, l2_offset, SEEK_SET);
                if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) !=
-                   s->l2_size * sizeof(uint64_t))
+                  s->l2_size * sizeof(uint64_t))
                        return 0;
        } else {
                lseek(s->fd, l2_offset, SEEK_SET);
@@ -573,7 +607,7 @@ found:
                           overwritten */
                        if (decompress_cluster(s, cluster_offset) < 0)
                                return 0;
-                       cluster_offset = lseek(s->fd, 0, SEEK_END);
+                       cluster_offset = lseek(s->fd, s->fd_end, SEEK_SET);
                        cluster_offset = (cluster_offset + s->cluster_size - 1)
                                & ~(s->cluster_size - 1);
                        /* write the cluster content - not asynchronous */
@@ -583,14 +617,15 @@ found:
                            return -1;
                } else {
                        /* allocate a new cluster */
-                       cluster_offset = lseek(s->fd, 0, SEEK_END);
+                       cluster_offset = lseek(s->fd, s->fd_end, SEEK_SET);
                        if (allocate == 1) {
                                /* round to cluster size */
                                cluster_offset = 
                                        (cluster_offset + s->cluster_size - 1) 
                                        & ~(s->cluster_size - 1);
-                               ftruncate(s->fd, cluster_offset + 
-                                         s->cluster_size);
+                               qtruncate(s->fd, cluster_offset + 
+                                         s->cluster_size, s->sparse);
+                               s->fd_end = (cluster_offset + s->cluster_size);
                                /* if encrypted, we must initialize the cluster
                                   content which won't be written */
                                if (s->crypt_method && 
@@ -633,9 +668,9 @@ found:
                        DPRINTF("ERROR allocating memory for L1 table\n");
                }
                memcpy(tmp_ptr2, l2_ptr, 4096);
-               aio_lock(s, offset >> 9);
-               async_write(s, s->fd, 4096, l2_offset + (l2_sector << 12), 
-                           tmp_ptr2, 0, -2, offset >> 9, 0, NULL);
+               lseek(s->fd, l2_offset + (l2_sector << 12), SEEK_SET);
+               write(s->fd, tmp_ptr2, 4096);
+               free(tmp_ptr2);
        }
        return cluster_offset;
 }
@@ -733,6 +768,7 @@ int tdqcow_open (struct td_state *bs, co
        QCowHeader *header;
        QCowHeader_ext *exthdr;
        uint32_t cksum;
+       uint64_t final_cluster = 0;
 
        DPRINTF("QCOW: Opening %s\n",name);
        /* set up a pipe so that we can hand back a poll fd that won't fire.*/
@@ -766,7 +802,7 @@ int tdqcow_open (struct td_state *bs, co
        be64_to_cpus(&header->size);
        be32_to_cpus(&header->crypt_method);
        be64_to_cpus(&header->l1_table_offset);
-   
+
        if (header->magic != QCOW_MAGIC || header->version > QCOW_VERSION)
                goto fail;
        if (header->size <= 1 || header->cluster_bits < 9)
@@ -798,6 +834,7 @@ int tdqcow_open (struct td_state *bs, co
        }
        ret = posix_memalign((void **)&s->l1_table, 4096, l1_table_size);
        if (ret != 0) goto fail;
+
        memset(s->l1_table, 0x00, l1_table_size);
 
        DPRINTF("L1 Table offset detected: %llu, size %d (%d)\n",
@@ -808,10 +845,13 @@ int tdqcow_open (struct td_state *bs, co
        lseek(fd, s->l1_table_offset, SEEK_SET);
        if (read(fd, s->l1_table, l1_table_size) != l1_table_size)
                goto fail;
-/*     for(i = 0;i < s->l1_size; i++) {
+
+       for(i = 0;i < s->l1_size; i++) {
                //be64_to_cpus(&s->l1_table[i]);
-               DPRINTF("L1[%d] => %llu\n", i, s->l1_table[i]);
-               }*/
+               //DPRINTF("L1[%d] => %llu\n", i, s->l1_table[i]);
+               if (s->l1_table[i] > final_cluster)
+                       final_cluster = s->l1_table[i];
+       }
 
        /* alloc L2 cache */
        size = s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t);
@@ -870,10 +910,14 @@ int tdqcow_open (struct td_state *bs, co
                /*Finally check the L1 table cksum*/
                be32_to_cpus(&exthdr->cksum);
                cksum = gen_cksum((char *)s->l1_table, s->l1_size * 
sizeof(uint64_t));
-               if(exthdr->cksum != cksum)
+               if(exthdr->cksum != cksum) {
                        goto end_xenhdr;
+               }
                        
                be32_to_cpus(&exthdr->min_cluster_alloc);
+               be32_to_cpus(&exthdr->flags);
+               if (exthdr->flags & QCOW_SPARSE_FILE)
+                       s->sparse = 1;
                s->min_cluster_alloc = exthdr->min_cluster_alloc; 
        }
 
@@ -882,7 +926,8 @@ int tdqcow_open (struct td_state *bs, co
                DPRINTF("Unable to initialise AIO state\n");
                goto fail;
        }
-       s->fd_end = lseek(s->fd, 0, SEEK_END);
+       s->fd_end = (final_cluster == 0 ? (s->l1_table_offset + l1_table_size) 
: 
+                               (final_cluster + s->cluster_size));
 
        return 0;
        
@@ -1172,7 +1217,7 @@ int qcow_create(const char *filename, ui
        QCowHeader header;
        QCowHeader_ext exthdr;
        char backing_filename[1024], *ptr;
-       uint64_t tmp, size;
+       uint64_t tmp, size, total_length;
        struct stat st;
 
        DPRINTF("Qcow_create: size %llu\n",(long long unsigned)total_size);
@@ -1260,7 +1305,7 @@ int qcow_create(const char *filename, ui
        DPRINTF("L1 Table offset: %d, size %d\n",
                header_size,
                (int)(l1_size * sizeof(uint64_t)));
-       if (flags) {
+       if (flags & QCOW_CRYPT_AES) {
                header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
        } else {
                header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
@@ -1270,8 +1315,26 @@ int qcow_create(const char *filename, ui
        exthdr.cksum = cpu_to_be32(gen_cksum(ptr, l1_size * sizeof(uint64_t)));
        printf("Created cksum: %d\n",exthdr.cksum);
        free(ptr);
+
+       /*adjust file length to 4 KByte boundary*/
+       length = header_size + l1_size * sizeof(uint64_t);
+       if (length % 4096 > 0) {
+               length = ((length >> 12) + 1) << 12;
+               qtruncate(fd, length, 0);
+               DPRINTF("Adjusted filelength to %d for 4 "
+                       "Kbyte alignment\n",length);
+       }
+
+       if (!(flags & QCOW_SPARSE_FILE)) {
+               /*Filesize is length +  l1_size * (1 << s->l2_bits) + 
(size*512)*/
+               total_length = length + (l1_size * (1 << 9)) + (size * 512);
+               qtruncate(fd, total_length, 0);
+               printf("File truncated to length %llu\n",total_length);
+       }
+       exthdr.flags = cpu_to_be32(flags);
        
        /* write all the data */
+       lseek(fd, 0, SEEK_SET);
        ret += write(fd, &header, sizeof(header));
        ret += write(fd, &exthdr, sizeof(exthdr));
        if (backing_file) {
@@ -1283,15 +1346,6 @@ int qcow_create(const char *filename, ui
                ret += write(fd, &tmp, sizeof(tmp));
        }
 
-       /*adjust file length to 4 KByte boundary*/
-       length = header_size + l1_size * sizeof(uint64_t);
-       if (length % 4096 > 0) {
-               length = ((length >> 12) + 1) << 12;
-               ftruncate(fd, length);
-               DPRINTF("Adjusted filelength to %d for 4 "
-                       "Kbyte alignment\n",length);
-       }
-
        close(fd);
 
        return 0;
@@ -1306,7 +1360,7 @@ int qcow_make_empty(struct td_state *bs)
        lseek(s->fd, s->l1_table_offset, SEEK_SET);
        if (write(s->fd, s->l1_table, l1_length) < 0)
                return -1;
-       ftruncate(s->fd, s->l1_table_offset + l1_length);
+       qtruncate(s->fd, s->l1_table_offset + l1_length, s->sparse);
 
        memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
        memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
diff -r bd102b60c43b -r a05fefbeb19f tools/blktap/drivers/qcow-create.c
--- a/tools/blktap/drivers/qcow-create.c        Thu Dec 14 18:24:41 2006 +0000
+++ b/tools/blktap/drivers/qcow-create.c        Thu Dec 14 20:23:07 2006 +0000
@@ -47,32 +47,69 @@
 #define DFPRINTF(_f, _a...) ((void)0)
 #endif
 
+#define QCOW_NONSPARSE_FILE 0x00
+#define QCOW_SPARSE_FILE 0x02
+#define MAX_NAME_LEN 1000
+
+void help(void)
+{
+       fprintf(stderr, "Qcow-utils: v1.0.0\n");
+       fprintf(stderr, 
+               "usage: qcow-create [-h help] [-p reserve] <SIZE(MB)> 
<FILENAME> "
+               "[<BACKING_FILENAME>]\n"); 
+       exit(-1);
+}
 
 int main(int argc, char *argv[])
 {
-       int ret = -1;
+       int ret = -1, c, backed = 0;
+       int flags =  QCOW_SPARSE_FILE;
        uint64_t size;
+       char filename[MAX_NAME_LEN], bfilename[MAX_NAME_LEN];
 
-       if ( (argc < 3) || (argc > 4) ) {
-               fprintf(stderr, "Qcow-utils: v1.0.0\n");
-               fprintf(stderr, 
-                       "usage: %s <SIZE(MB)> <FILENAME> "
-                       "[<BACKING_FILENAME>]\n", 
-                       argv[0]);
+        for(;;) {
+                c = getopt(argc, argv, "hp");
+                if (c == -1)
+                        break;
+                switch(c) {
+                case 'h':
+                        help();
+                        exit(0);
+                        break;
+                case 'p':
+                       flags = QCOW_NONSPARSE_FILE;
+                       break;
+               }
+       }
+
+       printf("Optind %d, argc %d\n", optind, argc);
+       if ( !(optind == (argc - 2) || optind == (argc - 3)) )
+               help();
+
+       size = atoi(argv[optind++]);
+       size = size << 20;
+
+       if (snprintf(filename, MAX_NAME_LEN, "%s",argv[optind++]) >=
+               MAX_NAME_LEN) {
+               fprintf(stderr,"Device name too long\n");
                exit(-1);
        }
 
-       size = atoi(argv[1]);
-       size = size << 20;
-       DFPRINTF("Creating file size %llu\n",(long long unsigned)size);
-       switch(argc) {
-       case 3: 
-               ret = qcow_create(argv[2],size,NULL,0);
-               break;
-       case 4:
-               ret = qcow_create(argv[2],size,argv[3],0);
-               break;          
+       if (optind != argc) {
+               backed = 1;
+               if (snprintf(bfilename, MAX_NAME_LEN, "%s",argv[optind++]) >=
+                       MAX_NAME_LEN) {
+                       fprintf(stderr,"Device name too long\n");
+                       exit(-1);
+               }
        }
+
+       DFPRINTF("Creating file size %llu, name %s\n",(long long unsigned)size, 
filename);
+       if (!backed)
+               ret = qcow_create(filename,size,NULL,flags);
+       else
+               ret = qcow_create(filename,size,bfilename,flags);
+
        if (ret < 0) DPRINTF("Unable to create QCOW file\n");
        else DPRINTF("QCOW file successfully created\n");
 

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.