block.c 13.4 KB
Newer Older
B
bellard 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * 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"

B
bellard 已提交
26 27 28
#ifndef _WIN32
#include <sys/mman.h>
#endif
29

B
bellard 已提交
30 31
#include "cow.h"

B
bellard 已提交
32
struct BlockDriverState {
33
    int fd; /* if -1, only COW mappings */
B
bellard 已提交
34
    int64_t total_sectors;
B
bellard 已提交
35 36 37 38 39 40 41
    int read_only; /* if true, the media is read only */
    int inserted; /* if true, the media is present */
    int removable; /* if true, the media can be removed */
    int locked;    /* if true, the media cannot temporarily be ejected */
    /* event callback when inserting/removing */
    void (*change_cb)(void *opaque);
    void *change_opaque;
42 43 44 45 46 47

    uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */
    uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */
    int cow_bitmap_size;
    int cow_fd;
    int64_t cow_sectors_offset;
B
bellard 已提交
48 49 50
    int boot_sector_enabled;
    uint8_t boot_sector_data[512];

51
    char filename[1024];
B
bellard 已提交
52 53 54 55 56 57 58
    
    /* NOTE: the following infos are only hints for real hardware
       drivers. They are not used by the block driver */
    int cyls, heads, secs;
    int type;
    char device_name[32];
    BlockDriverState *next;
B
bellard 已提交
59 60
};

B
bellard 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
static BlockDriverState *bdrv_first;

/* 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);
    /* insert at the end */
    pbs = &bdrv_first;
    while (*pbs != NULL)
        pbs = &(*pbs)->next;
    *pbs = bs;
    return bs;
}

int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot)
B
bellard 已提交
81
{
B
bellard 已提交
82
    int fd;
B
bellard 已提交
83
    int64_t size;
84
    struct cow_header_v2 cow_header;
B
bellard 已提交
85 86 87
#ifndef _WIN32
    char template[] = "/tmp/vl.XXXXXX";
    int cow_fd;
88
    struct stat st;
B
bellard 已提交
89
#endif
B
bellard 已提交
90

B
bellard 已提交
91
    bs->read_only = 0;
92 93 94
    bs->fd = -1;
    bs->cow_fd = -1;
    bs->cow_bitmap = NULL;
B
bellard 已提交
95
    pstrcpy(bs->filename, sizeof(bs->filename), filename);
96 97

    /* open standard HD image */
B
bellard 已提交
98 99 100
#ifdef _WIN32
    fd = open(filename, O_RDWR | O_BINARY);
#else
101
    fd = open(filename, O_RDWR | O_LARGEFILE);
B
bellard 已提交
102
#endif
B
bellard 已提交
103
    if (fd < 0) {
104
        /* read only image on disk */
B
bellard 已提交
105 106 107
#ifdef _WIN32
        fd = open(filename, O_RDONLY | O_BINARY);
#else
108
        fd = open(filename, O_RDONLY | O_LARGEFILE);
B
bellard 已提交
109
#endif
B
bellard 已提交
110
        if (fd < 0) {
111 112
            perror(filename);
            goto fail;
B
bellard 已提交
113
        }
114 115
        if (!snapshot)
            bs->read_only = 1;
B
bellard 已提交
116 117
    }
    bs->fd = fd;
118 119 120 121 122 123

    /* see if it is a cow image */
    if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) {
        fprintf(stderr, "%s: could not read header\n", filename);
        goto fail;
    }
B
bellard 已提交
124 125 126
#ifndef _WIN32
    if (be32_to_cpu(cow_header.magic) == COW_MAGIC &&
        be32_to_cpu(cow_header.version) == COW_VERSION) {
127 128 129 130 131 132 133 134 135 136 137 138 139 140
        /* cow image found */
        size = cow_header.size;
#ifndef WORDS_BIGENDIAN
        size = bswap64(size);
#endif    
        bs->total_sectors = size / 512;

        bs->cow_fd = fd;
        bs->fd = -1;
        if (cow_header.backing_file[0] != '\0') {
            if (stat(cow_header.backing_file, &st) != 0) {
                fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file);
                goto fail;
            }
B
bellard 已提交
141
            if (st.st_mtime != be32_to_cpu(cow_header.mtime)) {
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
                fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file);
                goto fail;
            }
            fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE);
            if (fd < 0)
                goto fail;
            bs->fd = fd;
        }
        /* mmap the bitmap */
        bs->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
        bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size), 
                                   bs->cow_bitmap_size, 
                                   PROT_READ | PROT_WRITE,
                                   MAP_SHARED, bs->cow_fd, 0);
        if (bs->cow_bitmap_addr == MAP_FAILED)
            goto fail;
        bs->cow_bitmap = bs->cow_bitmap_addr + sizeof(cow_header);
        bs->cow_sectors_offset = (bs->cow_bitmap_size + 511) & ~511;
        snapshot = 0;
