?? block.c
字號:
/* * 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 "qemu-common.h"#ifndef QEMU_IMG#include "console.h"#endif#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);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); }}static 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 _WIN32void get_tmp_filename(char *filename, int size){ char temp_dir[MAX_PATH]; GetTempPath(MAX_PATH, temp_dir); GetTempFileName(temp_dir, "qem", 0, filename);}#elsevoid 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 _WIN32static 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;}#endifstatic 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 | (flags & BDRV_O_DIRECT); 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) {
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -