xref: /openbmc/qemu/block.c (revision 67b915a5dd52a05f8030cd9edc005effd9c8eea5)
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 "vl.h"
25fc01f7e7Sbellard 
26*67b915a5Sbellard #ifndef _WIN32
27*67b915a5Sbellard #include <sys/mman.h>
28*67b915a5Sbellard #endif
2933e3963eSbellard 
3000af2b26Sbellard #include "cow.h"
3100af2b26Sbellard 
32fc01f7e7Sbellard struct BlockDriverState {
3333e3963eSbellard     int fd; /* if -1, only COW mappings */
34fc01f7e7Sbellard     int64_t total_sectors;
35b338082bSbellard     int read_only; /* if true, the media is read only */
36b338082bSbellard     int inserted; /* if true, the media is present */
37b338082bSbellard     int removable; /* if true, the media can be removed */
38b338082bSbellard     int locked;    /* if true, the media cannot temporarily be ejected */
39b338082bSbellard     /* event callback when inserting/removing */
40b338082bSbellard     void (*change_cb)(void *opaque);
41b338082bSbellard     void *change_opaque;
4233e3963eSbellard 
4333e3963eSbellard     uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */
4433e3963eSbellard     uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */
4533e3963eSbellard     int cow_bitmap_size;
4633e3963eSbellard     int cow_fd;
4733e3963eSbellard     int64_t cow_sectors_offset;
48cf98951bSbellard     int boot_sector_enabled;
49cf98951bSbellard     uint8_t boot_sector_data[512];
50cf98951bSbellard 
5133e3963eSbellard     char filename[1024];
52b338082bSbellard 
53b338082bSbellard     /* NOTE: the following infos are only hints for real hardware
54b338082bSbellard        drivers. They are not used by the block driver */
55b338082bSbellard     int cyls, heads, secs;
56b338082bSbellard     int type;
57b338082bSbellard     char device_name[32];
58b338082bSbellard     BlockDriverState *next;
59fc01f7e7Sbellard };
60fc01f7e7Sbellard 
61b338082bSbellard static BlockDriverState *bdrv_first;
62b338082bSbellard 
63b338082bSbellard /* create a new block device (by default it is empty) */
64b338082bSbellard BlockDriverState *bdrv_new(const char *device_name)
65fc01f7e7Sbellard {
66b338082bSbellard     BlockDriverState **pbs, *bs;
67b338082bSbellard 
68b338082bSbellard     bs = qemu_mallocz(sizeof(BlockDriverState));
69b338082bSbellard     if(!bs)
70b338082bSbellard         return NULL;
71b338082bSbellard     pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
72b338082bSbellard     /* insert at the end */
73b338082bSbellard     pbs = &bdrv_first;
74b338082bSbellard     while (*pbs != NULL)
75b338082bSbellard         pbs = &(*pbs)->next;
76b338082bSbellard     *pbs = bs;
77b338082bSbellard     return bs;
78b338082bSbellard }
79b338082bSbellard 
80b338082bSbellard int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot)
81b338082bSbellard {
82*67b915a5Sbellard     int fd;
83fc01f7e7Sbellard     int64_t size;
8433e3963eSbellard     struct cow_header_v2 cow_header;
85*67b915a5Sbellard #ifndef _WIN32
86*67b915a5Sbellard     char template[] = "/tmp/vl.XXXXXX";
87*67b915a5Sbellard     int cow_fd;
8833e3963eSbellard     struct stat st;
89*67b915a5Sbellard #endif
90fc01f7e7Sbellard 
910849bf08Sbellard     bs->read_only = 0;
9233e3963eSbellard     bs->fd = -1;
9333e3963eSbellard     bs->cow_fd = -1;
9433e3963eSbellard     bs->cow_bitmap = NULL;
9533e3963eSbellard     strcpy(bs->filename, filename);
9633e3963eSbellard 
9733e3963eSbellard     /* open standard HD image */
98*67b915a5Sbellard #ifdef _WIN32
99*67b915a5Sbellard     fd = open(filename, O_RDWR | O_BINARY);
100*67b915a5Sbellard #else
10133e3963eSbellard     fd = open(filename, O_RDWR | O_LARGEFILE);
102*67b915a5Sbellard #endif
103fc01f7e7Sbellard     if (fd < 0) {
10433e3963eSbellard         /* read only image on disk */
105*67b915a5Sbellard #ifdef _WIN32
106*67b915a5Sbellard         fd = open(filename, O_RDONLY | O_BINARY);
107*67b915a5Sbellard #else
10833e3963eSbellard         fd = open(filename, O_RDONLY | O_LARGEFILE);
109*67b915a5Sbellard #endif
1100849bf08Sbellard         if (fd < 0) {
11133e3963eSbellard             perror(filename);
11233e3963eSbellard             goto fail;
113fc01f7e7Sbellard         }
11433e3963eSbellard         if (!snapshot)
1150849bf08Sbellard             bs->read_only = 1;
1160849bf08Sbellard     }
11733e3963eSbellard     bs->fd = fd;
11833e3963eSbellard 
11933e3963eSbellard     /* see if it is a cow image */
12033e3963eSbellard     if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) {
12133e3963eSbellard         fprintf(stderr, "%s: could not read header\n", filename);
12233e3963eSbellard         goto fail;
12333e3963eSbellard     }
124*67b915a5Sbellard #ifndef _WIN32
125*67b915a5Sbellard     if (be32_to_cpu(cow_header.magic) == COW_MAGIC &&
126*67b915a5Sbellard         be32_to_cpu(cow_header.version) == COW_VERSION) {
12733e3963eSbellard         /* cow image found */
12833e3963eSbellard         size = cow_header.size;
12933e3963eSbellard #ifndef WORDS_BIGENDIAN
13033e3963eSbellard         size = bswap64(size);
13133e3963eSbellard #endif
13233e3963eSbellard         bs->total_sectors = size / 512;
13333e3963eSbellard 
13433e3963eSbellard         bs->cow_fd = fd;
13533e3963eSbellard         bs->fd = -1;
13633e3963eSbellard         if (cow_header.backing_file[0] != '\0') {
13733e3963eSbellard             if (stat(cow_header.backing_file, &st) != 0) {
13833e3963eSbellard                 fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file);
13933e3963eSbellard                 goto fail;
14033e3963eSbellard             }
141*67b915a5Sbellard             if (st.st_mtime != be32_to_cpu(cow_header.mtime)) {
14233e3963eSbellard                 fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file);
14333e3963eSbellard                 goto fail;
14433e3963eSbellard             }
14533e3963eSbellard             fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE);
14633e3963eSbellard             if (fd < 0)
14733e3963eSbellard                 goto fail;
14833e3963eSbellard             bs->fd = fd;
14933e3963eSbellard         }
15033e3963eSbellard         /* mmap the bitmap */
15133e3963eSbellard         bs->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
15233e3963eSbellard         bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size),
15333e3963eSbellard                                    bs->cow_bitmap_size,
15433e3963eSbellard                                    PROT_READ | PROT_WRITE,
15533e3963eSbellard                                    MAP_SHARED, bs->cow_fd, 0);
15633e3963eSbellard         if (bs->cow_bitmap_addr == MAP_FAILED)
15733e3963eSbellard             goto fail;
15833e3963eSbellard         bs->cow_bitmap = bs->cow_bitmap_addr + sizeof(cow_header);
15933e3963eSbellard         bs->cow_sectors_offset = (bs->cow_bitmap_size + 511) & ~511;
16033e3963eSbellard         snapshot = 0;
161*67b915a5Sbellard     } else
162*67b915a5Sbellard #endif
163*67b915a5Sbellard     {
16433e3963eSbellard         /* standard raw image */
165fc01f7e7Sbellard         size = lseek64(fd, 0, SEEK_END);
166fc01f7e7Sbellard         bs->total_sectors = size / 512;
167fc01f7e7Sbellard         bs->fd = fd;
16833e3963eSbellard     }
16933e3963eSbellard 
170*67b915a5Sbellard #ifndef _WIN32
17133e3963eSbellard     if (snapshot) {
17233e3963eSbellard         /* create a temporary COW file */
17333e3963eSbellard         cow_fd = mkstemp(template);
17433e3963eSbellard         if (cow_fd < 0)
17533e3963eSbellard             goto fail;
17633e3963eSbellard         bs->cow_fd = cow_fd;
17733e3963eSbellard 	unlink(template);
17833e3963eSbellard 
17933e3963eSbellard         /* just need to allocate bitmap */
18033e3963eSbellard         bs->cow_bitmap_size = (bs->total_sectors + 7) >> 3;
18133e3963eSbellard         bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size),
18233e3963eSbellard                                    bs->cow_bitmap_size,
18333e3963eSbellard                                    PROT_READ | PROT_WRITE,
18433e3963eSbellard                                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
18533e3963eSbellard         if (bs->cow_bitmap_addr == MAP_FAILED)
18633e3963eSbellard             goto fail;
18733e3963eSbellard         bs->cow_bitmap = bs->cow_bitmap_addr;
18833e3963eSbellard         bs->cow_sectors_offset = 0;
18933e3963eSbellard     }
190*67b915a5Sbellard #endif
19133e3963eSbellard 
192b338082bSbellard     bs->inserted = 1;
193b338082bSbellard 
194b338082bSbellard     /* call the change callback */
195b338082bSbellard     if (bs->change_cb)
196b338082bSbellard         bs->change_cb(bs->change_opaque);
197b338082bSbellard 
198b338082bSbellard     return 0;
19933e3963eSbellard  fail:
20033e3963eSbellard     bdrv_close(bs);
201b338082bSbellard     return -1;
202fc01f7e7Sbellard }
203fc01f7e7Sbellard 
204fc01f7e7Sbellard void bdrv_close(BlockDriverState *bs)
205fc01f7e7Sbellard {
206b338082bSbellard     if (bs->inserted) {
207*67b915a5Sbellard #ifndef _WIN32
20833e3963eSbellard         /* we unmap the mapping so that it is written to the COW file */
20933e3963eSbellard         if (bs->cow_bitmap_addr)
21033e3963eSbellard             munmap(bs->cow_bitmap_addr, bs->cow_bitmap_size);
211*67b915a5Sbellard #endif
21233e3963eSbellard         if (bs->cow_fd >= 0)
21333e3963eSbellard             close(bs->cow_fd);
21433e3963eSbellard         if (bs->fd >= 0)
215fc01f7e7Sbellard             close(bs->fd);
216b338082bSbellard         bs->inserted = 0;
217b338082bSbellard 
218b338082bSbellard         /* call the change callback */
219b338082bSbellard         if (bs->change_cb)
220b338082bSbellard             bs->change_cb(bs->change_opaque);
221b338082bSbellard     }
222b338082bSbellard }
223b338082bSbellard 
224b338082bSbellard void bdrv_delete(BlockDriverState *bs)
225b338082bSbellard {
226b338082bSbellard     bdrv_close(bs);
227b338082bSbellard     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 
270b338082bSbellard     if (!bs->inserted)
271b338082bSbellard         return -1;
272b338082bSbellard 
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 
315b338082bSbellard     if (!bs->inserted)
316b338082bSbellard         return -1;
317b338082bSbellard 
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 
357b338082bSbellard     if (!bs->inserted)
358b338082bSbellard         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 }
401b338082bSbellard 
402b338082bSbellard void bdrv_set_geometry_hint(BlockDriverState *bs,
403b338082bSbellard                             int cyls, int heads, int secs)
404b338082bSbellard {
405b338082bSbellard     bs->cyls = cyls;
406b338082bSbellard     bs->heads = heads;
407b338082bSbellard     bs->secs = secs;
408b338082bSbellard }
409b338082bSbellard 
410b338082bSbellard void bdrv_set_type_hint(BlockDriverState *bs, int type)
411b338082bSbellard {
412b338082bSbellard     bs->type = type;
413b338082bSbellard     bs->removable = ((type == BDRV_TYPE_CDROM ||
414b338082bSbellard                       type == BDRV_TYPE_FLOPPY));
415b338082bSbellard }
416b338082bSbellard 
417b338082bSbellard void bdrv_get_geometry_hint(BlockDriverState *bs,
418b338082bSbellard                             int *pcyls, int *pheads, int *psecs)
419b338082bSbellard {
420b338082bSbellard     *pcyls = bs->cyls;
421b338082bSbellard     *pheads = bs->heads;
422b338082bSbellard     *psecs = bs->secs;
423b338082bSbellard }
424b338082bSbellard 
425b338082bSbellard int bdrv_get_type_hint(BlockDriverState *bs)
426b338082bSbellard {
427b338082bSbellard     return bs->type;
428b338082bSbellard }
429b338082bSbellard 
430b338082bSbellard int bdrv_is_removable(BlockDriverState *bs)
431b338082bSbellard {
432b338082bSbellard     return bs->removable;
433b338082bSbellard }
434b338082bSbellard 
435b338082bSbellard int bdrv_is_read_only(BlockDriverState *bs)
436b338082bSbellard {
437b338082bSbellard     return bs->read_only;
438b338082bSbellard }
439b338082bSbellard 
440b338082bSbellard int bdrv_is_inserted(BlockDriverState *bs)
441b338082bSbellard {
442b338082bSbellard     return bs->inserted;
443b338082bSbellard }
444b338082bSbellard 
445b338082bSbellard int bdrv_is_locked(BlockDriverState *bs)
446b338082bSbellard {
447b338082bSbellard     return bs->locked;
448b338082bSbellard }
449b338082bSbellard 
450b338082bSbellard void bdrv_set_locked(BlockDriverState *bs, int locked)
451b338082bSbellard {
452b338082bSbellard     bs->locked = locked;
453b338082bSbellard }
454b338082bSbellard 
455b338082bSbellard void bdrv_set_change_cb(BlockDriverState *bs,
456b338082bSbellard                         void (*change_cb)(void *opaque), void *opaque)
457b338082bSbellard {
458b338082bSbellard     bs->change_cb = change_cb;
459b338082bSbellard     bs->change_opaque = opaque;
460b338082bSbellard }
461b338082bSbellard 
462b338082bSbellard BlockDriverState *bdrv_find(const char *name)
463b338082bSbellard {
464b338082bSbellard     BlockDriverState *bs;
465b338082bSbellard 
466b338082bSbellard     for (bs = bdrv_first; bs != NULL; bs = bs->next) {
467b338082bSbellard         if (!strcmp(name, bs->device_name))
468b338082bSbellard             return bs;
469b338082bSbellard     }
470b338082bSbellard     return NULL;
471b338082bSbellard }
472b338082bSbellard 
473b338082bSbellard void bdrv_info(void)
474b338082bSbellard {
475b338082bSbellard     BlockDriverState *bs;
476b338082bSbellard 
477b338082bSbellard     for (bs = bdrv_first; bs != NULL; bs = bs->next) {
478b338082bSbellard         term_printf("%s:", bs->device_name);
479b338082bSbellard         term_printf(" type=");
480b338082bSbellard         switch(bs->type) {
481b338082bSbellard         case BDRV_TYPE_HD:
482b338082bSbellard             term_printf("hd");
483b338082bSbellard             break;
484b338082bSbellard         case BDRV_TYPE_CDROM:
485b338082bSbellard             term_printf("cdrom");
486b338082bSbellard             break;
487b338082bSbellard         case BDRV_TYPE_FLOPPY:
488b338082bSbellard             term_printf("floppy");
489b338082bSbellard             break;
490b338082bSbellard         }
491b338082bSbellard         term_printf(" removable=%d", bs->removable);
492b338082bSbellard         if (bs->removable) {
493b338082bSbellard             term_printf(" locked=%d", bs->locked);
494b338082bSbellard         }
495b338082bSbellard         if (bs->inserted) {
496b338082bSbellard             term_printf(" file=%s", bs->filename);
497b338082bSbellard             term_printf(" ro=%d", bs->read_only);
498b338082bSbellard         } else {
499b338082bSbellard             term_printf(" [not inserted]");
500b338082bSbellard         }
501b338082bSbellard         term_printf("\n");
502b338082bSbellard     }
503b338082bSbellard }
504