Commit 33e3963e1b9298e01cadd738124f0e618b5b79f5

Authored by bellard
1 parent cd4c3e88

added user mode Linux Copy On Write disk image support - added -snapshot support…

… (initial patch by Rusty Russell)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@309 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile
... ... @@ -28,7 +28,7 @@ else
28 28 LDFLAGS+=-Wl,-shared
29 29 endif
30 30 ifeq ($(TARGET_ARCH), i386)
31   -PROGS+=vl
  31 +PROGS+=vl vlmkcow
32 32 endif
33 33 endif
34 34  
... ... @@ -141,7 +141,10 @@ endif
141 141  
142 142 # must use static linking to avoid leaving stuff in virtual address space
143 143 vl: vl.o block.o libqemu.a
144   - $(CC) -static -Wl,-T,i386-vl.ld -o $@ $^ $(LIBS)
  144 + $(CC) -pg -static -Wl,-T,i386-vl.ld -o $@ $^ $(LIBS)
  145 +
  146 +vlmkcow: vlmkcow.o
  147 + $(CC) -o $@ $^ $(LIBS)
145 148  
146 149 depend: $(SRCS)
147 150 $(CC) -MM $(CFLAGS) $^ 1>.depend
... ...
... ... @@ -38,76 +38,286 @@
38 38 #include <sys/poll.h>
39 39 #include <errno.h>
40 40 #include <sys/wait.h>
  41 +#include <netinet/in.h>
41 42  
42 43 #include "vl.h"
43 44  
  45 +#define NO_THUNK_TYPE_SIZE
  46 +#include "thunk.h"
  47 +
44 48 struct BlockDriverState {
45   - int fd;
  49 + int fd; /* if -1, only COW mappings */
46 50 int64_t total_sectors;
47 51 int read_only;
  52 +
  53 + uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */
  54 + uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */
  55 + int cow_bitmap_size;
  56 + int cow_fd;
  57 + int64_t cow_sectors_offset;
  58 + char filename[1024];
48 59 };
49 60  
50   -BlockDriverState *bdrv_open(const char *filename)
  61 +BlockDriverState *bdrv_open(const char *filename, int snapshot)
51 62 {
52 63 BlockDriverState *bs;
53   - int fd;
  64 + int fd, cow_fd;
54 65 int64_t size;
  66 + char template[] = "/tmp/vl.XXXXXX";
  67 + struct cow_header_v2 cow_header;
  68 + struct stat st;
55 69  
56 70 bs = malloc(sizeof(BlockDriverState));
57 71 if(!bs)
58 72 return NULL;
59 73 bs->read_only = 0;
60   - fd = open(filename, O_RDWR);
  74 + bs->fd = -1;
  75 + bs->cow_fd = -1;
  76 + bs->cow_bitmap = NULL;
  77 + strcpy(bs->filename, filename);
  78 +
  79 + /* open standard HD image */
  80 + fd = open(filename, O_RDWR | O_LARGEFILE);
61 81 if (fd < 0) {
62   - fd = open(filename, O_RDONLY);
  82 + /* read only image on disk */
  83 + fd = open(filename, O_RDONLY | O_LARGEFILE);
63 84 if (fd < 0) {
64   - close(fd);
65   - free(bs);
66   - return NULL;
  85 + perror(filename);
  86 + goto fail;
67 87 }
68   - bs->read_only = 1;
  88 + if (!snapshot)
  89 + bs->read_only = 1;
69 90 }
70   - size = lseek64(fd, 0, SEEK_END);
71   - bs->total_sectors = size / 512;
72 91 bs->fd = fd;
  92 +
  93 + /* see if it is a cow image */
  94 + if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) {
  95 + fprintf(stderr, "%s: could not read header\n", filename);
  96 + goto fail;
  97 + }
  98 + if (cow_header.magic == htonl(COW_MAGIC) &&
  99 + cow_header.version == htonl(COW_VERSION)) {
  100 + /* cow image found */
  101 + size = cow_header.size;
  102 +#ifndef WORDS_BIGENDIAN
  103 + size = bswap64(size);
  104 +#endif
  105 + bs->total_sectors = size / 512;
  106 +
  107 + bs->cow_fd = fd;
  108 + bs->fd = -1;
  109 + if (cow_header.backing_file[0] != '\0') {
  110 + if (stat(cow_header.backing_file, &st) != 0) {
  111 + fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file);
  112 + goto fail;
  113 + }
  114 + if (st.st_mtime != htonl(cow_header.mtime)) {
  115 + fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file);
  116 + goto fail;
  117 + }
  118 + fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE);
  119 + if (fd < 0)
  120 + goto fail;
  121 + bs->fd = fd;
  122 + }
  123 + /* mmap the bitmap */
  124 + bs->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
  125 + bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size),
  126 + bs->cow_bitmap_size,
  127 + PROT_READ | PROT_WRITE,
  128 + MAP_SHARED, bs->cow_fd, 0);
  129 + if (bs->cow_bitmap_addr == MAP_FAILED)
  130 + goto fail;
  131 + bs->cow_bitmap = bs->cow_bitmap_addr + sizeof(cow_header);
  132 + bs->cow_sectors_offset = (bs->cow_bitmap_size + 511) & ~511;
  133 + snapshot = 0;
  134 + } else {
  135 + /* standard raw image */
  136 + size = lseek64(fd, 0, SEEK_END);
  137 + bs->total_sectors = size / 512;
  138 + bs->fd = fd;
  139 + }
  140 +
  141 + if (snapshot) {
  142 + /* create a temporary COW file */
  143 + cow_fd = mkstemp(template);
  144 + if (cow_fd < 0)
  145 + goto fail;
  146 + bs->cow_fd = cow_fd;
  147 + unlink(template);
  148 +
  149 + /* just need to allocate bitmap */
  150 + bs->cow_bitmap_size = (bs->total_sectors + 7) >> 3;
  151 + bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size),
  152 + bs->cow_bitmap_size,
  153 + PROT_READ | PROT_WRITE,
  154 + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  155 + if (bs->cow_bitmap_addr == MAP_FAILED)
  156 + goto fail;
  157 + bs->cow_bitmap = bs->cow_bitmap_addr;
  158 + bs->cow_sectors_offset = 0;
  159 + }
  160 +