B
bellard 已提交
161 162 163
    } else 
#endif
    {
164 165 166 167 168 169
        /* standard raw image */
        size = lseek64(fd, 0, SEEK_END);
        bs->total_sectors = size / 512;
        bs->fd = fd;
    }

B
bellard 已提交
170
#ifndef _WIN32
171 172
    if (snapshot) {
        /* create a temporary COW file */
B
bellard 已提交
173
        cow_fd = mkstemp64(template);
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
        if (cow_fd < 0)
            goto fail;
        bs->cow_fd = cow_fd;
	unlink(template);
        
        /* just need to allocate bitmap */
        bs->cow_bitmap_size = (bs->total_sectors + 7) >> 3;
        bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size), 
                                   bs->cow_bitmap_size, 
                                   PROT_READ | PROT_WRITE,
                                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        if (bs->cow_bitmap_addr == MAP_FAILED)
            goto fail;
        bs->cow_bitmap = bs->cow_bitmap_addr;
        bs->cow_sectors_offset = 0;
    }
B
bellard 已提交
190
#endif
191
    
B
bellard 已提交
192 193 194 195 196 197 198
    bs->inserted = 1;

    /* call the change callback */
    if (bs->change_cb)
        bs->change_cb(bs->change_opaque);

    return 0;
199 200
 fail:
    bdrv_close(bs);
B
bellard 已提交
201
    return -1;
B
bellard 已提交
202 203 204 205
}

void bdrv_close(BlockDriverState *bs)
{
B
bellard 已提交
206
    if (bs->inserted) {
B
bellard 已提交
207
#ifndef _WIN32
B
bellard 已提交
208 209 210
        /* we unmap the mapping so that it is written to the COW file */
        if (bs->cow_bitmap_addr)
            munmap(bs->cow_bitmap_addr, bs->cow_bitmap_size);
B
bellard 已提交
211
#endif
B
bellard 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
        if (bs->cow_fd >= 0)
            close(bs->cow_fd);
        if (bs->fd >= 0)
            close(bs->fd);
        bs->inserted = 0;

        /* call the change callback */
        if (bs->change_cb)
            bs->change_cb(bs->change_opaque);
    }
}

void bdrv_delete(BlockDriverState *bs)
{
    bdrv_close(bs);
    qemu_free(bs);
B
bellard 已提交
228 229
}

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
static inline void set_bit(uint8_t *bitmap, int64_t bitnum)
{
    bitmap[bitnum / 8] |= (1 << (bitnum%8));
}

static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum)
{
    return !!(bitmap[bitnum / 8] & (1 << (bitnum%8)));
}


/* Return true if first block has been changed (ie. current version is
 * in COW file).  Set the number of continuous blocks for which that
 * is true. */
static int is_changed(uint8_t *bitmap,
                      int64_t sector_num, int nb_sectors,
                      int *num_same)
{
    int changed;

    if (!bitmap || nb_sectors == 0) {
	*num_same = nb_sectors;
	return 0;
    }

    changed = is_bit_set(bitmap, sector_num);
    for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
	if (is_bit_set(bitmap, sector_num + *num_same) != changed)
	    break;
    }

    return changed;
}

/* commit COW file into the raw image */
int bdrv_commit(BlockDriverState *bs)
{
    int64_t i;
    uint8_t *cow_bitmap;

B
bellard 已提交
270 271 272
    if (!bs->inserted)
        return -1;

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
    if (!bs->cow_bitmap) {
	fprintf(stderr, "Already committed to %s\n", bs->filename);
	return 0;
    }

    if (bs->read_only) {
	fprintf(stderr, "Can't commit to %s: read-only\n", bs->filename);
	return -1;
    }

    cow_bitmap = bs->cow_bitmap;
    for (i = 0; i < bs->total_sectors; i++) {
	if (is_bit_set(cow_bitmap, i)) {
	    unsigned char sector[512];
	    if (bdrv_read(bs, i, sector, 1) != 0) {
		fprintf(stderr, "Error reading sector %lli: aborting commit\n",
			(long long)i);
		return -1;
	    }

	    /* Make bdrv_write write to real file for a moment. */
	    bs->cow_bitmap = NULL;
	    if (bdrv_write(bs, i, sector, 1) != 0) {
		fprintf(stderr, "Error writing sector %lli: aborting commit\n",
			(long long)i);
		bs->cow_bitmap = cow_bitmap;
		return -1;
	    }
	    bs->cow_bitmap = cow_bitmap;
	}
    }
    fprintf(stderr, "Committed snapshot to %s\n", bs->filename);
    return 0;
}

