xref: /openbmc/qemu/block.c (revision b338082b3f0f1831deec6fa68145ae01ae136398)
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