73 161 return bs;
  162 + fail:
  163 + bdrv_close(bs);
  164 + return NULL;
74 165 }
75 166  
76 167 void bdrv_close(BlockDriverState *bs)
77 168 {
78   - close(bs->fd);
  169 + /* we unmap the mapping so that it is written to the COW file */
  170 + if (bs->cow_bitmap_addr)
  171 + munmap(bs->cow_bitmap_addr, bs->cow_bitmap_size);
  172 + if (bs->cow_fd >= 0)
  173 + close(bs->cow_fd);
  174 + if (bs->fd >= 0)
  175 + close(bs->fd);
79 176 free(bs);
80 177 }
81 178  
  179 +static inline void set_bit(uint8_t *bitmap, int64_t bitnum)
  180 +{
  181 + bitmap[bitnum / 8] |= (1 << (bitnum%8));
  182 +}
  183 +
  184 +static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum)
  185 +{
  186 + return !!(bitmap[bitnum / 8] & (1 << (bitnum%8)));
  187 +}
  188 +
  189 +
  190 +/* Return true if first block has been changed (ie. current version is
  191 + * in COW file). Set the number of continuous blocks for which that
  192 + * is true. */
  193 +static int is_changed(uint8_t *bitmap,
  194 + int64_t sector_num, int nb_sectors,
  195 + int *num_same)
  196 +{
  197 + int changed;
  198 +
  199 + if (!bitmap || nb_sectors == 0) {
  200 + *num_same = nb_sectors;
  201 + return 0;
  202 + }
  203 +
  204 + changed = is_bit_set(bitmap, sector_num);
  205 + for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
  206 + if (is_bit_set(bitmap, sector_num + *num_same) != changed)
  207 + break;
  208 + }
  209 +
  210 + return changed;
  211 +}
  212 +
  213 +/* commit COW file into the raw image */
  214 +int bdrv_commit(BlockDriverState *bs)
  215 +{
  216 + int64_t i;
  217 + uint8_t *cow_bitmap;
  218 +
  219 + if (!bs->cow_bitmap) {
  220 + fprintf(stderr, "Already committed to %s\n", bs->filename);
  221 + return 0;
  222 + }
  223 +
  224 + if (bs->read_only) {
  225 + fprintf(stderr, "Can't commit to %s: read-only\n", bs->filename);
  226 + return -1;
  227 + }
  228 +
  229 + cow_bitmap = bs->cow_bitmap;
  230 + for (i = 0; i < bs->total_sectors; i++) {
  231 + if (is_bit_set(cow_bitmap, i)) {
  232 + unsigned char sector[512];
  233 + if (bdrv_read(bs, i, sector, 1) != 0) {
  234 + fprintf(stderr, "Error reading sector %lli: aborting commit\n",
  235 + (long long)i);
  236 + return -1;
  237 + }
  238 +
  239 + /* Make bdrv_write write to real file for a moment. */
  240 + bs->cow_bitmap = NULL;
  241 + if (bdrv_write(bs, i, sector, 1) != 0) {
  242 + fprintf(stderr, "Error writing sector %lli: aborting commit\n",
  243 + (long long)i);
  244 + bs->cow_bitmap = cow_bitmap;
  245 + return -1;
  246 + }
  247 + bs->cow_bitmap = cow_bitmap;
  248 + }
  249 + }
  250 + fprintf(stderr, "Committed snapshot to %s\n", bs->filename);
  251 + return 0;
  252 +}
  253 +
