1fc01f7e7Sbellard /* 2fc01f7e7Sbellard * QEMU System Emulator block driver 3fc01f7e7Sbellard * 4fc01f7e7Sbellard * Copyright (c) 2003 Fabrice Bellard 5fc01f7e7Sbellard * 6fc01f7e7Sbellard * Permission is hereby granted, free of charge, to any person obtaining a copy 7fc01f7e7Sbellard * of this software and associated documentation files (the "Software"), to deal 8fc01f7e7Sbellard * in the Software without restriction, including without limitation the rights 9fc01f7e7Sbellard * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10fc01f7e7Sbellard * copies of the Software, and to permit persons to whom the Software is 11fc01f7e7Sbellard * furnished to do so, subject to the following conditions: 12fc01f7e7Sbellard * 13fc01f7e7Sbellard * The above copyright notice and this permission notice shall be included in 14fc01f7e7Sbellard * all copies or substantial portions of the Software. 15fc01f7e7Sbellard * 16fc01f7e7Sbellard * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17fc01f7e7Sbellard * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18fc01f7e7Sbellard * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19fc01f7e7Sbellard * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20fc01f7e7Sbellard * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21fc01f7e7Sbellard * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22fc01f7e7Sbellard * THE SOFTWARE. 23fc01f7e7Sbellard */ 24fc01f7e7Sbellard #include <stdlib.h> 25fc01f7e7Sbellard #include <stdio.h> 26fc01f7e7Sbellard #include <stdarg.h> 27fc01f7e7Sbellard #include <string.h> 28fc01f7e7Sbellard #include <getopt.h> 29fc01f7e7Sbellard #include <inttypes.h> 30fc01f7e7Sbellard #include <unistd.h> 31fc01f7e7Sbellard #include <sys/mman.h> 32fc01f7e7Sbellard #include <fcntl.h> 33fc01f7e7Sbellard #include <signal.h> 34fc01f7e7Sbellard #include <time.h> 35fc01f7e7Sbellard #include <sys/time.h> 36fc01f7e7Sbellard #include <malloc.h> 37fc01f7e7Sbellard #include <termios.h> 38fc01f7e7Sbellard #include <sys/poll.h> 39fc01f7e7Sbellard #include <errno.h> 40fc01f7e7Sbellard #include <sys/wait.h> 4133e3963eSbellard #include <netinet/in.h> 42fc01f7e7Sbellard 43fc01f7e7Sbellard #include "vl.h" 44fc01f7e7Sbellard 4533e3963eSbellard #define NO_THUNK_TYPE_SIZE 4633e3963eSbellard #include "thunk.h" 4733e3963eSbellard 4800af2b26Sbellard #include "cow.h" 4900af2b26Sbellard 50fc01f7e7Sbellard struct BlockDriverState { 5133e3963eSbellard int fd; /* if -1, only COW mappings */ 52fc01f7e7Sbellard int64_t total_sectors; 53*b338082bSbellard int read_only; /* if true, the media is read only */ 54*b338082bSbellard int inserted; /* if true, the media is present */ 55*b338082bSbellard int removable; /* if true, the media can be removed */ 56*b338082bSbellard int locked; /* if true, the media cannot temporarily be ejected */ 57*b338082bSbellard /* event callback when inserting/removing */ 58*b338082bSbellard void (*change_cb)(void *opaque); 59*b338082bSbellard void *change_opaque; 6033e3963eSbellard 6133e3963eSbellard uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */ 6233e3963eSbellard uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */ 6333e3963eSbellard int cow_bitmap_size; 6433e3963eSbellard int cow_fd; 6533e3963eSbellard int64_t cow_sectors_offset; 66cf98951bSbellard int boot_sector_enabled; 67cf98951bSbellard uint8_t boot_sector_data[512]; 68cf98951bSbellard 6933e3963eSbellard char filename[1024]; 70*b338082bSbellard 71*b338082bSbellard /* NOTE: the following infos are only hints for real hardware 72*b338082bSbellard drivers. They are not used by the block driver */ 73*b338082bSbellard int cyls, heads, secs; 74*b338082bSbellard int type; 75*b338082bSbellard char device_name[32]; 76*b338082bSbellard BlockDriverState *next; 77fc01f7e7Sbellard }; 78fc01f7e7Sbellard 79*b338082bSbellard static BlockDriverState *bdrv_first; 80*b338082bSbellard 81*b338082bSbellard /* create a new block device (by default it is empty) */ 82*b338082bSbellard BlockDriverState *bdrv_new(const char *device_name) 83fc01f7e7Sbellard { 84*b338082bSbellard BlockDriverState **pbs, *bs; 85*b338082bSbellard 86*b338082bSbellard bs = qemu_mallocz(sizeof(BlockDriverState)); 87*b338082bSbellard if(!bs) 88*b338082bSbellard return NULL; 89*b338082bSbellard pstrcpy(bs->device_name, sizeof(bs->device_name), device_name); 90*b338082bSbellard /* insert at the end */ 91*b338082bSbellard pbs = &bdrv_first; 92*b338082bSbellard while (*pbs != NULL) 93*b338082bSbellard pbs = &(*pbs)->next; 94*b338082bSbellard *pbs = bs; 95*b338082bSbellard return bs; 96*b338082bSbellard } 97*b338082bSbellard 98*b338082bSbellard int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot) 99*b338082bSbellard { 10033e3963eSbellard int fd, cow_fd; 101fc01f7e7Sbellard int64_t size; 10233e3963eSbellard char template[] = "/tmp/vl.XXXXXX"; 10333e3963eSbellard struct cow_header_v2 cow_header; 10433e3963eSbellard struct stat st; 105fc01f7e7Sbellard 1060849bf08Sbellard bs->read_only = 0; 10733e3963eSbellard bs->fd = -1; 10833e3963eSbellard bs->cow_fd = -1; 10933e3963eSbellard bs->cow_bitmap = NULL; 11033e3963eSbellard strcpy(bs->filename, filename); 11133e3963eSbellard 11233e3963eSbellard /* open standard HD image */ 11333e3963eSbellard fd = open(filename, O_RDWR | O_LARGEFILE); 114fc01f7e7Sbellard if (fd < 0) { 11533e3963eSbellard /* read only image on disk */ 11633e3963eSbellard fd = open(filename, O_RDONLY | O_LARGEFILE); 1170849bf08Sbellard if (fd < 0) { 11833e3963eSbellard perror(filename); 11933e3963eSbellard goto fail; 120fc01f7e7Sbellard } 12133e3963eSbellard if (!snapshot) 1220849bf08Sbellard bs->read_only = 1; 1230849bf08Sbellard } 12433e3963eSbellard bs->fd = fd; 12533e3963eSbellard 12633e3963eSbellard /* see if it is a cow image */ 12733e3963eSbellard if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) { 12833e3963eSbellard fprintf(stderr, "%s: could not read header\n", filename); 12933e3963eSbellard goto fail; 13033e3963eSbellard } 13133e3963eSbellard if (cow_header.magic == htonl(COW_MAGIC) && 13233e3963eSbellard cow_header.version == htonl(COW_VERSION)) { 13333e3963eSbellard /* cow image found */ 13433e3963eSbellard size = cow_header.size; 13533e3963eSbellard #ifndef WORDS_BIGENDIAN 13633e3963eSbellard size = bswap64(size); 13733e3963eSbellard #endif 13833e3963eSbellard bs->total_sectors = size / 512; 13933e3963eSbellard 14033e3963eSbellard bs->cow_fd = fd; 14133e3963eSbellard bs->fd = -1; 14233e3963eSbellard if (cow_header.backing_file[0] != '\0') { 14333e3963eSbellard if (stat(cow_header.backing_file, &st) != 0) { 14433e3963eSbellard fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file); 14533e3963eSbellard goto fail; 14633e3963eSbellard } 14733e3963eSbellard if (st.st_mtime != htonl(cow_header.mtime)) { 14833e3963eSbellard fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file); 14933e3963eSbellard goto fail; 15033e3963eSbellard } 15133e3963eSbellard fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE); 15233e3963eSbellard if (fd < 0) 15333e3963eSbellard goto fail; 15433e3963eSbellard bs->fd = fd; 15533e3963eSbellard } 15633e3963eSbellard /* mmap the bitmap */ 15733e3963eSbellard bs->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); 15833e3963eSbellard bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size), 15933e3963eSbellard bs->cow_bitmap_size, 16033e3963eSbellard PROT_READ | PROT_WRITE, 16133e3963eSbellard MAP_SHARED, bs->cow_fd, 0); 16233e3963eSbellard if (bs->cow_bitmap_addr == MAP_FAILED) 16333e3963eSbellard goto fail; 16433e3963eSbellard bs->cow_bitmap = bs->cow_bitmap_addr + sizeof(cow_header); 16533e3963eSbellard bs->cow_sectors_offset = (bs->cow_bitmap_size + 511) & ~511; 16633e3963eSbellard snapshot = 0; 16733e3963eSbellard } else { 16833e3963eSbellard /* standard raw image */ 169fc01f7e7Sbellard size = lseek64(fd, 0, SEEK_END); 170fc01f7e7Sbellard bs->total_sectors = size / 512; 171fc01f7e7Sbellard bs->fd = fd; 17233e3963eSbellard } 17333e3963eSbellard 17433e3963eSbellard if (snapshot) { 17533e3963eSbellard /* create a temporary COW file */ 17633e3963eSbellard cow_fd = mkstemp(template); 17733e3963eSbellard if (cow_fd < 0) 17833e3963eSbellard goto fail; 17933e3963eSbellard bs->cow_fd = cow_fd; 18033e3963eSbellard unlink(template); 18133e3963eSbellard 18233e3963eSbellard /* just need to allocate bitmap */ 18333e3963eSbellard bs->cow_bitmap_size = (bs->total_sectors + 7) >> 3; 18433e3963eSbellard bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size), 18533e3963eSbellard bs->cow_bitmap_size, 18633e3963eSbellard PROT_READ | PROT_WRITE, 18733e3963eSbellard MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 18833e3963eSbellard if (bs->cow_bitmap_addr == MAP_FAILED) 18933e3963eSbellard goto fail; 19033e3963eSbellard bs->cow_bitmap = bs->cow_bitmap_addr; 19133e3963eSbellard bs->cow_sectors_offset = 0; 19233e3963eSbellard } 19333e3963eSbellard 194*b338082bSbellard bs->inserted = 1; 195*b338082bSbellard 196*b338082bSbellard /* call the change callback */ 197*b338082bSbellard if (bs->change_cb) 198*b338082bSbellard bs->change_cb(bs->change_opaque); 199*b338082bSbellard 200*b338082bSbellard return 0; 20133e3963eSbellard fail: 20233e3963eSbellard bdrv_close(bs); 203*b338082bSbellard return -1; 204fc01f7e7Sbellard } 205fc01f7e7Sbellard 206fc01f7e7Sbellard void bdrv_close(BlockDriverState *bs) 207fc01f7e7Sbellard { 208*b338082bSbellard if (bs->inserted) { 20933e3963eSbellard /* we unmap the mapping so that it is written to the COW file */ 21033e3963eSbellard if (bs->cow_bitmap_addr) 21133e3963eSbellard munmap(bs->cow_bitmap_addr, bs->cow_bitmap_size); 21233e3963eSbellard if (bs->cow_fd >= 0) 21333e3963eSbellard close(bs->cow_fd); 21433e3963eSbellard if (bs->fd >= 0) 215fc01f7e7Sbellard close(bs->fd); 216*b338082bSbellard bs->inserted = 0; 217*b338082bSbellard 218*b338082bSbellard /* call the change callback */ 219*b338082bSbellard if (bs->change_cb) 220*b338082bSbellard bs->change_cb(bs->change_opaque); 221*b338082bSbellard } 222*b338082bSbellard } 223*b338082bSbellard 224*b338082bSbellard void bdrv_delete(BlockDriverState *bs) 225*b338082bSbellard { 226*b338082bSbellard bdrv_close(bs); 227*b338082bSbellard qemu_free(bs); 228fc01f7e7Sbellard } 229fc01f7e7Sbellard 23033e3963eSbellard static inline void set_bit(uint8_t *bitmap, int64_t bitnum) 23133e3963eSbellard { 23233e3963eSbellard bitmap[bitnum / 8] |= (1 << (bitnum%8)); 23333e3963eSbellard } 23433e3963eSbellard 23533e3963eSbellard static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum) 23633e3963eSbellard { 23733e3963eSbellard return !!(bitmap[bitnum / 8] & (1 << (bitnum%8))); 23833e3963eSbellard } 23933e3963eSbellard 24033e3963eSbellard 24133e3963eSbellard /* Return true if first block has been changed (ie. current version is 24233e3963eSbellard * in COW file). Set the number of continuous blocks for which that 24333e3963eSbellard * is true. */ 24433e3963eSbellard static int is_changed(uint8_t *bitmap, 24533e3963eSbellard int64_t sector_num, int nb_sectors, 24633e3963eSbellard int *num_same) 24733e3963eSbellard { 24833e3963eSbellard int changed; 24933e3963eSbellard 25033e3963eSbellard if (!bitmap || nb_sectors == 0) { 25133e3963eSbellard *num_same = nb_sectors; 25233e3963eSbellard return 0; 25333e3963eSbellard } 25433e3963eSbellard 25533e3963eSbellard changed = is_bit_set(bitmap, sector_num); 25633e3963eSbellard for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { 25733e3963eSbellard if (is_bit_set(bitmap, sector_num + *num_same) != changed) 25833e3963eSbellard break; 25933e3963eSbellard } 26033e3963eSbellard 26133e3963eSbellard return changed; 26233e3963eSbellard } 26333e3963eSbellard 26433e3963eSbellard /* commit COW file into the raw image */ 26533e3963eSbellard int bdrv_commit(BlockDriverState *bs) 26633e3963eSbellard { 26733e3963eSbellard int64_t i; 26833e3963eSbellard uint8_t *cow_bitmap; 26933e3963eSbellard 270*b338082bSbellard if (!bs->inserted) 271*b338082bSbellard return -1; 272*b338082bSbellard 27333e3963eSbellard if (!bs->cow_bitmap) { 27433e3963eSbellard fprintf(stderr, "Already committed to %s\n", bs->filename); 27533e3963eSbellard return 0; 27633e3963eSbellard } 27733e3963eSbellard 27833e3963eSbellard if (bs->read_only) { 27933e3963eSbellard fprintf(stderr, "Can't commit to %s: read-only\n", bs->filename); 28033e3963eSbellard return -1; 28133e3963eSbellard } 28233e3963eSbellard 28333e3963eSbellard cow_bitmap = bs->cow_bitmap; 28433e3963eSbellard for (i = 0; i < bs->total_sectors; i++) { 28533e3963eSbellard if (is_bit_set(cow_bitmap, i)) { 28633e3963eSbellard unsigned char sector[512]; 28733e3963eSbellard if (bdrv_read(bs, i, sector, 1) != 0) { 28833e3963eSbellard fprintf(stderr, "Error reading sector %lli: aborting commit\n", 28933e3963eSbellard (long long)i); 29033e3963eSbellard return -1; 29133e3963eSbellard } 29233e3963eSbellard 29333e3963eSbellard /* Make bdrv_write write to real file for a moment. */ 29433e3963eSbellard bs->cow_bitmap = NULL; 29533e3963eSbellard if (bdrv_write(bs, i, sector, 1) != 0) { 29633e3963eSbellard fprintf(stderr, "Error writing sector %lli: aborting commit\n", 29733e3963eSbellard (long long)i); 29833e3963eSbellard bs->cow_bitmap = cow_bitmap; 29933e3963eSbellard return -1; 30033e3963eSbellard } 30133e3963eSbellard bs->cow_bitmap = cow_bitmap; 30233e3963eSbellard } 30333e3963eSbellard } 30433e3963eSbellard fprintf(stderr, "Committed snapshot to %s\n", bs->filename); 30533e3963eSbellard return 0; 30633e3963eSbellard } 30733e3963eSbellard 308fc01f7e7Sbellard /* return -1 if error */ 309fc01f7e7Sbellard int bdrv_read(BlockDriverState *bs, int64_t sector_num, 310fc01f7e7Sbellard uint8_t *buf, int nb_sectors) 311fc01f7e7Sbellard { 31233e3963eSbellard int ret, n, fd; 31333e3963eSbellard int64_t offset; 314fc01f7e7Sbellard 315*b338082bSbellard if (!bs->inserted) 316*b338082bSbellard return -1; 317*b338082bSbellard 31833e3963eSbellard while (nb_sectors > 0) { 31933e3963eSbellard if (is_changed(bs->cow_bitmap, sector_num, nb_sectors, &n)) { 32033e3963eSbellard fd = bs->cow_fd; 32133e3963eSbellard offset = bs->cow_sectors_offset; 322cf98951bSbellard } else if (sector_num == 0 && bs->boot_sector_enabled) { 323cf98951bSbellard memcpy(buf, bs->boot_sector_data, 512); 324cf98951bSbellard n = 1; 325cf98951bSbellard goto next; 32633e3963eSbellard } else { 32733e3963eSbellard fd = bs->fd; 32833e3963eSbellard offset = 0; 32933e3963eSbellard } 33033e3963eSbellard 33133e3963eSbellard if (fd < 0) { 33233e3963eSbellard /* no file, just return empty sectors */ 33333e3963eSbellard memset(buf, 0, n * 512); 33433e3963eSbellard } else { 33533e3963eSbellard offset += sector_num * 512; 33633e3963eSbellard lseek64(fd, offset, SEEK_SET); 33733e3963eSbellard ret = read(fd, buf, n * 512); 33833e3963eSbellard if (ret != n * 512) { 339fc01f7e7Sbellard return -1; 34033e3963eSbellard } 34133e3963eSbellard } 342cf98951bSbellard next: 34333e3963eSbellard nb_sectors -= n; 34433e3963eSbellard sector_num += n; 34533e3963eSbellard buf += n * 512; 34633e3963eSbellard } 347fc01f7e7Sbellard return 0; 348fc01f7e7Sbellard } 349fc01f7e7Sbellard 350fc01f7e7Sbellard /* return -1 if error */ 351fc01f7e7Sbellard int bdrv_write(BlockDriverState *bs, int64_t sector_num, 352fc01f7e7Sbellard const uint8_t *buf, int nb_sectors) 353fc01f7e7Sbellard { 35433e3963eSbellard int ret, fd, i; 35533e3963eSbellard int64_t offset, retl; 356fc01f7e7Sbellard 357*b338082bSbellard if (!bs->inserted) 358*b338082bSbellard return -1; 3590849bf08Sbellard if (bs->read_only) 3600849bf08Sbellard return -1; 3610849bf08Sbellard 36233e3963eSbellard if (bs->cow_bitmap) { 36333e3963eSbellard fd = bs->cow_fd; 36433e3963eSbellard offset = bs->cow_sectors_offset; 36533e3963eSbellard } else { 36633e3963eSbellard fd = bs->fd; 36733e3963eSbellard offset = 0; 36833e3963eSbellard } 36933e3963eSbellard 37033e3963eSbellard offset += sector_num * 512; 37133e3963eSbellard retl = lseek64(fd, offset, SEEK_SET); 37233e3963eSbellard if (retl == -1) { 373fc01f7e7Sbellard return -1; 37433e3963eSbellard } 37533e3963eSbellard ret = write(fd, buf, nb_sectors * 512); 37633e3963eSbellard if (ret != nb_sectors * 512) { 37733e3963eSbellard return -1; 37833e3963eSbellard } 37933e3963eSbellard 38033e3963eSbellard if (bs->cow_bitmap) { 38133e3963eSbellard for (i = 0; i < nb_sectors; i++) 38233e3963eSbellard set_bit(bs->cow_bitmap, sector_num + i); 38333e3963eSbellard } 384fc01f7e7Sbellard return 0; 385fc01f7e7Sbellard } 386fc01f7e7Sbellard 387fc01f7e7Sbellard void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr) 388fc01f7e7Sbellard { 389fc01f7e7Sbellard *nb_sectors_ptr = bs->total_sectors; 390fc01f7e7Sbellard } 391cf98951bSbellard 392cf98951bSbellard /* force a given boot sector. */ 393cf98951bSbellard void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size) 394cf98951bSbellard { 395cf98951bSbellard bs->boot_sector_enabled = 1; 396cf98951bSbellard if (size > 512) 397cf98951bSbellard size = 512; 398cf98951bSbellard memcpy(bs->boot_sector_data, data, size); 399cf98951bSbellard memset(bs->boot_sector_data + size, 0, 512 - size); 400cf98951bSbellard } 401*b338082bSbellard 402*b338082bSbellard void bdrv_set_geometry_hint(BlockDriverState *bs, 403*b338082bSbellard int cyls, int heads, int secs) 404*b338082bSbellard { 405*b338082bSbellard bs->cyls = cyls; 406*b338082bSbellard bs->heads = heads; 407*b338082bSbellard bs->secs = secs; 408*b338082bSbellard } 409*b338082bSbellard 410*b338082bSbellard void bdrv_set_type_hint(BlockDriverState *bs, int type) 411*b338082bSbellard { 412*b338082bSbellard bs->type = type; 413*b338082bSbellard bs->removable = ((type == BDRV_TYPE_CDROM || 414*b338082bSbellard type == BDRV_TYPE_FLOPPY)); 415*b338082bSbellard } 416*b338082bSbellard 417*b338082bSbellard void bdrv_get_geometry_hint(BlockDriverState *bs, 418*b338082bSbellard int *pcyls, int *pheads, int *psecs) 419*b338082bSbellard { 420*b338082bSbellard *pcyls = bs->cyls; 421*b338082bSbellard *pheads = bs->heads; 422*b338082bSbellard *psecs = bs->secs; 423*b338082bSbellard } 424*b338082bSbellard 425*b338082bSbellard int bdrv_get_type_hint(BlockDriverState *bs) 426*b338082bSbellard { 427*b338082bSbellard return bs->type; 428*b338082bSbellard } 429*b338082bSbellard 430*b338082bSbellard int bdrv_is_removable(BlockDriverState *bs) 431*b338082bSbellard { 432*b338082bSbellard return bs->removable; 433*b338082bSbellard } 434*b338082bSbellard 435*b338082bSbellard int bdrv_is_read_only(BlockDriverState *bs) 436*b338082bSbellard { 437*b338082bSbellard return bs->read_only; 438*b338082bSbellard } 439*b338082bSbellard 440*b338082bSbellard int bdrv_is_inserted(BlockDriverState *bs) 441*b338082bSbellard { 442*b338082bSbellard return bs->inserted; 443*b338082bSbellard } 444*b338082bSbellard 445*b338082bSbellard int bdrv_is_locked(BlockDriverState *bs) 446*b338082bSbellard { 447*b338082bSbellard return bs->locked; 448*b338082bSbellard } 449*b338082bSbellard 450*b338082bSbellard void bdrv_set_locked(BlockDriverState *bs, int locked) 451*b338082bSbellard { 452*b338082bSbellard bs->locked = locked; 453*b338082bSbellard } 454*b338082bSbellard 455*b338082bSbellard void bdrv_set_change_cb(BlockDriverState *bs, 456*b338082bSbellard void (*change_cb)(void *opaque), void *opaque) 457*b338082bSbellard { 458*b338082bSbellard bs->change_cb = change_cb; 459*b338082bSbellard bs->change_opaque = opaque; 460*b338082bSbellard } 461*b338082bSbellard 462*b338082bSbellard BlockDriverState *bdrv_find(const char *name) 463*b338082bSbellard { 464*b338082bSbellard BlockDriverState *bs; 465*b338082bSbellard 466*b338082bSbellard for (bs = bdrv_first; bs != NULL; bs = bs->next) { 467*b338082bSbellard if (!strcmp(name, bs->device_name)) 468*b338082bSbellard return bs; 469*b338082bSbellard } 470*b338082bSbellard return NULL; 471*b338082bSbellard } 472*b338082bSbellard 473*b338082bSbellard void bdrv_info(void) 474*b338082bSbellard { 475*b338082bSbellard BlockDriverState *bs; 476*b338082bSbellard 477*b338082bSbellard for (bs = bdrv_first; bs != NULL; bs = bs->next) { 478*b338082bSbellard term_printf("%s:", bs->device_name); 479*b338082bSbellard term_printf(" type="); 480*b338082bSbellard switch(bs->type) { 481*b338082bSbellard case BDRV_TYPE_HD: 482*b338082bSbellard term_printf("hd"); 483*b338082bSbellard break; 484*b338082bSbellard case BDRV_TYPE_CDROM: 485*b338082bSbellard term_printf("cdrom"); 486*b338082bSbellard break; 487*b338082bSbellard case BDRV_TYPE_FLOPPY: 488*b338082bSbellard term_printf("floppy"); 489*b338082bSbellard break; 490*b338082bSbellard } 491*b338082bSbellard term_printf(" removable=%d", bs->removable); 492*b338082bSbellard if (bs->removable) { 493*b338082bSbellard term_printf(" locked=%d", bs->locked); 494*b338082bSbellard } 495*b338082bSbellard if (bs->inserted) { 496*b338082bSbellard term_printf(" file=%s", bs->filename); 497*b338082bSbellard term_printf(" ro=%d", bs->read_only); 498*b338082bSbellard } else { 499*b338082bSbellard term_printf(" [not inserted]"); 500*b338082bSbellard } 501*b338082bSbellard term_printf("\n"); 502*b338082bSbellard } 503*b338082bSbellard } 504