bs->drv = drv;
bs->opaque = qemu_mallocz(drv->instance_size);
- /*
- * Yes, BDRV_O_NOCACHE aka O_DIRECT means we have to present a
- * write cache to the guest. We do need the fdatasync to flush
- * out transactions for block allocations, and we maybe have a
- * volatile write cache in our backing device to deal with.
- */
- if (flags & (BDRV_O_CACHE_WB|BDRV_O_NOCACHE))
+ if (flags & BDRV_O_CACHE_WB)
bs->enable_write_cache = 1;
/*
open_flags = flags & ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
/*
- * Snapshots should be writeable.
+ * Snapshots should be writable.
*/
if (bs->is_temporary) {
open_flags |= BDRV_O_RDWR;
BlockDriver *back_drv = NULL;
bs->backing_hd = bdrv_new("");
- path_combine(backing_filename, sizeof(backing_filename),
- filename, bs->backing_file);
- if (bs->backing_format[0] != '\0')
+
+ if (path_has_protocol(bs->backing_file)) {
+ pstrcpy(backing_filename, sizeof(backing_filename),
+ bs->backing_file);
+ } else {
+ path_combine(backing_filename, sizeof(backing_filename),
+ filename, bs->backing_file);
+ }
+
+ if (bs->backing_format[0] != '\0') {
back_drv = bdrv_find_format(bs->backing_format);
+ }
/* backing files always opened read-only */
back_flags =
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
- bs->change_cb(bs->change_opaque);
+ bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
}
return 0;
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
- bs->change_cb(bs->change_opaque);
+ bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
}
}
}
}
+/* make a BlockDriverState anonymous by removing from bdrv_state list.
+ Also, NULL terminate the device_name to prevent double remove */
+void bdrv_make_anon(BlockDriverState *bs)
+{
+ if (bs->device_name[0] != '\0') {
+ QTAILQ_REMOVE(&bdrv_states, bs, list);
+ }
+ bs->device_name[0] = '\0';
+}
+
void bdrv_delete(BlockDriverState *bs)
{
assert(!bs->peer);
/* remove from list, if necessary */
- if (bs->device_name[0] != '\0') {
- QTAILQ_REMOVE(&bdrv_states, bs, list);
- }
+ bdrv_make_anon(bs);
bdrv_close(bs);
if (bs->file != NULL) {
* Run consistency checks on an image
*
* Returns 0 if the check could be completed (it doesn't mean that the image is
- * free of errors) or -errno when an internal error occured. The results of the
+ * free of errors) or -errno when an internal error occurred. The results of the
* check are stored in res.
*/
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
return -ENOTSUP;
if (bs->read_only)
return -EACCES;
+ if (bdrv_in_use(bs))
+ return -EBUSY;
ret = drv->bdrv_truncate(bs, offset);
if (ret == 0) {
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
+ if (bs->change_cb) {
+ bs->change_cb(bs->change_opaque, CHANGE_SIZE);
+ }
}
return ret;
}
+/**
+ * Length of a allocated file in bytes. Sparse files are counted by actual
+ * allocated space. Return < 0 if error or unknown.
+ */
+int64_t bdrv_get_allocated_file_size(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+ if (drv->bdrv_get_allocated_file_size) {
+ return drv->bdrv_get_allocated_file_size(bs);
+ }
+ if (bs->file) {
+ return bdrv_get_allocated_file_size(bs->file);
+ }
+ return -ENOTSUP;
+}
+
/**
* Length of a file in bytes. Return < 0 if error or unknown.
*/
if (!drv)
return -ENOMEDIUM;
- /* Fixed size devices use the total_sectors value for speed instead of
- issuing a length query (like lseek) on each call. Also, legacy block
- drivers don't provide a bdrv_getlength function and must use
- total_sectors. */
- if (!bs->growable || !drv->bdrv_getlength) {
- return bs->total_sectors * BDRV_SECTOR_SIZE;
+ if (bs->growable || bs->removable) {
+ if (drv->bdrv_getlength) {
+ return drv->bdrv_getlength(bs);
+ }
}
- return drv->bdrv_getlength(bs);
+ return bs->total_sectors * BDRV_SECTOR_SIZE;
}
/* return 0 as number of sectors if no device present or error */
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_set_translation_hint(BlockDriverState *bs, int translation)
{
bs->translation = translation;
*psecs = bs->secs;
}
-int bdrv_get_type_hint(BlockDriverState *bs)
+/* Recognize floppy formats */
+typedef struct FDFormat {
+ FDriveType drive;
+ uint8_t last_sect;
+ uint8_t max_track;
+ uint8_t max_head;
+} FDFormat;
+
+static const FDFormat fd_formats[] = {
+ /* First entry is default format */
+ /* 1.44 MB 3"1/2 floppy disks */
+ { FDRIVE_DRV_144, 18, 80, 1, },
+ { FDRIVE_DRV_144, 20, 80, 1, },
+ { FDRIVE_DRV_144, 21, 80, 1, },
+ { FDRIVE_DRV_144, 21, 82, 1, },
+ { FDRIVE_DRV_144, 21, 83, 1, },
+ { FDRIVE_DRV_144, 22, 80, 1, },
+ { FDRIVE_DRV_144, 23, 80, 1, },
+ { FDRIVE_DRV_144, 24, 80, 1, },
+ /* 2.88 MB 3"1/2 floppy disks */
+ { FDRIVE_DRV_288, 36, 80, 1, },
+ { FDRIVE_DRV_288, 39, 80, 1, },
+ { FDRIVE_DRV_288, 40, 80, 1, },
+ { FDRIVE_DRV_288, 44, 80, 1, },
+ { FDRIVE_DRV_288, 48, 80, 1, },
+ /* 720 kB 3"1/2 floppy disks */
+ { FDRIVE_DRV_144, 9, 80, 1, },
+ { FDRIVE_DRV_144, 10, 80, 1, },
+ { FDRIVE_DRV_144, 10, 82, 1, },
+ { FDRIVE_DRV_144, 10, 83, 1, },
+ { FDRIVE_DRV_144, 13, 80, 1, },
+ { FDRIVE_DRV_144, 14, 80, 1, },
+ /* 1.2 MB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, 15, 80, 1, },
+ { FDRIVE_DRV_120, 18, 80, 1, },
+ { FDRIVE_DRV_120, 18, 82, 1, },
+ { FDRIVE_DRV_120, 18, 83, 1, },
+ { FDRIVE_DRV_120, 20, 80, 1, },
+ /* 720 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, 9, 80, 1, },
+ { FDRIVE_DRV_120, 11, 80, 1, },
+ /* 360 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, 9, 40, 1, },
+ { FDRIVE_DRV_120, 9, 40, 0, },
+ { FDRIVE_DRV_120, 10, 41, 1, },
+ { FDRIVE_DRV_120, 10, 42, 1, },
+ /* 320 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, 8, 40, 1, },
+ { FDRIVE_DRV_120, 8, 40, 0, },
+ /* 360 kB must match 5"1/4 better than 3"1/2... */
+ { FDRIVE_DRV_144, 9, 80, 0, },
+ /* end */
+ { FDRIVE_DRV_NONE, -1, -1, 0, },
+};
+
+void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
+ int *max_track, int *last_sect,
+ FDriveType drive_in, FDriveType *drive)
{
- return bs->type;
+ const FDFormat *parse;
+ uint64_t nb_sectors, size;
+ int i, first_match, match;
+
+ bdrv_get_geometry_hint(bs, nb_heads, max_track, last_sect);
+ if (*nb_heads != 0 && *max_track != 0 && *last_sect != 0) {
+ /* User defined disk */
+ } else {
+ bdrv_get_geometry(bs, &nb_sectors);
+ match = -1;
+ first_match = -1;
+ for (i = 0; ; i++) {
+ parse = &fd_formats[i];
+ if (parse->drive == FDRIVE_DRV_NONE) {
+ break;
+ }
+ if (drive_in == parse->drive ||
+ drive_in == FDRIVE_DRV_NONE) {
+ size = (parse->max_head + 1) * parse->max_track *
+ parse->last_sect;
+ if (nb_sectors == size) {
+ match = i;
+ break;
+ }
+ if (first_match == -1) {
+ first_match = i;
+ }
+ }
+ }
+ if (match == -1) {
+ if (first_match == -1) {
+ match = 1;
+ } else {
+ match = first_match;
+ }
+ parse = &fd_formats[match];
+ }
+ *nb_heads = parse->max_head + 1;
+ *max_track = parse->max_track;
+ *last_sect = parse->last_sect;
+ *drive = parse->drive;
+ }
}
int bdrv_get_translation_hint(BlockDriverState *bs)
/* XXX: no longer used */
void bdrv_set_change_cb(BlockDriverState *bs,
- void (*change_cb)(void *opaque), void *opaque)
+ void (*change_cb)(void *opaque, int reason),
+ void *opaque)
{
bs->change_cb = change_cb;
bs->change_opaque = opaque;
/* call the change callback now, we skipped it on open */
bs->media_changed = 1;
if (bs->change_cb)
- bs->change_cb(bs->change_opaque);
+ bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
}
return ret;
}
return 1;
}
+int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
+{
+ if (!bs->drv) {
+ return -ENOMEDIUM;
+ }
+ if (!bs->drv->bdrv_discard) {
+ return 0;
+ }
+ return bs->drv->bdrv_discard(bs, sector_num, nb_sectors);
+}
+
/*
* Returns true iff the specified sector is present in the disk image. Drivers
* not implementing the functionality are assumed to not support backing files,
bs_dict = qobject_to_qdict(obj);
- monitor_printf(mon, "%s: type=%s removable=%d",
+ monitor_printf(mon, "%s: removable=%d",
qdict_get_str(bs_dict, "device"),
- qdict_get_str(bs_dict, "type"),
qdict_get_bool(bs_dict, "removable"));
if (qdict_get_bool(bs_dict, "removable")) {
QTAILQ_FOREACH(bs, &bdrv_states, list) {
QObject *bs_obj;
- const char *type = "unknown";
-
- switch(bs->type) {
- case BDRV_TYPE_HD:
- type = "hd";
- break;
- case BDRV_TYPE_CDROM:
- type = "cdrom";
- break;
- case BDRV_TYPE_FLOPPY:
- type = "floppy";
- break;
- }
- bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': %s, "
+ bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': 'unknown', "
"'removable': %i, 'locked': %i }",
- bs->device_name, type, bs->removable,
+ bs->device_name, bs->removable,
bs->locked);
if (bs->drv) {
MultiwriteCB *mcb;
int i;
+ /* don't submit writes if we don't have a medium */
+ if (bs->drv == NULL) {
+ for (i = 0; i < num_reqs; i++) {
+ reqs[i].error = -ENOMEDIUM;
+ }
+ return -1;
+ }
+
if (num_reqs == 0) {
return 0;
}
{
BlockDriver *drv = bs->drv;
+ trace_bdrv_aio_flush(bs, opaque);
+
if (bs->open_flags & BDRV_O_NO_FLUSH) {
return bdrv_aio_noop_em(bs, cb, opaque);
}
{
BlockDriver *drv = bs->drv;
+ trace_bdrv_set_locked(bs, locked);
+
bs->locked = locked;
if (drv && drv->bdrv_set_locked) {
drv->bdrv_set_locked(bs, locked);
{
return bs->dirty_count;
}
+
+void bdrv_set_in_use(BlockDriverState *bs, int in_use)
+{
+ assert(bs->in_use != in_use);
+ bs->in_use = in_use;
+}
+
+int bdrv_in_use(BlockDriverState *bs)
+{
+ return bs->in_use;
+}
+
+int bdrv_img_create(const char *filename, const char *fmt,
+ const char *base_filename, const char *base_fmt,
+ char *options, uint64_t img_size, int flags)
+{
+ QEMUOptionParameter *param = NULL, *create_options = NULL;
+ QEMUOptionParameter *backing_fmt, *backing_file, *size;
+ BlockDriverState *bs = NULL;
+ BlockDriver *drv, *proto_drv;
+ BlockDriver *backing_drv = NULL;
+ int ret = 0;
+
+ /* Find driver and parse its options */
+ drv = bdrv_find_format(fmt);
+ if (!drv) {
+ error_report("Unknown file format '%s'", fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ proto_drv = bdrv_find_protocol(filename);
+ if (!proto_drv) {
+ error_report("Unknown protocol '%s'", filename);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ create_options = append_option_parameters(create_options,
+ drv->create_options);
+ create_options = append_option_parameters(create_options,
+ proto_drv->create_options);
+
+ /* Create parameter list with default values */
+ param = parse_option_parameters("", create_options, param);
+
+ set_option_parameter_int(param, BLOCK_OPT_SIZE, img_size);
+
+ /* Parse -o options */
+ if (options) {
+ param = parse_option_parameters(options, create_options, param);
+ if (param == NULL) {
+ error_report("Invalid options for file format '%s'.", fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (base_filename) {
+ if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE,
+ base_filename)) {
+ error_report("Backing file not supported for file format '%s'",
+ fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (base_fmt) {
+ if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) {
+ error_report("Backing file format not supported for file "
+ "format '%s'", fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
+ if (backing_file && backing_file->value.s) {
+ if (!strcmp(filename, backing_file->value.s)) {
+ error_report("Error: Trying to create an image with the "
+ "same filename as the backing file");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
+ if (backing_fmt && backing_fmt->value.s) {
+ backing_drv = bdrv_find_format(backing_fmt->value.s);
+ if (!backing_drv) {
+ error_report("Unknown backing file format '%s'",
+ backing_fmt->value.s);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ // The size for the image must always be specified, with one exception:
+ // If we are using a backing file, we can obtain the size from there
+ size = get_option_parameter(param, BLOCK_OPT_SIZE);
+ if (size && size->value.n == -1) {
+ if (backing_file && backing_file->value.s) {
+ uint64_t size;
+ char buf[32];
+
+ bs = bdrv_new("");
+
+ ret = bdrv_open(bs, backing_file->value.s, flags, backing_drv);
+ if (ret < 0) {
+ error_report("Could not open '%s'", backing_file->value.s);
+ goto out;
+ }
+ bdrv_get_geometry(bs, &size);
+ size *= 512;
+
+ snprintf(buf, sizeof(buf), "%" PRId64, size);
+ set_option_parameter(param, BLOCK_OPT_SIZE, buf);
+ } else {
+ error_report("Image creation needs a size parameter");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ printf("Formatting '%s', fmt=%s ", filename, fmt);
+ print_option_parameters(param);
+ puts("");
+
+ ret = bdrv_create(drv, filename, param);
+
+ if (ret < 0) {
+ if (ret == -ENOTSUP) {
+ error_report("Formatting or formatting option not supported for "
+ "file format '%s'", fmt);
+ } else if (ret == -EFBIG) {
+ error_report("The image size is too large for file format '%s'",
+ fmt);
+ } else {
+ error_report("%s: error while creating %s: %s", filename, fmt,
+ strerror(-ret));
+ }
+ }
+
+out:
+ free_option_parameters(create_options);
+ free_option_parameters(param);
+
+ if (bs) {
+ bdrv_delete(bs);
+ }
+
+ return ret;
+}