82 254 /* return -1 if error */
83 255 int bdrv_read(BlockDriverState *bs, int64_t sector_num,
84 256 uint8_t *buf, int nb_sectors)
85 257 {
86   - int ret;
  258 + int ret, n, fd;
  259 + int64_t offset;
  260 +
  261 + while (nb_sectors > 0) {
  262 + if (is_changed(bs->cow_bitmap, sector_num, nb_sectors, &n)) {
  263 + fd = bs->cow_fd;
  264 + offset = bs->cow_sectors_offset;
  265 + } else {
  266 + fd = bs->fd;
  267 + offset = 0;
  268 + }
87 269  
88   - lseek64(bs->fd, sector_num * 512, SEEK_SET);
89   - ret = read(bs->fd, buf, nb_sectors * 512);
90   - if (ret != nb_sectors * 512)
91   - return -1;
92   - else
93   - return 0;
  270 + if (fd < 0) {
  271 + /* no file, just return empty sectors */
  272 + memset(buf, 0, n * 512);
  273 + } else {
  274 + offset += sector_num * 512;
  275 + lseek64(fd, offset, SEEK_SET);
  276 + ret = read(fd, buf, n * 512);
  277 + if (ret != n * 512) {
  278 + return -1;
  279 + }
  280 + }
  281 + nb_sectors -= n;
  282 + sector_num += n;
  283 + buf += n * 512;
  284 + }
  285 + return 0;
94 286 }
95 287  
96 288 /* return -1 if error */
97 289 int bdrv_write(BlockDriverState *bs, int64_t sector_num,
98 290 const uint8_t *buf, int nb_sectors)
99 291 {
100   - int ret;
  292 + int ret, fd, i;
  293 + int64_t offset, retl;
101 294  
102 295 if (bs->read_only)
103 296 return -1;
104 297  
105   - lseek64(bs->fd, sector_num * 512, SEEK_SET);
106   - ret = write(bs->fd, buf, nb_sectors * 512);
107   - if (ret != nb_sectors * 512)
  298 + if (bs->cow_bitmap) {
  299 + fd = bs->cow_fd;
  300 + offset = bs->cow_sectors_offset;
  301 + } else {
  302 + fd = bs->fd;
  303 + offset = 0;
  304 + }
  305 +
  306 + offset += sector_num * 512;
  307 + retl = lseek64(fd, offset, SEEK_SET);
  308 + if (retl == -1) {
108 309 return -1;
109   - else
110   - return 0;
  310 + }
  311 + ret = write(fd, buf, nb_sectors * 512);
  312 + if (ret != nb_sectors * 512) {
  313 + return -1;
  314 + }
  315 +
  316 + if (bs->cow_bitmap) {
  317 + for (i = 0; i < nb_sectors; i++)
  318 + set_bit(bs->cow_bitmap, sector_num + i);
  319 + }
  320 + return 0;
111 321 }
112 322  
113 323 void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr)
... ...
... ... @@ -63,6 +63,8 @@
63 63 #define INITRD_LOAD_ADDR 0x00400000
64 64 #define KERNEL_PARAMS_ADDR 0x00090000
65 65  
  66 +#define MAX_DISKS 2
  67 +