B
bellard 已提交
308 309 310 311
/* return -1 if error */
int bdrv_read(BlockDriverState *bs, int64_t sector_num, 
              uint8_t *buf, int nb_sectors)
{
312 313 314
    int ret, n, fd;
    int64_t offset;
    
B
bellard 已提交
315 316 317
    if (!bs->inserted)
        return -1;

318 319 320 321
    while (nb_sectors > 0) {
        if (is_changed(bs->cow_bitmap, sector_num, nb_sectors, &n)) {
            fd = bs->cow_fd;
            offset = bs->cow_sectors_offset;
B
bellard 已提交
322 323 324 325
        } else if (sector_num == 0 && bs->boot_sector_enabled) {
            memcpy(buf, bs->boot_sector_data, 512);
            n = 1;
            goto next;
326 327 328 329
        } else {
            fd = bs->fd;
            offset = 0;
        }
B
bellard 已提交
330

331 332 333 334 335 336 337 338 339 340 341
        if (fd < 0) {
            /* no file, just return empty sectors */
            memset(buf, 0, n * 512);
        } else {
            offset += sector_num * 512;
            lseek64(fd, offset, SEEK_SET);
            ret = read(fd, buf, n * 512);
            if (ret != n * 512) {
                return -1;
            }
        }
B
bellard 已提交
342
    next:
343 344 345 346 347
        nb_sectors -= n;
        sector_num += n;
        buf += n * 512;
    }
    return 0;
B
bellard 已提交
348 349 350 351 352 353
}

/* return -1 if error */
int bdrv_write(BlockDriverState *bs, int64_t sector_num, 
               const uint8_t *buf, int nb_sectors)
{
354 355
    int ret, fd, i;
    int64_t offset, retl;
B
bellard 已提交
356
    
B
bellard 已提交
357 358
    if (!bs->inserted)
        return -1;
B
bellard 已提交
359 360 361
    if (bs->read_only)
        return -1;

362 363 364 365 366 367 368 369 370 371 372
    if (bs->cow_bitmap) {
        fd = bs->cow_fd;
        offset = bs->cow_sectors_offset;
    } else {
        fd = bs->fd;
        offset = 0;
    }
    
    offset += sector_num * 512;
    retl = lseek64(fd, offset, SEEK_SET);
    if (retl == -1) {
B
bellard 已提交
373
        return -1;
374 375 376 377 378 379 380 381 382 383 384
    }
    ret = write(fd, buf, nb_sectors * 512);
    if (ret != nb_sectors * 512) {
        return -1;
    }

    if (bs->cow_bitmap) {
	for (i = 0; i < nb_sectors; i++)
	    set_bit(bs->cow_bitmap, sector_num + i);
    }
    return 0;
B
bellard 已提交
385 386 387 388 389 390
}

void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr)
{
    *nb_sectors_ptr = bs->total_sectors;
}
B
bellard 已提交
391 392 393 394 395 396 397 398 399 400

/* 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);
}
B
bellard 已提交
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472

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_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_is_removable(BlockDriverState *bs)
{
    return bs->removable;
}

int bdrv_is_read_only(BlockDriverState *bs)
{
    return bs->read_only;
}

int bdrv_is_inserted(BlockDriverState *bs)
{
    return bs->inserted;
}

int bdrv_is_locked(BlockDriverState *bs)
{
    return bs->locked;
}

void bdrv_set_locked(BlockDriverState *bs, int locked)
{
    bs->locked = locked;
}

void bdrv_set_change_cb(BlockDriverState *bs, 
                        void (*change_cb)(void *opaque), void *opaque)
{
    bs->change_cb = change_cb;
    bs->change_opaque = opaque;
}

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;
}

B
bellard 已提交
473 474 475 476 477 478 479 480 481
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);
    }
}

B
bellard 已提交
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
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->inserted) {
            term_printf(" file=%s", bs->filename);
            term_printf(" ro=%d", bs->read_only);
        } else {
            term_printf(" [not inserted]");
        }
        term_printf("\n");
    }
}