/* * QEMU System Emulator block driver * * Copyright (c) 2003 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. */ #include "vl.h" #include "block_int.h" #ifdef _BSD #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/queue.h> #include <sys/disk.h> #endif #define SECTOR_BITS 9 #define SECTOR_SIZE (1 << SECTOR_BITS) typedef struct BlockDriverAIOCBSync { BlockDriverAIOCB common; QEMUBH *bh; int ret; } BlockDriverAIOCBSync; static BlockDriverAIOCB *bdrv_aio_read_em(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); static BlockDriverAIOCB *bdrv_aio_write_em(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); static void bdrv_aio_cancel_em(BlockDriverAIOCB *acb); static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); static BlockDriverState *bdrv_first; static BlockDriver *first_drv; int path_is_absolute(const char *path) { const char *p; #ifdef _WIN32 /* specific case for names like: "\\.\d:" */ if (*path == '/' || *path == '\\') return 1; #endif p = strchr(path, ':'); if (p) p++; else p = path; #ifdef _WIN32 return (*p == '/' || *p == '\\'); #else return (*p == '/'); #endif } /* if filename is absolute, just copy it to dest. Otherwise, build a path to it by considering it is relative to base_path. URL are supported. */ void path_combine(char *dest, int dest_size, const char *base_path, const char *filename) { const char *p, *p1; int len; if (dest_size <= 0) return; if (path_is_absolute(filename)) { pstrcpy(dest, dest_size, filename); } else { p = strchr(base_path, ':'); if (p) p++; else p = base_path; p1 = strrchr(base_path, '/'); #ifdef _WIN32 { const char *p2; p2 = strrchr(base_path, '\\'); if (!p1 || p2 > p1) p1 = p2; } #endif if (p1) p1++; else p1 = base_path; if (p1 > p) p = p1; len = p - base_path; if (len > dest_size - 1) len = dest_size - 1; memcpy(dest, base_path, len); dest[len] = '\0'; pstrcat(dest, dest_size, filename); } } void bdrv_register(BlockDriver *bdrv) { if (!bdrv->bdrv_aio_read) { /* add AIO emulation layer */ bdrv->bdrv_aio_read = bdrv_aio_read_em; bdrv->bdrv_aio_write = bdrv_aio_write_em; bdrv->bdrv_aio_cancel = bdrv_aio_cancel_em; bdrv->aiocb_size = sizeof(BlockDriverAIOCBSync); } else if (!bdrv->bdrv_read && !bdrv->bdrv_pread) { /* add synchronous IO emulation layer */ bdrv->bdrv_read = bdrv_read_em; bdrv->bdrv_write = bdrv_write_em; } bdrv->next = first_drv; first_drv = bdrv; } /* create a new block device (by default it is empty) */ BlockDriverState *bdrv_new(const char *device_name) { BlockDriverState **pbs, *bs; bs = qemu_mallocz(sizeof(BlockDriverState)); if(!bs) return NULL; pstrcpy(bs->device_name, sizeof(bs->device_name), device_name); if (device_name[0] != '\0') { /* insert at the end */ pbs = &bdrv_first; while (*pbs != NULL) pbs = &(*pbs)->next; *pbs = bs; } return bs; } BlockDriver *bdrv_find_format(const char *format_name) { BlockDriver *drv1; for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { if (!strcmp(drv1->format_name, format_name)) return drv1; } return NULL; } int bdrv_create(BlockDriver *drv, const char *filename, int64_t size_in_sectors, const char *backing_file, int flags) { if (!drv->bdrv_create) return -ENOTSUP; return drv->bdrv_create(filename, size_in_sectors, backing_file, flags); } #ifdef _WIN32 void get_tmp_filename(char *filename, int size) { char temp_dir[MAX_PATH]; GetTempPath(MAX_PATH, temp_dir); GetTempFileName(temp_dir, "qem", 0, filename); } #else void get_tmp_filename(char *filename, int size) { int fd; /* XXX: race condition possible */ pstrcpy(filename, size, "/tmp/vl.XXXXXX"); fd = mkstemp(filename); close(fd); } #endif #ifdef _WIN32 static int is_windows_drive_prefix(const char *filename) { return (((filename[0] >= 'a' && filename[0] <= 'z') || (filename[0] >= 'A' && filename[0] <= 'Z')) && filename[1] == ':'); } static int is_windows_drive(const char *filename) { if (is_windows_drive_prefix(filename) && filename[2] == '\0') return 1; if (strstart(filename, "\\\\.\\", NULL) || strstart(filename, "//./", NULL)) return 1; return 0; } #endif static BlockDriver *find_protocol(const char *filename) { BlockDriver *drv1; char protocol[128]; int len; const char *p; #ifdef _WIN32 if (is_windows_drive(filename) || is_windows_drive_prefix(filename)) return &bdrv_raw; #endif p = strchr(filename, ':'); if (!p) return &bdrv_raw; len = p - filename; if (len > sizeof(protocol) - 1) len = sizeof(protocol) - 1; memcpy(protocol, filename, len); protocol[len] = '\0'; for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { if (drv1->protocol_name && !strcmp(drv1->protocol_name, protocol)) return drv1; } return NULL; } /* XXX: force raw format if block or character device ? It would simplify the BSD case */ static BlockDriver *find_image_format(const char *filename) { int ret, score, score_max; BlockDriver *drv1, *drv; uint8_t buf[2048]; BlockDriverState *bs; /* detect host devices. By convention, /dev/cdrom[N] is always recognized as a host CDROM */ if (strstart(filename, "/dev/cdrom", NULL)) return &bdrv_host_device; #ifdef _WIN32 if (is_windows_drive(filename)) return &bdrv_host_device; #else { struct stat st; if (stat(filename, &st) >= 0 && (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { return &bdrv_host_device; } } #endif drv = find_protocol(filename); /* no need to test disk image formats for vvfat */ if (drv == &bdrv_vvfat) return drv; ret = bdrv_file_open(&bs, filename, BDRV_O_RDONLY); if (ret < 0) return NULL; ret = bdrv_pread(bs, 0, buf, sizeof(buf)); bdrv_delete(bs); if (ret < 0) { return NULL; } score_max = 0; for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { if (drv1->bdrv_probe) { score = drv1->bdrv_probe(buf, ret, filename); if (score > score_max) { score_max = score; drv = drv1; } } } return drv; } int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags) { BlockDriverState *bs; int ret; bs = bdrv_new(""); if (!bs) return -ENOMEM; ret = bdrv_open2(bs, filename, flags | BDRV_O_FILE, NULL); if (ret < 0) { bdrv_delete(bs); return ret; } *pbs = bs; return 0; } int bdrv_open(BlockDriverState *bs, const char *filename, int flags) { return bdrv_open2(bs, filename, flags, NULL); } int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, BlockDriver *drv) { int ret, open_flags; char tmp_filename[PATH_MAX]; char backing_filename[PATH_MAX]; bs->read_only = 0; bs->is_temporary = 0; bs->encrypted = 0; if (flags & BDRV_O_SNAPSHOT) { BlockDriverState *bs1; int64_t total_size; /* if snapshot, we create a temporary backing file and open it instead of opening 'filename' directly */ /* if there is a backing file, use it */ bs1 = bdrv_new(""); if (!bs1) { return -ENOMEM; } if (bdrv_open(bs1, filename, 0) < 0) { bdrv_delete(bs1); return -1; } total_size = bdrv_getlength(bs1) >> SECTOR_BITS; bdrv_delete(bs1); get_tmp_filename(tmp_filename, sizeof(tmp_filename)); realpath(filename, backing_filename); if (bdrv_create(&bdrv_qcow2, tmp_filename, total_size, backing_filename, 0) < 0) { return -1; } filename = tmp_filename; bs->is_temporary = 1; } pstrcpy(bs->filename, sizeof(bs->filename), filename); if (flags & BDRV_O_FILE) { drv = find_protocol(filename); if (!drv) return -ENOENT; } else { if (!drv) { drv = find_image_format(filename); if (!drv) return -1; } } bs->drv = drv; bs->opaque = qemu_mallocz(drv->instance_size); if (bs->opaque == NULL && drv->instance_size > 0) return -1; /* Note: for compatibility, we open disk image files as RDWR, and RDONLY as fallback */ if (!(flags & BDRV_O_FILE)) open_flags = BDRV_O_RDWR; else open_flags = flags & ~(BDRV_O_FILE | BDRV_O_SNAPSHOT); ret = drv->bdrv_open(bs, filename, open_flags); if (ret == -EACCES && !(flags & BDRV_O_FILE)) { ret = drv->bdrv_open(bs, filename, BDRV_O_RDONLY); bs->read_only = 1; } if (ret < 0) { qemu_free(bs->opaque); bs->opaque = NULL; bs->drv = NULL; return ret; } if (drv->bdrv_getlength) { bs->total_sectors = bdrv_getlength(bs) >> SECTOR_BITS; } #ifndef _WIN32 if (bs->is_temporary) { unlink(filename); } #endif if (bs->backing_file[0] != '\0') { /* if there is a backing file, use it */ bs->backing_hd = bdrv_new(""); if (!bs->backing_hd) { fail: bdrv_close(bs); return -ENOMEM; } path_combine(backing_filename, sizeof(backing_filename), filename, bs->backing_file); if (bdrv_open(bs->backing_hd, backing_filename, 0) < 0) goto fail; } /* call the change callback */ bs->media_changed = 1; if (bs->change_cb) bs->change_cb(bs->change_opaque); return 0; } void bdrv_close(BlockDriverState *bs) { if (bs->drv) { if (bs->backing_hd) bdrv_delete(bs->backing_hd); bs->drv->bdrv_close(bs); qemu_free(bs->opaque); #ifdef _WIN32 if (bs->is_temporary) { unlink(bs->filename); } #endif bs->opaque = NULL; bs->drv = NULL; /* call the change callback */ bs->media_changed = 1; if (bs->change_cb) bs->change_cb(bs->change_opaque); } } void bdrv_delete(BlockDriverState *bs) { /* XXX: remove the driver list */ bdrv_close(bs); qemu_free(bs); } /* commit COW file into the raw image */ int bdrv_commit(BlockDriverState *bs) { BlockDriver *drv = bs->drv; int64_t i, total_sectors; int n, j; unsigned char sector[512]; if (!drv) return -ENOMEDIUM; if (bs->read_only) { return -EACCES; } if (!bs->backing_hd) { return -ENOTSUP; } total_sectors = bdrv_getlength(bs) >> SECTOR_BITS; for (i = 0; i < total_sectors;) { if (drv->bdrv_is_allocated(bs, i, 65536, &n)) { for(j = 0; j < n; j++) { if (bdrv_read(bs, i, sector, 1) != 0) { return -EIO; } if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) { return -EIO; } i++; } } else { i += n; } } if (drv->bdrv_make_empty) return drv->bdrv_make_empty(bs); return 0; } /* return < 0 if error. See bdrv_write() for the return codes */ int bdrv_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { memcpy(buf, bs->boot_sector_data, 512); sector_num++; nb_sectors--; buf += 512; if (nb_sectors == 0) return 0; } if (drv->bdrv_pread) { int ret, len; len = nb_sectors * 512; ret = drv->bdrv_pread(bs, sector_num * 512, buf, len); if (ret < 0) return ret; else if (ret != len) return -EINVAL; else return 0; } else { return drv->bdrv_read(bs, sector_num, buf, nb_sectors); } } /* Return < 0 if error. Important errors are: -EIO generic I/O error (may happen for all errors) -ENOMEDIUM No media inserted. -EINVAL Invalid sector number or nb_sectors -EACCES Trying to write a read-only device */ int bdrv_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { BlockDriver *drv = bs->drv; if (!bs->drv) return -ENOMEDIUM; if (bs->read_only) return -EACCES; if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { memcpy(bs->boot_sector_data, buf, 512); } if (drv->bdrv_pwrite) { int ret, len; len = nb_sectors * 512; ret = drv->bdrv_pwrite(bs, sector_num * 512, buf, len); if (ret < 0) return ret; else if (ret != len) return -EIO; else return 0; } else { return drv->bdrv_write(bs, sector_num, buf, nb_sectors); } } static int bdrv_pread_em(BlockDriverState *bs, int64_t offset, uint8_t *buf, int count1) { uint8_t tmp_buf[SECTOR_SIZE]; int len, nb_sectors, count; int64_t sector_num; count = count1; /* first read to align to sector start */ len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1); if (len > count) len = count; sector_num = offset >> SECTOR_BITS; if (len > 0) { if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) return -EIO; memcpy(buf, tmp_buf + (offset & (SECTOR_SIZE - 1)), len); count -= len; if (count == 0) return count1; sector_num++; buf += len; } /* read the sectors "in place" */ nb_sectors = count >> SECTOR_BITS; if (nb_sectors > 0) { if (bdrv_read(bs, sector_num, buf, nb_sectors) < 0) return -EIO; sector_num += nb_sectors; len = nb_sectors << SECTOR_BITS; buf += len; count -= len; } /* add data from the last sector */ if (count > 0) { if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) return -EIO; memcpy(buf, tmp_buf, count); } return count1; } static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset, const uint8_t *buf, int count1) { uint8_t tmp_buf[SECTOR_SIZE]; int len, nb_sectors, count; int64_t sector_num; count = count1; /* first write to align to sector start */ len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1); if (len > count) len = count; sector_num = offset >> SECTOR_BITS; if (len > 0) { if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) return -EIO; memcpy(tmp_buf + (offset & (SECTOR_SIZE - 1)), buf, len); if (bdrv_write(bs, sector_num, tmp_buf, 1) < 0) return -EIO; count -= len; if (count == 0) return count1; sector_num++; buf += len; } /* write the sectors "in place" */ nb_sectors = count >> SECTOR_BITS; if (nb_sectors > 0) { if (bdrv_write(bs, sector_num, buf, nb_sectors) < 0) return -EIO; sector_num += nb_sectors; len = nb_sectors << SECTOR_BITS; buf += len; count -= len; } /* add data from the last sector */ if (count > 0) { if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) return -EIO; memcpy(tmp_buf, buf, count); if (bdrv_write(bs, sector_num, tmp_buf, 1) < 0) return -EIO; } return count1; } /** * Read with byte offsets (needed only for file protocols) */ int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf1, int count1) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_pread) return bdrv_pread_em(bs, offset, buf1, count1); return drv->bdrv_pread(bs, offset, buf1, count1); } /** * Write with byte offsets (needed only for file protocols) */ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, const void *buf1, int count1) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_pwrite) return bdrv_pwrite_em(bs, offset, buf1, count1); return drv->bdrv_pwrite(bs, offset, buf1, count1); } /** * Truncate file to 'offset' bytes (needed only for file protocols) */ int bdrv_truncate(BlockDriverState *bs, int64_t offset) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_truncate) return -ENOTSUP; return drv->bdrv_truncate(bs, offset); } /** * Length of a file in bytes. Return < 0 if error or unknown. */ int64_t bdrv_getlength(BlockDriverState *bs) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_getlength) { /* legacy mode */ return bs->total_sectors * SECTOR_SIZE; } return drv->bdrv_getlength(bs); } /* return 0 as number of sectors if no device present or error */ void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr) { int64_t length; length = bdrv_getlength(bs); if (length < 0) length = 0; else length = length >> SECTOR_BITS; *nb_sectors_ptr = length; } /* force a given boot sector. */ void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size) { bs->boot_sector_enabled = 1; if (size > 512) size = 512; memcpy(bs->boot_sector_data, data, size); memset(bs->boot_sector_data + size, 0, 512 - size); } void bdrv_set_geometry_hint(BlockDriverState *bs, int cyls, int heads, int secs) { bs->cyls = cyls; bs->heads = heads; bs->secs = secs; } void bdrv_set_type_hint(BlockDriverState *bs, int type) { bs->type = type; bs->removable = ((type == BDRV_TYPE_CDROM || type == BDRV_TYPE_FLOPPY)); } void bdrv_set_translation_hint(BlockDriverState *bs, int translation) { bs->translation = translation; } void bdrv_get_geometry_hint(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs) { *pcyls = bs->cyls; *pheads = bs->heads; *psecs = bs->secs; } int bdrv_get_type_hint(BlockDriverState *bs) { return bs->type; } int bdrv_get_translation_hint(BlockDriverState *bs) { return bs->translation; } int bdrv_is_removable(BlockDriverState *bs) { return bs->removable; } int bdrv_is_read_only(BlockDriverState *bs) { return bs->read_only; } /* XXX: no longer used */ void bdrv_set_change_cb(BlockDriverState *bs, void (*change_cb)(void *opaque), void *opaque) { bs->change_cb = change_cb; bs->change_opaque = opaque; } int bdrv_is_encrypted(BlockDriverState *bs) { if (bs->backing_hd && bs->backing_hd->encrypted) return 1; return bs->encrypted; } int bdrv_set_key(BlockDriverState *bs, const char *key) { int ret; if (bs->backing_hd && bs->backing_hd->encrypted) { ret = bdrv_set_key(bs->backing_hd, key); if (ret < 0) return ret; if (!bs->encrypted) return 0; } if (!bs->encrypted || !bs->drv || !bs->drv->bdrv_set_key) return -1; return bs->drv->bdrv_set_key(bs, key); } void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size) { if (!bs->drv) { buf[0] = '\0'; } else { pstrcpy(buf, buf_size, bs->drv->format_name); } } void bdrv_iterate_format(void (*it)(void *opaque, const char *name), void *opaque) { BlockDriver *drv; for (drv = first_drv; drv != NULL; drv = drv->next) { it(opaque, drv->format_name); } } BlockDriverState *bdrv_find(const char *name) { BlockDriverState *bs; for (bs = bdrv_first; bs != NULL; bs = bs->next) { if (!strcmp(name, bs->device_name)) return bs; } return NULL; } void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque) { BlockDriverState *bs; for (bs = bdrv_first; bs != NULL; bs = bs->next) { it(opaque, bs->device_name); } } const char *bdrv_get_device_name(BlockDriverState *bs) { return bs->device_name; } void bdrv_flush(BlockDriverState *bs) { if (bs->drv->bdrv_flush) bs->drv->bdrv_flush(bs); if (bs->backing_hd) bdrv_flush(bs->backing_hd); } void bdrv_info(void) { BlockDriverState *bs; for (bs = bdrv_first; bs != NULL; bs = bs->next) { term_printf("%s:", bs->device_name); term_printf(" type="); switch(bs->type) { case BDRV_TYPE_HD: term_printf("hd"); break; case BDRV_TYPE_CDROM: term_printf("cdrom"); break; case BDRV_TYPE_FLOPPY: term_printf("floppy"); break; } term_printf(" removable=%d", bs->removable); if (bs->removable) { term_printf(" locked=%d", bs->locked); } if (bs->drv) { term_printf(" file="); term_print_filename(bs->filename); if (bs->backing_file[0] != '\0') { term_printf(" backing_file="); term_print_filename(bs->backing_file); } term_printf(" ro=%d", bs->read_only); term_printf(" drv=%s", bs->drv->format_name); if (bs->encrypted) term_printf(" encrypted"); } else { term_printf(" [not inserted]"); } term_printf("\n"); } } void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size) { if (!bs->backing_hd) { pstrcpy(filename, filename_size, ""); } else { pstrcpy(filename, filename_size, bs->backing_file); } } int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_write_compressed) return -ENOTSUP; return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors); } int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_get_info) return -ENOTSUP; memset(bdi, 0, sizeof(*bdi)); return drv->bdrv_get_info(bs, bdi); } /**************************************************************/ /* handling of snapshots */ int bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_snapshot_create) return -ENOTSUP; return drv->bdrv_snapshot_create(bs, sn_info); } int bdrv_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_snapshot_goto) return -ENOTSUP; return drv->bdrv_snapshot_goto(bs, snapshot_id); } int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_snapshot_delete) return -ENOTSUP; return drv->bdrv_snapshot_delete(bs, snapshot_id); } int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_snapshot_list) return -ENOTSUP; return drv->bdrv_snapshot_list(bs, psn_info); } #define NB_SUFFIXES 4 char *get_human_readable_size(char *buf, int buf_size, int64_t size) { static const char suffixes[NB_SUFFIXES] = "KMGT"; int64_t base; int i; if (size <= 999) { snprintf(buf, buf_size, "%" PRId64, size); } else { base = 1024; for(i = 0; i < NB_SUFFIXES; i++) { if (size < (10 * base)) { snprintf(buf, buf_size, "%0.1f%c", (double)size / base, suffixes[i]); break; } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { snprintf(buf, buf_size, "%" PRId64 "%c", ((size + (base >> 1)) / base), suffixes[i]); break; } base = base * 1024; } } return buf; } char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) { char buf1[128], date_buf[128], clock_buf[128]; #ifdef _WIN32 struct tm *ptm; #else struct tm tm; #endif time_t ti; int64_t secs; if (!sn) { snprintf(buf, buf_size, "%-10s%-20s%7s%20s%15s", "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); } else { ti = sn->date_sec; #ifdef _WIN32 ptm = localtime(&ti); strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", ptm); #else localtime_r(&ti, &tm); strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm); #endif secs = sn->vm_clock_nsec / 1000000000; snprintf(clock_buf, sizeof(clock_buf), "%02d:%02d:%02d.%03d", (int)(secs / 3600), (int)((secs / 60) % 60), (int)(secs % 60), (int)((sn->vm_clock_nsec / 1000000) % 1000)); snprintf(buf, buf_size, "%-10s%-20s%7s%20s%15s", sn->id_str, sn->name, get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size), date_buf, clock_buf); } return buf; } /**************************************************************/ /* async I/Os */ BlockDriverAIOCB *bdrv_aio_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { BlockDriver *drv = bs->drv; if (!drv) return NULL; /* XXX: we assume that nb_sectors == 0 is suppored by the async read */ if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { memcpy(buf, bs->boot_sector_data, 512); sector_num++; nb_sectors--; buf += 512; } return drv->bdrv_aio_read(bs, sector_num, buf, nb_sectors, cb, opaque); } BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { BlockDriver *drv = bs->drv; if (!drv) return NULL; if (bs->read_only) return NULL; if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { memcpy(bs->boot_sector_data, buf, 512); } return drv->bdrv_aio_write(bs, sector_num, buf, nb_sectors, cb, opaque); } void bdrv_aio_cancel(BlockDriverAIOCB *acb) { BlockDriver *drv = acb->bs->drv; drv->bdrv_aio_cancel(acb); } /**************************************************************/ /* async block device emulation */ #ifdef QEMU_TOOL static BlockDriverAIOCB *bdrv_aio_read_em(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { int ret; ret = bdrv_read(bs, sector_num, buf, nb_sectors); cb(opaque, ret); return NULL; } static BlockDriverAIOCB *bdrv_aio_write_em(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { int ret; ret = bdrv_write(bs, sector_num, buf, nb_sectors); cb(opaque, ret); return NULL; } static void bdrv_aio_cancel_em(BlockDriverAIOCB *acb) { } #else static void bdrv_aio_bh_cb(void *opaque) { BlockDriverAIOCBSync *acb = opaque; acb->common.cb(acb->common.opaque, acb->ret); qemu_aio_release(acb); } static BlockDriverAIOCB *bdrv_aio_read_em(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { BlockDriverAIOCBSync *acb; int ret; acb = qemu_aio_get(bs, cb, opaque); if (!acb->bh) acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb); ret = bdrv_read(bs, sector_num, buf, nb_sectors); acb->ret = ret; qemu_bh_schedule(acb->bh); return &acb->common; } static BlockDriverAIOCB *bdrv_aio_write_em(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { BlockDriverAIOCBSync *acb; int ret; acb = qemu_aio_get(bs, cb, opaque); if (!acb->bh) acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb); ret = bdrv_write(bs, sector_num, buf, nb_sectors); acb->ret = ret; qemu_bh_schedule(acb->bh); return &acb->common; } static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb) { BlockDriverAIOCBSync *acb = (BlockDriverAIOCBSync *)blockacb; qemu_bh_cancel(acb->bh); qemu_aio_release(acb); } #endif /* !QEMU_TOOL */ /**************************************************************/ /* sync block device emulation */ static void bdrv_rw_em_cb(void *opaque, int ret) { *(int *)opaque = ret; } #define NOT_DONE 0x7fffffff static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { int async_ret; BlockDriverAIOCB *acb; async_ret = NOT_DONE; qemu_aio_wait_start(); acb = bdrv_aio_read(bs, sector_num, buf, nb_sectors, bdrv_rw_em_cb, &async_ret); if (acb == NULL) { qemu_aio_wait_end(); return -1; } while (async_ret == NOT_DONE) { qemu_aio_wait(); } qemu_aio_wait_end(); return async_ret; } static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { int async_ret; BlockDriverAIOCB *acb; async_ret = NOT_DONE; qemu_aio_wait_start(); acb = bdrv_aio_write(bs, sector_num, buf, nb_sectors, bdrv_rw_em_cb, &async_ret); if (acb == NULL) { qemu_aio_wait_end(); return -1; } while (async_ret == NOT_DONE) { qemu_aio_wait(); } qemu_aio_wait_end(); return async_ret; } void bdrv_init(void) { bdrv_register(&bdrv_raw); bdrv_register(&bdrv_host_device); #ifndef _WIN32 bdrv_register(&bdrv_cow); #endif bdrv_register(&bdrv_qcow); bdrv_register(&bdrv_vmdk); bdrv_register(&bdrv_cloop); bdrv_register(&bdrv_dmg); bdrv_register(&bdrv_bochs); bdrv_register(&bdrv_vpc); bdrv_register(&bdrv_vvfat); bdrv_register(&bdrv_qcow2); bdrv_register(&bdrv_parallels); } void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque) { BlockDriver *drv; BlockDriverAIOCB *acb; drv = bs->drv; if (drv->free_aiocb) { acb = drv->free_aiocb; drv->free_aiocb = acb->next; } else { acb = qemu_mallocz(drv->aiocb_size); if (!acb) return NULL; } acb->bs = bs; acb->cb = cb; acb->opaque = opaque; return acb; } void qemu_aio_release(void *p) { BlockDriverAIOCB *acb = p; BlockDriver *drv = acb->bs->drv; acb->next = drv->free_aiocb; drv->free_aiocb = acb; } /**************************************************************/ /* removable device support */ /** * Return TRUE if the media is present */ int bdrv_is_inserted(BlockDriverState *bs) { BlockDriver *drv = bs->drv; int ret; if (!drv) return 0; if (!drv->bdrv_is_inserted) return 1; ret = drv->bdrv_is_inserted(bs); return ret; } /** * Return TRUE if the media changed since the last call to this * function. It is currently only used for floppy disks */ int bdrv_media_changed(BlockDriverState *bs) { BlockDriver *drv = bs->drv; int ret; if (!drv || !drv->bdrv_media_changed) ret = -ENOTSUP; else ret = drv->bdrv_media_changed(bs); if (ret == -ENOTSUP) ret = bs->media_changed; bs->media_changed = 0; return ret; } /** * If eject_flag is TRUE, eject the media. Otherwise, close the tray */ void bdrv_eject(BlockDriverState *bs, int eject_flag) { BlockDriver *drv = bs->drv; int ret; if (!drv || !drv->bdrv_eject) { ret = -ENOTSUP; } else { ret = drv->bdrv_eject(bs, eject_flag); } if (ret == -ENOTSUP) { if (eject_flag) bdrv_close(bs); } } int bdrv_is_locked(BlockDriverState *bs) { return bs->locked; } /** * Lock or unlock the media (if it is locked, the user won't be able * to eject it manually). */ void bdrv_set_locked(BlockDriverState *bs, int locked) { BlockDriver *drv = bs->drv; bs->locked = locked; if (drv && drv->bdrv_set_locked) { drv->bdrv_set_locked(bs, locked); } }