66 68 /* from plex86 (BSD license) */
67 69 struct __attribute__ ((packed)) linux_params {
68 70 // For 0x00..0x3f, see 'struct screen_info' in linux/include/linux/tty.h.
... ... @@ -190,6 +192,7 @@ FILE *logfile = NULL;
190 192 int loglevel;
191 193 IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS];
192 194 IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS];
  195 +BlockDriverState *bs_table[MAX_DISKS];
193 196  
194 197 /***********************************************************/
195 198 /* x86 io ports */
... ... @@ -1265,6 +1268,7 @@ void term_print_help(void)
1265 1268 printf("\n"
1266 1269 "C-a h print this help\n"
1267 1270 "C-a x exit emulatior\n"
  1271 + "C-a s save disk data back to file (if -snapshot)\n"
1268 1272 "C-a b send break (magic sysrq)\n"
1269 1273 "C-a C-a send C-a\n"
1270 1274 );
... ... @@ -1282,6 +1286,15 @@ void serial_received_byte(SerialState *s, int ch)
1282 1286 case 'x':
1283 1287 exit(0);
1284 1288 break;
  1289 + case 's':
  1290 + {
  1291 + int i;
  1292 + for (i = 0; i < MAX_DISKS; i++) {
  1293 + if (bs_table[i])
  1294 + bdrv_commit(bs_table[i]);
  1295 + }
  1296 + }
  1297 + break;
1285 1298 case 'b':
1286 1299 /* send break */
1287 1300 s->rbr = 0;
... ... @@ -1976,8 +1989,6 @@ void ne2000_init(void)
1976 1989 /* set to 1 set disable mult support */
1977 1990 #define MAX_MULT_SECTORS 8
1978 1991  
1979   -#define MAX_DISKS 2
1980   -
1981 1992 struct IDEState;
1982 1993  
1983 1994 typedef void EndTransferFunc(struct IDEState *);
... ... @@ -2009,7 +2020,6 @@ typedef struct IDEState {
2009 2020 uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
2010 2021 } IDEState;
2011 2022  
2012   -BlockDriverState *bs_table[MAX_DISKS];
2013 2023 IDEState ide_state[MAX_DISKS];
2014 2024  
2015 2025 static void padstr(char *str, const char *src, int len)
... ... @@ -2513,6 +2523,16 @@ static void host_alarm_handler(int host_signum, siginfo_t *info,
2513 2523 }
2514 2524 }
2515 2525  
  2526 +unsigned long mmap_addr = PHYS_RAM_BASE;
  2527 +
  2528 +void *get_mmap_addr(unsigned long size)
  2529 +{
  2530 + unsigned long addr;
  2531 + addr = mmap_addr;
  2532 + mmap_addr += ((size + 4095) & ~4095) + 4096;
  2533 + return (void *)addr;
  2534 +}
  2535 +
2516 2536 /* main execution loop */
2517 2537  
2518 2538 CPUState *cpu_gdbstub_get_env(void *opaque)
... ... @@ -2612,6 +2632,7 @@ void help(void)
2612 2632 "-initrd file use 'file' as initial ram disk\n"
2613 2633 "-hda file use 'file' as hard disk 0 image\n"
2614 2634 "-hdb file use 'file' as hard disk 1 image\n"
  2635 + "-snapshot write to temporary files instead of disk image files\n"
