|  | /* | 
|  | * Block driver for the COW format | 
|  | * | 
|  | * Copyright (c) 2004 Fabrice Bellard | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | * of this software and associated documentation files (the "Software"), to deal | 
|  | * in the Software without restriction, including without limitation the rights | 
|  | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | * copies of the Software, and to permit persons to whom the Software is | 
|  | * furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
|  | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
|  | * THE SOFTWARE. | 
|  | */ | 
|  | #ifndef _WIN32 | 
|  | #include "qemu-common.h" | 
|  | #include "block_int.h" | 
|  | #include "module.h" | 
|  | #include <sys/mman.h> | 
|  |  | 
|  | /**************************************************************/ | 
|  | /* COW block driver using file system holes */ | 
|  |  | 
|  | /* user mode linux compatible COW file */ | 
|  | #define COW_MAGIC 0x4f4f4f4d  /* MOOO */ | 
|  | #define COW_VERSION 2 | 
|  |  | 
|  | struct cow_header_v2 { | 
|  | uint32_t magic; | 
|  | uint32_t version; | 
|  | char backing_file[1024]; | 
|  | int32_t mtime; | 
|  | uint64_t size; | 
|  | uint32_t sectorsize; | 
|  | }; | 
|  |  | 
|  | typedef struct BDRVCowState { | 
|  | int fd; | 
|  | uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */ | 
|  | uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */ | 
|  | int cow_bitmap_size; | 
|  | int64_t cow_sectors_offset; | 
|  | } BDRVCowState; | 
|  |  | 
|  | static int cow_probe(const uint8_t *buf, int buf_size, const char *filename) | 
|  | { | 
|  | const struct cow_header_v2 *cow_header = (const void *)buf; | 
|  |  | 
|  | if (buf_size >= sizeof(struct cow_header_v2) && | 
|  | be32_to_cpu(cow_header->magic) == COW_MAGIC && | 
|  | be32_to_cpu(cow_header->version) == COW_VERSION) | 
|  | return 100; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int cow_open(BlockDriverState *bs, const char *filename, int flags) | 
|  | { | 
|  | BDRVCowState *s = bs->opaque; | 
|  | int fd; | 
|  | struct cow_header_v2 cow_header; | 
|  | int64_t size; | 
|  |  | 
|  | fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); | 
|  | if (fd < 0) { | 
|  | fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | 
|  | if (fd < 0) | 
|  | return -1; | 
|  | } | 
|  | s->fd = fd; | 
|  | /* see if it is a cow image */ | 
|  | if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) { | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | if (be32_to_cpu(cow_header.magic) != COW_MAGIC || | 
|  | be32_to_cpu(cow_header.version) != COW_VERSION) { | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* cow image found */ | 
|  | size = be64_to_cpu(cow_header.size); | 
|  | bs->total_sectors = size / 512; | 
|  |  | 
|  | pstrcpy(bs->backing_file, sizeof(bs->backing_file), | 
|  | cow_header.backing_file); | 
|  |  | 
|  | /* mmap the bitmap */ | 
|  | s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); | 
|  | s->cow_bitmap_addr = (void *)mmap(get_mmap_addr(s->cow_bitmap_size), | 
|  | s->cow_bitmap_size, | 
|  | PROT_READ | PROT_WRITE, | 
|  | MAP_SHARED, s->fd, 0); | 
|  | if (s->cow_bitmap_addr == MAP_FAILED) | 
|  | goto fail; | 
|  | s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header); | 
|  | s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511; | 
|  | return 0; | 
|  | fail: | 
|  | close(fd); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum) | 
|  | { | 
|  | bitmap[bitnum / 8] |= (1 << (bitnum%8)); | 
|  | } | 
|  |  | 
|  | static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum) | 
|  | { | 
|  | return !!(bitmap[bitnum / 8] & (1 << (bitnum%8))); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Return true if first block has been changed (ie. current version is | 
|  | * in COW file).  Set the number of continuous blocks for which that | 
|  | * is true. */ | 
|  | static inline int is_changed(uint8_t *bitmap, | 
|  | int64_t sector_num, int nb_sectors, | 
|  | int *num_same) | 
|  | { | 
|  | int changed; | 
|  |  | 
|  | if (!bitmap || nb_sectors == 0) { | 
|  | *num_same = nb_sectors; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | changed = is_bit_set(bitmap, sector_num); | 
|  | for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { | 
|  | if (is_bit_set(bitmap, sector_num + *num_same) != changed) | 
|  | break; | 
|  | } | 
|  |  | 
|  | return changed; | 
|  | } | 
|  |  | 
|  | static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, | 
|  | int nb_sectors, int *pnum) | 
|  | { | 
|  | BDRVCowState *s = bs->opaque; | 
|  | return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum); | 
|  | } | 
|  |  | 
|  | static int cow_read(BlockDriverState *bs, int64_t sector_num, | 
|  | uint8_t *buf, int nb_sectors) | 
|  | { | 
|  | BDRVCowState *s = bs->opaque; | 
|  | int ret, n; | 
|  |  | 
|  | while (nb_sectors > 0) { | 
|  | if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) { | 
|  | lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); | 
|  | ret = read(s->fd, buf, n * 512); | 
|  | if (ret != n * 512) | 
|  | return -1; | 
|  | } else { | 
|  | if (bs->backing_hd) { | 
|  | /* read from the base image */ | 
|  | ret = bdrv_read(bs->backing_hd, sector_num, buf, n); | 
|  | if (ret < 0) | 
|  | return -1; | 
|  | } else { | 
|  | memset(buf, 0, n * 512); | 
|  | } | 
|  | } | 
|  | nb_sectors -= n; | 
|  | sector_num += n; | 
|  | buf += n * 512; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int cow_write(BlockDriverState *bs, int64_t sector_num, | 
|  | const uint8_t *buf, int nb_sectors) | 
|  | { | 
|  | BDRVCowState *s = bs->opaque; | 
|  | int ret, i; | 
|  |  | 
|  | lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); | 
|  | ret = write(s->fd, buf, nb_sectors * 512); | 
|  | if (ret != nb_sectors * 512) | 
|  | return -1; | 
|  | for (i = 0; i < nb_sectors; i++) | 
|  | cow_set_bit(s->cow_bitmap, sector_num + i); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void cow_close(BlockDriverState *bs) | 
|  | { | 
|  | BDRVCowState *s = bs->opaque; | 
|  | munmap((void *)s->cow_bitmap_addr, s->cow_bitmap_size); | 
|  | close(s->fd); | 
|  | } | 
|  |  | 
|  | static int cow_create(const char *filename, QEMUOptionParameter *options) | 
|  | { | 
|  | int fd, cow_fd; | 
|  | struct cow_header_v2 cow_header; | 
|  | struct stat st; | 
|  | int64_t image_sectors = 0; | 
|  | const char *image_filename = NULL; | 
|  | int ret; | 
|  |  | 
|  | /* Read out options */ | 
|  | while (options && options->name) { | 
|  | if (!strcmp(options->name, BLOCK_OPT_SIZE)) { | 
|  | image_sectors = options->value.n / 512; | 
|  | } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { | 
|  | image_filename = options->value.s; | 
|  | } | 
|  | options++; | 
|  | } | 
|  |  | 
|  | cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, | 
|  | 0644); | 
|  | if (cow_fd < 0) | 
|  | return -errno; | 
|  | memset(&cow_header, 0, sizeof(cow_header)); | 
|  | cow_header.magic = cpu_to_be32(COW_MAGIC); | 
|  | cow_header.version = cpu_to_be32(COW_VERSION); | 
|  | if (image_filename) { | 
|  | /* Note: if no file, we put a dummy mtime */ | 
|  | cow_header.mtime = cpu_to_be32(0); | 
|  |  | 
|  | fd = open(image_filename, O_RDONLY | O_BINARY); | 
|  | if (fd < 0) { | 
|  | close(cow_fd); | 
|  | goto mtime_fail; | 
|  | } | 
|  | if (fstat(fd, &st) != 0) { | 
|  | close(fd); | 
|  | goto mtime_fail; | 
|  | } | 
|  | close(fd); | 
|  | cow_header.mtime = cpu_to_be32(st.st_mtime); | 
|  | mtime_fail: | 
|  | pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file), | 
|  | image_filename); | 
|  | } | 
|  | cow_header.sectorsize = cpu_to_be32(512); | 
|  | cow_header.size = cpu_to_be64(image_sectors * 512); | 
|  | ret = qemu_write_full(cow_fd, &cow_header, sizeof(cow_header)); | 
|  | if (ret != sizeof(cow_header)) { | 
|  | ret = -errno; | 
|  | goto exit; | 
|  | } | 
|  |  | 
|  | /* resize to include at least all the bitmap */ | 
|  | ret = ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); | 
|  | if (ret) { | 
|  | ret = -errno; | 
|  | goto exit; | 
|  | } | 
|  |  | 
|  | exit: | 
|  | close(cow_fd); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void cow_flush(BlockDriverState *bs) | 
|  | { | 
|  | BDRVCowState *s = bs->opaque; | 
|  | qemu_fdatasync(s->fd); | 
|  | } | 
|  |  | 
|  | static QEMUOptionParameter cow_create_options[] = { | 
|  | { | 
|  | .name = BLOCK_OPT_SIZE, | 
|  | .type = OPT_SIZE, | 
|  | .help = "Virtual disk size" | 
|  | }, | 
|  | { | 
|  | .name = BLOCK_OPT_BACKING_FILE, | 
|  | .type = OPT_STRING, | 
|  | .help = "File name of a base image" | 
|  | }, | 
|  | { NULL } | 
|  | }; | 
|  |  | 
|  | static BlockDriver bdrv_cow = { | 
|  | .format_name	= "cow", | 
|  | .instance_size	= sizeof(BDRVCowState), | 
|  | .bdrv_probe		= cow_probe, | 
|  | .bdrv_file_open	= cow_open, | 
|  | .bdrv_read		= cow_read, | 
|  | .bdrv_write		= cow_write, | 
|  | .bdrv_close		= cow_close, | 
|  | .bdrv_create	= cow_create, | 
|  | .bdrv_flush		= cow_flush, | 
|  | .bdrv_is_allocated	= cow_is_allocated, | 
|  |  | 
|  | .create_options = cow_create_options, | 
|  | }; | 
|  |  | 
|  | static void bdrv_cow_init(void) | 
|  | { | 
|  | bdrv_register(&bdrv_cow); | 
|  | } | 
|  |  | 
|  | block_init(bdrv_cow_init); | 
|  | #endif |