ia64/xen-unstable

changeset 13044:a05fefbeb19f

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@xensource.com>
author Julian Chesterfield <julian@xensource.com>
date Thu Dec 14 20:23:07 2006 +0000 (2006-12-14)
parents bd102b60c43b
children 8b2de97db39d
files tools/blktap/drivers/block-qcow.c tools/blktap/drivers/qcow-create.c
line diff
     1.1 --- a/tools/blktap/drivers/block-qcow.c	Thu Dec 14 18:24:41 2006 +0000
     1.2 +++ b/tools/blktap/drivers/block-qcow.c	Thu Dec 14 20:23:07 2006 +0000
     1.3 @@ -74,8 +74,9 @@ struct pending_aio {
     1.4  #define XEN_MAGIC  (('X' << 24) | ('E' << 16) | ('N' << 8) | 0xfb)
     1.5  #define QCOW_VERSION 1
     1.6  
     1.7 -#define QCOW_CRYPT_NONE 0
     1.8 -#define QCOW_CRYPT_AES  1
     1.9 +#define QCOW_CRYPT_NONE 0x00
    1.10 +#define QCOW_CRYPT_AES  0x01
    1.11 +#define QCOW_SPARSE_FILE 0x02
    1.12  
    1.13  #define QCOW_OFLAG_COMPRESSED (1LL << 63)
    1.14  
    1.15 @@ -101,6 +102,7 @@ typedef struct QCowHeader_ext {
    1.16          uint32_t xmagic;
    1.17          uint32_t cksum;
    1.18          uint32_t min_cluster_alloc;
    1.19 +        uint32_t flags;
    1.20  } QCowHeader_ext;
    1.21  
    1.22  #define L2_CACHE_SIZE 16  /*Fixed allocation in Qemu*/
    1.23 @@ -119,6 +121,7 @@ struct tdqcow_state {
    1.24  	int cluster_alloc;             /*Blktap fix for allocating full 
    1.25  					*extents*/
    1.26  	int min_cluster_alloc;         /*Blktap historical extent alloc*/
    1.27 +	int sparse;                    /*Indicates whether to preserve sparseness*/
    1.28  	int l2_bits;                   /*Size of L2 table entry*/
    1.29  	int l2_size;                   /*Full table size*/
    1.30  	int l1_size;                   /*L1 table size*/
    1.31 @@ -413,6 +416,37 @@ static void encrypt_sectors(struct tdqco
    1.32  	}
    1.33  }
    1.34  
    1.35 +static int qtruncate(int fd, off_t length, int sparse)
    1.36 +{
    1.37 +	int current, ret, i; 
    1.38 +	int sectors = length/DEFAULT_SECTOR_SIZE;
    1.39 +	struct stat st;
    1.40 +	char buf[DEFAULT_SECTOR_SIZE];
    1.41 +
    1.42 +	/* If length is greater than the current file len
    1.43 +	 * we synchronously write zeroes to the end of the 
    1.44 +	 * file, otherwise we truncate the length down
    1.45 +	 */
    1.46 +	memset(buf, 0x00, DEFAULT_SECTOR_SIZE);
    1.47 +	ret = fstat(fd, &st);
    1.48 +	if((ret == -1) || S_ISBLK(st.st_mode))
    1.49 +		return -1;
    1.50 +
    1.51 +	if(st.st_size < length) {
    1.52 +		/*We are extending the file*/
    1.53 +		lseek(fd, 0, SEEK_END);
    1.54 +		for (i = 0; i < sectors; i++ ) {
    1.55 +			ret = write(fd, buf, DEFAULT_SECTOR_SIZE);
    1.56 +			if (ret != DEFAULT_SECTOR_SIZE)
    1.57 +				return -1;
    1.58 +		}
    1.59 +		
    1.60 +	} else if(sparse && (st.st_size > length))
    1.61 +		ftruncate(fd, length);
    1.62 +
    1.63 +	return 1;
    1.64 +}
    1.65 +
    1.66  
    1.67  /* 'allocate' is:
    1.68   *
    1.69 @@ -463,8 +497,8 @@ static uint64_t get_cluster_offset(struc
    1.70  		
    1.71  		/*Truncate file for L2 table 
    1.72  		 *(initialised to zero in case we crash)*/
    1.73 -		ftruncate(s->fd, l2_offset + (s->l2_size * sizeof(uint64_t)));
    1.74 -		s->fd_end += (s->l2_size * sizeof(uint64_t));
    1.75 +		qtruncate(s->fd, l2_offset + (s->l2_size * sizeof(uint64_t)), s->sparse);
    1.76 +		s->fd_end = l2_offset + (s->l2_size * sizeof(uint64_t));
    1.77  
    1.78  		/*Update the L1 table entry on disk
    1.79                   * (for O_DIRECT we write 4KByte blocks)*/
    1.80 @@ -483,7 +517,7 @@ static uint64_t get_cluster_offset(struc
    1.81  		 */
    1.82  		lseek(s->fd, s->l1_table_offset + (l1_sector << 12), SEEK_SET);
    1.83  		if (write(s->fd, tmp_ptr, 4096) != 4096)
    1.84 -			return 0;
    1.85 +		 	return 0;
    1.86  		free(tmp_ptr);
    1.87  
    1.88  		new_l2_table = 1;
    1.89 @@ -530,8 +564,8 @@ cache_miss:
    1.90  				(s->l2_size * sizeof(uint64_t));
    1.91  			cluster_offset = (cluster_offset + s->cluster_size - 1)
    1.92  				& ~(s->cluster_size - 1);
    1.93 -			ftruncate(s->fd, cluster_offset + 
    1.94 -				  (s->cluster_size * s->l2_size));
    1.95 +			qtruncate(s->fd, cluster_offset + 
    1.96 +				  (s->cluster_size * s->l2_size), s->sparse);
    1.97  			s->fd_end = cluster_offset + 
    1.98  				(s->cluster_size * s->l2_size);
    1.99  			for (i = 0; i < s->l2_size; i++) {
   1.100 @@ -542,7 +576,7 @@ cache_miss:
   1.101  
   1.102  		lseek(s->fd, l2_offset, SEEK_SET);
   1.103  		if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) !=
   1.104 -		    s->l2_size * sizeof(uint64_t))
   1.105 +		   s->l2_size * sizeof(uint64_t))
   1.106  			return 0;
   1.107  	} else {
   1.108  		lseek(s->fd, l2_offset, SEEK_SET);
   1.109 @@ -573,7 +607,7 @@ found:
   1.110  			   overwritten */
   1.111  			if (decompress_cluster(s, cluster_offset) < 0)
   1.112  				return 0;
   1.113 -			cluster_offset = lseek(s->fd, 0, SEEK_END);
   1.114 +			cluster_offset = lseek(s->fd, s->fd_end, SEEK_SET);
   1.115  			cluster_offset = (cluster_offset + s->cluster_size - 1)
   1.116  				& ~(s->cluster_size - 1);
   1.117  			/* write the cluster content - not asynchronous */
   1.118 @@ -583,14 +617,15 @@ found:
   1.119  			    return -1;
   1.120  		} else {
   1.121  			/* allocate a new cluster */
   1.122 -			cluster_offset = lseek(s->fd, 0, SEEK_END);
   1.123 +			cluster_offset = lseek(s->fd, s->fd_end, SEEK_SET);
   1.124  			if (allocate == 1) {
   1.125  				/* round to cluster size */
   1.126  				cluster_offset = 
   1.127  					(cluster_offset + s->cluster_size - 1) 
   1.128  					& ~(s->cluster_size - 1);
   1.129 -				ftruncate(s->fd, cluster_offset + 
   1.130 -					  s->cluster_size);
   1.131 +				qtruncate(s->fd, cluster_offset + 
   1.132 +					  s->cluster_size, s->sparse);
   1.133 +				s->fd_end = (cluster_offset + s->cluster_size);
   1.134  				/* if encrypted, we must initialize the cluster
   1.135  				   content which won't be written */
   1.136  				if (s->crypt_method && 
   1.137 @@ -633,9 +668,9 @@ found:
   1.138  			DPRINTF("ERROR allocating memory for L1 table\n");
   1.139  		}
   1.140  		memcpy(tmp_ptr2, l2_ptr, 4096);
   1.141 -		aio_lock(s, offset >> 9);
   1.142 -		async_write(s, s->fd, 4096, l2_offset + (l2_sector << 12), 
   1.143 -			    tmp_ptr2, 0, -2, offset >> 9, 0, NULL);
   1.144 +		lseek(s->fd, l2_offset + (l2_sector << 12), SEEK_SET);
   1.145 +		write(s->fd, tmp_ptr2, 4096);
   1.146 +		free(tmp_ptr2);
   1.147  	}
   1.148  	return cluster_offset;
   1.149  }
   1.150 @@ -733,6 +768,7 @@ int tdqcow_open (struct td_state *bs, co
   1.151  	QCowHeader *header;
   1.152  	QCowHeader_ext *exthdr;
   1.153  	uint32_t cksum;
   1.154 +	uint64_t final_cluster = 0;
   1.155  
   1.156   	DPRINTF("QCOW: Opening %s\n",name);
   1.157  	/* set up a pipe so that we can hand back a poll fd that won't fire.*/
   1.158 @@ -766,7 +802,7 @@ int tdqcow_open (struct td_state *bs, co
   1.159  	be64_to_cpus(&header->size);
   1.160  	be32_to_cpus(&header->crypt_method);
   1.161  	be64_to_cpus(&header->l1_table_offset);
   1.162 -   
   1.163 +
   1.164  	if (header->magic != QCOW_MAGIC || header->version > QCOW_VERSION)
   1.165  		goto fail;
   1.166  	if (header->size <= 1 || header->cluster_bits < 9)
   1.167 @@ -798,6 +834,7 @@ int tdqcow_open (struct td_state *bs, co
   1.168  	}
   1.169  	ret = posix_memalign((void **)&s->l1_table, 4096, l1_table_size);
   1.170  	if (ret != 0) goto fail;
   1.171 +
   1.172  	memset(s->l1_table, 0x00, l1_table_size);
   1.173  
   1.174  	DPRINTF("L1 Table offset detected: %llu, size %d (%d)\n",
   1.175 @@ -808,10 +845,13 @@ int tdqcow_open (struct td_state *bs, co
   1.176  	lseek(fd, s->l1_table_offset, SEEK_SET);
   1.177  	if (read(fd, s->l1_table, l1_table_size) != l1_table_size)
   1.178  		goto fail;
   1.179 -/*	for(i = 0;i < s->l1_size; i++) {
   1.180 +
   1.181 +	for(i = 0;i < s->l1_size; i++) {
   1.182  		//be64_to_cpus(&s->l1_table[i]);
   1.183 -		DPRINTF("L1[%d] => %llu\n", i, s->l1_table[i]);
   1.184 -		}*/
   1.185 +		//DPRINTF("L1[%d] => %llu\n", i, s->l1_table[i]);
   1.186 +		if (s->l1_table[i] > final_cluster)
   1.187 +			final_cluster = s->l1_table[i];
   1.188 +	}
   1.189  
   1.190  	/* alloc L2 cache */
   1.191  	size = s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t);
   1.192 @@ -870,10 +910,14 @@ int tdqcow_open (struct td_state *bs, co
   1.193  		/*Finally check the L1 table cksum*/
   1.194  		be32_to_cpus(&exthdr->cksum);
   1.195  		cksum = gen_cksum((char *)s->l1_table, s->l1_size * sizeof(uint64_t));
   1.196 -		if(exthdr->cksum != cksum)
   1.197 +		if(exthdr->cksum != cksum) {
   1.198  			goto end_xenhdr;
   1.199 +		}
   1.200  			
   1.201  		be32_to_cpus(&exthdr->min_cluster_alloc);
   1.202 +		be32_to_cpus(&exthdr->flags);
   1.203 +		if (exthdr->flags & QCOW_SPARSE_FILE)
   1.204 +			s->sparse = 1;
   1.205  		s->min_cluster_alloc = exthdr->min_cluster_alloc; 
   1.206  	}
   1.207  
   1.208 @@ -882,7 +926,8 @@ int tdqcow_open (struct td_state *bs, co
   1.209  		DPRINTF("Unable to initialise AIO state\n");
   1.210  		goto fail;
   1.211  	}
   1.212 -	s->fd_end = lseek(s->fd, 0, SEEK_END);
   1.213 +	s->fd_end = (final_cluster == 0 ? (s->l1_table_offset + l1_table_size) : 
   1.214 +				(final_cluster + s->cluster_size));
   1.215  
   1.216  	return 0;
   1.217  	
   1.218 @@ -1172,7 +1217,7 @@ int qcow_create(const char *filename, ui
   1.219  	QCowHeader header;
   1.220  	QCowHeader_ext exthdr;
   1.221  	char backing_filename[1024], *ptr;
   1.222 -	uint64_t tmp, size;
   1.223 +	uint64_t tmp, size, total_length;
   1.224  	struct stat st;
   1.225  
   1.226  	DPRINTF("Qcow_create: size %llu\n",(long long unsigned)total_size);
   1.227 @@ -1260,7 +1305,7 @@ int qcow_create(const char *filename, ui
   1.228  	DPRINTF("L1 Table offset: %d, size %d\n",
   1.229  		header_size,
   1.230  		(int)(l1_size * sizeof(uint64_t)));
   1.231 -	if (flags) {
   1.232 +	if (flags & QCOW_CRYPT_AES) {
   1.233  		header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
   1.234  	} else {
   1.235  		header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
   1.236 @@ -1270,8 +1315,26 @@ int qcow_create(const char *filename, ui
   1.237  	exthdr.cksum = cpu_to_be32(gen_cksum(ptr, l1_size * sizeof(uint64_t)));
   1.238  	printf("Created cksum: %d\n",exthdr.cksum);
   1.239  	free(ptr);
   1.240 +
   1.241 +	/*adjust file length to 4 KByte boundary*/
   1.242 +	length = header_size + l1_size * sizeof(uint64_t);
   1.243 +	if (length % 4096 > 0) {
   1.244 +		length = ((length >> 12) + 1) << 12;
   1.245 +		qtruncate(fd, length, 0);
   1.246 +		DPRINTF("Adjusted filelength to %d for 4 "
   1.247 +			"Kbyte alignment\n",length);
   1.248 +	}
   1.249 +
   1.250 +	if (!(flags & QCOW_SPARSE_FILE)) {
   1.251 +		/*Filesize is length + 	l1_size * (1 << s->l2_bits) + (size*512)*/
   1.252 +		total_length = length + (l1_size * (1 << 9)) + (size * 512);
   1.253 +		qtruncate(fd, total_length, 0);
   1.254 +		printf("File truncated to length %llu\n",total_length);
   1.255 +	}
   1.256 +	exthdr.flags = cpu_to_be32(flags);
   1.257  	
   1.258  	/* write all the data */
   1.259 +	lseek(fd, 0, SEEK_SET);
   1.260  	ret += write(fd, &header, sizeof(header));
   1.261  	ret += write(fd, &exthdr, sizeof(exthdr));
   1.262  	if (backing_file) {
   1.263 @@ -1283,15 +1346,6 @@ int qcow_create(const char *filename, ui
   1.264  		ret += write(fd, &tmp, sizeof(tmp));
   1.265  	}
   1.266  
   1.267 -	/*adjust file length to 4 KByte boundary*/
   1.268 -	length = header_size + l1_size * sizeof(uint64_t);
   1.269 -	if (length % 4096 > 0) {
   1.270 -		length = ((length >> 12) + 1) << 12;
   1.271 -		ftruncate(fd, length);
   1.272 -		DPRINTF("Adjusted filelength to %d for 4 "
   1.273 -			"Kbyte alignment\n",length);
   1.274 -	}
   1.275 -
   1.276  	close(fd);
   1.277  
   1.278  	return 0;
   1.279 @@ -1306,7 +1360,7 @@ int qcow_make_empty(struct td_state *bs)
   1.280  	lseek(s->fd, s->l1_table_offset, SEEK_SET);
   1.281  	if (write(s->fd, s->l1_table, l1_length) < 0)
   1.282  		return -1;
   1.283 -	ftruncate(s->fd, s->l1_table_offset + l1_length);
   1.284 +	qtruncate(s->fd, s->l1_table_offset + l1_length, s->sparse);
   1.285  
   1.286  	memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
   1.287  	memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
     2.1 --- a/tools/blktap/drivers/qcow-create.c	Thu Dec 14 18:24:41 2006 +0000
     2.2 +++ b/tools/blktap/drivers/qcow-create.c	Thu Dec 14 20:23:07 2006 +0000
     2.3 @@ -47,32 +47,69 @@
     2.4  #define DFPRINTF(_f, _a...) ((void)0)
     2.5  #endif
     2.6  
     2.7 +#define QCOW_NONSPARSE_FILE 0x00
     2.8 +#define QCOW_SPARSE_FILE 0x02
     2.9 +#define MAX_NAME_LEN 1000
    2.10 +
    2.11 +void help(void)
    2.12 +{
    2.13 +	fprintf(stderr, "Qcow-utils: v1.0.0\n");
    2.14 +	fprintf(stderr, 
    2.15 +		"usage: qcow-create [-h help] [-p reserve] <SIZE(MB)> <FILENAME> "
    2.16 +		"[<BACKING_FILENAME>]\n"); 
    2.17 +	exit(-1);
    2.18 +}
    2.19  
    2.20  int main(int argc, char *argv[])
    2.21  {
    2.22 -	int ret = -1;
    2.23 +	int ret = -1, c, backed = 0;
    2.24 +	int flags =  QCOW_SPARSE_FILE;
    2.25  	uint64_t size;
    2.26 +	char filename[MAX_NAME_LEN], bfilename[MAX_NAME_LEN];
    2.27  
    2.28 -	if ( (argc < 3) || (argc > 4) ) {
    2.29 -		fprintf(stderr, "Qcow-utils: v1.0.0\n");
    2.30 -		fprintf(stderr, 
    2.31 -			"usage: %s <SIZE(MB)> <FILENAME> "
    2.32 -			"[<BACKING_FILENAME>]\n", 
    2.33 -			argv[0]);
    2.34 +        for(;;) {
    2.35 +                c = getopt(argc, argv, "hp");
    2.36 +                if (c == -1)
    2.37 +                        break;
    2.38 +                switch(c) {
    2.39 +                case 'h':
    2.40 +                        help();
    2.41 +                        exit(0);
    2.42 +                        break;
    2.43 +                case 'p':
    2.44 +			flags = QCOW_NONSPARSE_FILE;
    2.45 +			break;
    2.46 +		}
    2.47 +	}
    2.48 +
    2.49 +	printf("Optind %d, argc %d\n", optind, argc);
    2.50 +	if ( !(optind == (argc - 2) || optind == (argc - 3)) )
    2.51 +		help();
    2.52 +
    2.53 +	size = atoi(argv[optind++]);
    2.54 +	size = size << 20;
    2.55 +
    2.56 +	if (snprintf(filename, MAX_NAME_LEN, "%s",argv[optind++]) >=
    2.57 +		MAX_NAME_LEN) {
    2.58 +		fprintf(stderr,"Device name too long\n");
    2.59  		exit(-1);
    2.60  	}
    2.61  
    2.62 -	size = atoi(argv[1]);
    2.63 -	size = size << 20;
    2.64 -	DFPRINTF("Creating file size %llu\n",(long long unsigned)size);
    2.65 -	switch(argc) {
    2.66 -	case 3: 
    2.67 -		ret = qcow_create(argv[2],size,NULL,0);
    2.68 -		break;
    2.69 -	case 4:
    2.70 -		ret = qcow_create(argv[2],size,argv[3],0);
    2.71 -		break;		
    2.72 +	if (optind != argc) {
    2.73 +		backed = 1;
    2.74 +		if (snprintf(bfilename, MAX_NAME_LEN, "%s",argv[optind++]) >=
    2.75 +			MAX_NAME_LEN) {
    2.76 +			fprintf(stderr,"Device name too long\n");
    2.77 +			exit(-1);
    2.78 +		}
    2.79  	}
    2.80 +
    2.81 +	DFPRINTF("Creating file size %llu, name %s\n",(long long unsigned)size, filename);
    2.82 +	if (!backed)
    2.83 +		ret = qcow_create(filename,size,NULL,flags);
    2.84 +	else
    2.85 +		ret = qcow_create(filename,size,bfilename,flags);
    2.86 +
    2.87  	if (ret < 0) DPRINTF("Unable to create QCOW file\n");
    2.88  	else DPRINTF("QCOW file successfully created\n");
    2.89