2615 2636 "-m megs set virtual RAM size to megs MB\n"
2616 2637 "-n script set network init script [default=%s]\n"
2617 2638 "\n"
... ... @@ -2630,12 +2651,14 @@ struct option long_options[] = {
2630 2651 { "initrd", 1, NULL, 0, },
2631 2652 { "hda", 1, NULL, 0, },
2632 2653 { "hdb", 1, NULL, 0, },
  2654 + { "snapshot", 0, NULL, 0, },
2633 2655 { NULL, 0, NULL, 0 },
2634 2656 };
2635 2657  
2636 2658 int main(int argc, char **argv)
2637 2659 {
2638 2660 int c, ret, initrd_size, i, use_gdbstub, gdbstub_port, long_index;
  2661 + int snapshot;
2639 2662 struct linux_params *params;
2640 2663 struct sigaction act;
2641 2664 struct itimerval itv;
... ... @@ -2652,6 +2675,7 @@ int main(int argc, char **argv)
2652 2675 pstrcpy(network_script, sizeof(network_script), DEFAULT_NETWORK_SCRIPT);
2653 2676 use_gdbstub = 0;
2654 2677 gdbstub_port = DEFAULT_GDBSTUB_PORT;
  2678 + snapshot = 0;
2655 2679 for(;;) {
2656 2680 c = getopt_long_only(argc, argv, "hm:dn:sp:", long_options, &long_index);
2657 2681 if (c == -1)
... ... @@ -2668,6 +2692,9 @@ int main(int argc, char **argv)
2668 2692 case 2:
2669 2693 hd_filename[1] = optarg;
2670 2694 break;
  2695 + case 3:
  2696 + snapshot = 1;
  2697 + break;
2671 2698 }
2672 2699 break;
2673 2700 case 'h':
... ... @@ -2711,18 +2738,6 @@ int main(int argc, char **argv)
2711 2738 setvbuf(logfile, NULL, _IOLBF, 0);
2712 2739 }
2713 2740  
2714   - /* open the virtual block devices */
2715   - for(i = 0; i < MAX_DISKS; i++) {
2716   - if (hd_filename[i]) {
2717   - bs_table[i] = bdrv_open(hd_filename[i]);
2718   - if (!bs_table[i]) {
2719   - fprintf(stderr, "vl: could not open hard disk image '%s\n",
2720   - hd_filename[i]);
2721   - exit(1);
2722   - }
2723   - }
2724   - }
2725   -
2726 2741 /* init network tun interface */
2727 2742 net_init();
2728 2743  
... ... @@ -2744,7 +2759,7 @@ int main(int argc, char **argv)
2744 2759 }
2745 2760 ftruncate(phys_ram_fd, phys_ram_size);
2746 2761 unlink(phys_ram_file);
2747   - phys_ram_base = mmap((void *)PHYS_RAM_BASE, phys_ram_size,
  2762 + phys_ram_base = mmap(get_mmap_addr(phys_ram_size), phys_ram_size,
2748 2763 PROT_WRITE | PROT_READ, MAP_SHARED | MAP_FIXED,
2749 2764 phys_ram_fd, 0);
2750 2765 if (phys_ram_base == MAP_FAILED) {
... ... @@ -2752,6 +2767,18 @@ int main(int argc, char **argv)
2752 2767 exit(1);
2753 2768 }
2754 2769  
  2770 + /* open the virtual block devices */
  2771 + for(i = 0; i < MAX_DISKS; i++) {
  2772 + if (hd_filename[i]) {
  2773 + bs_table[i] = bdrv_open(hd_filename[i], snapshot);
  2774 + if (!bs_table[i]) {
  2775 + fprintf(stderr, "vl: could not open hard disk image '%s\n",
  2776 + hd_filename[i]);
  2777 + exit(1);
  2778 + }
  2779 + }
  2780 + }
  2781 +
2755 2782 /* now we can load the kernel */
2756 2783 ret = load_kernel(argv[optind], phys_ram_base + KERNEL_LOAD_ADDR);
2757 2784 if (ret < 0) {
... ...
... ... @@ -24,16 +24,32 @@
24 24 #ifndef VL_H
25 25 #define VL_H
26 26  
  27 +/* vl.c */
  28 +void *get_mmap_addr(unsigned long size);
  29 +
27 30 /* block.c */
28 31 typedef struct BlockDriverState BlockDriverState;
29 32  
30   -BlockDriverState *bdrv_open(const char *filename);
  33 +BlockDriverState *bdrv_open(const char *filename, int snapshot);
31 34 void bdrv_close(BlockDriverState *bs);
32 35 int bdrv_read(BlockDriverState *bs, int64_t sector_num,
33 36 uint8_t *buf, int nb_sectors);
34 37 int bdrv_write(BlockDriverState *bs, int64_t sector_num,
35 38 const uint8_t *buf, int nb_sectors);
36 39 void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr);
  40 +int bdrv_commit(BlockDriverState *bs);
  41 +
  42 +/* user mode linux compatible COW file */
  43 +#define COW_MAGIC 0x4f4f4f4d /* MOOO */
  44 +#define COW_VERSION 2
37 45  
  46 +struct cow_header_v2 {
  47 + uint32_t magic;
  48 + uint32_t long version;
  49 + char backing_file[1024];
  50 + int32_t mtime;
  51 + uint64_t size;
  52 + uint32_t sectorsize;
  53 +};
38 54  
39 55 #endif /* VL_H */
... ...
vlmkcow.c 0 → 100644
  1 +/*
  2 + * create a COW disk image
  3 + *
  4 + * Copyright (c) 2003 Fabrice Bellard
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +#include <stdlib.h>
  25 +#include <stdio.h>
  26 +#include <stdarg.h>
  27 +#include <string.h>
  28 +#include <getopt.h>
  29 +#include <inttypes.h>
  30 +#include <unistd.h>
  31 +#include <sys/mman.h>
  32 +#include <fcntl.h>
  33 +#include <signal.h>
  34 +#include <time.h>
  35 +#include <sys/time.h>
  36 +#include <malloc.h>
  37 +#include <termios.h>
  38 +#include <sys/poll.h>
  39 +#include <errno.h>
  40 +#include <sys/wait.h>
  41 +#include <netinet/in.h>
  42 +
  43 +#include "vl.h"
  44 +
  45 +#define NO_THUNK_TYPE_SIZE
  46 +#include "thunk.h"
  47 +
  48 +int cow_create(int cow_fd, const char *image_filename,
  49 + int64_t image_sectors)
  50 +{
  51 + struct cow_header_v2 cow_header;
  52 + int fd;
  53 + struct stat st;
  54 +
  55 + memset(&cow_header, 0, sizeof(cow_header));
  56 + cow_header.magic = htonl(COW_MAGIC);
  57 + cow_header.version = htonl(COW_VERSION);
  58 + if (image_filename) {
  59 + fd = open(image_filename, O_RDONLY);
  60 + if (fd < 0) {
  61 + perror(image_filename);
  62 + exit(1);
  63 + }
  64 + image_sectors = lseek64(fd, 0, SEEK_END);
  65 + if (fstat(fd, &st) != 0) {
  66 + close(fd);
  67 + return -1;
  68 + }
  69 + close(fd);
  70 + image_sectors /= 512;
  71 + cow_header.mtime = htonl(st.st_mtime);
  72 + realpath(image_filename, cow_header.backing_file);
  73 + }
  74 + cow_header.sectorsize = htonl(512);
  75 + cow_header.size = image_sectors * 512;
  76 +#ifndef WORDS_BIGENDIAN
  77 + cow_header.size = bswap64(cow_header.size);
  78 +#endif
  79 + write(cow_fd, &cow_header, sizeof(cow_header));
  80 + /* resize to include at least all the bitmap */
  81 + ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
  82 + lseek(cow_fd, 0, SEEK_SET);
  83 + return 0;
  84 +}
  85 +
  86 +void help(void)
  87 +{
  88 + printf("usage vlmkcow [-h] [-f disk_image] cow_image [cow_size]\n"
  89 + "Create a Copy On Write disk image from an optional raw disk image\n"
  90 + "\n"
  91 + "-f disk_image set the raw disk image name\n"
  92 + "cow_image the created cow_image\n"
  93 + "cow_size the create cow_image size in MB if no raw disk image is used\n"
  94 + "\n"
  95 + "Once the cow_image is created from a raw disk image, you must not modify the original raw disk image\n"
  96 + );
  97 + exit(1);
  98 +}
  99 +
  100 +int main(int argc, char **argv)
  101 +{
  102 + const char *image_filename, *cow_filename;
  103 + int cow_fd, c, nb_args;
  104 + int64_t image_size;
  105 +
  106 + image_filename = NULL;
  107 + image_size = 0;
  108 + for(;;) {
  109 + c = getopt(argc, argv, "hf:");
  110 + if (c == -1)
  111 + break;
  112 + switch(c) {
  113 + case 'h':
  114 + help();
  115 + break;
  116 + case 'f':
  117 + image_filename = optarg;
  118 + break;
  119 + }
  120 + }
  121 + if (!image_filename)
  122 + nb_args = 2;
  123 + else
  124 + nb_args = 1;
  125 + if (optind + nb_args != argc)
  126 + help();
  127 +
  128 + cow_filename = argv[optind];
  129 + if (nb_args == 2) {
  130 + image_size = (int64_t)atoi(argv[optind + 1]) * 2 * 1024;
  131 + }
  132 +
  133 + cow_fd = open(cow_filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
  134 + if (!cow_fd < 0)
  135 + return -1;
  136 + if (cow_create(cow_fd, image_filename, image_size) < 0) {
  137 + fprintf(stderr, "%s: error while formating\n", cow_filename);
  138 + exit(1);
  139 + }
  140 + close(cow_fd);
  141 + return 0;
  142 +}
... ...