* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include <sys/stat.h>
+#include "qemu/osdep.h"
#include <dirent.h>
-#include "qemu-common.h"
+#include "qapi/error.h"
#include "block/block_int.h"
#include "qemu/module.h"
+#include "qemu/bswap.h"
#include "migration/migration.h"
#include "qapi/qmp/qint.h"
#include "qapi/qmp/qbool.h"
+#include "qapi/qmp/qstring.h"
+#include "qemu/cutils.h"
#ifndef S_IWGRP
#define S_IWGRP 0
#define DLOG(a) a
-#undef stderr
-#define stderr STDERR
-FILE* stderr = NULL;
-
static void checkpoint(void);
#ifdef __MINGW32__
static inline void* array_get_next(array_t* array) {
unsigned int next = array->next;
- void* result;
if (array_ensure_allocated(array, next) < 0)
return NULL;
array->next = next + 1;
- result = array_get(array, next);
-
- return result;
+ return array_get(array, next);
}
static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
int fat_type; /* 16 or 32 */
array_t fat,directory,mapping;
+ char volume_label[11];
unsigned int cluster_size;
unsigned int sectors_per_cluster;
unsigned int current_cluster;
/* write support */
- BlockDriverState* write_target;
char* qcow_filename;
- BlockDriverState* qcow;
+ BdrvChild* qcow;
void* fat2;
char* used_clusters;
array_t commits;
if(first_cluster == 0 && (is_dotdot || is_dot))
continue;
- buffer=(char*)g_malloc(length);
+ buffer = g_malloc(length);
snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
if(stat(buffer,&st)<0) {
/* create mapping for this file */
if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
- s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
+ s->current_mapping = array_get_next(&(s->mapping));
s->current_mapping->begin=0;
s->current_mapping->end=st.st_size;
/*
s->current_mapping->path=buffer;
s->current_mapping->read_only =
(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
- }
+ } else {
+ g_free(buffer);
+ }
}
closedir(dir);
}
/* reget the mapping, since s->mapping was possibly realloc()ed */
- mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
+ mapping = array_get(&(s->mapping), mapping_index);
first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
* 0x20 / s->cluster_size;
mapping->end = first_cluster;
- direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
+ direntry = array_get(&(s->directory), mapping->dir_index);
set_begin_of_direntry(direntry, mapping->begin);
return 0;
}
static int init_directories(BDRVVVFATState* s,
- const char *dirname, int heads, int secs)
+ const char *dirname, int heads, int secs,
+ Error **errp)
{
bootsector_t* bootsector;
mapping_t* mapping;
{
direntry_t* entry=array_get_next(&(s->directory));
entry->attributes=0x28; /* archive | volume label */
- memcpy(entry->name, "QEMU VVFAT ", sizeof(entry->name));
+ memcpy(entry->name, s->volume_label, sizeof(entry->name));
}
/* Now build FAT, and write back information into directory */
if (mapping->mode & MODE_DIRECTORY) {
mapping->begin = cluster;
if(read_directory(s, i)) {
- fprintf(stderr, "Could not read directory %s\n",
- mapping->path);
+ error_setg(errp, "Could not read directory %s",
+ mapping->path);
return -1;
}
mapping = array_get(&(s->mapping), i);
cluster = mapping->end;
if(cluster > s->cluster_count) {
- fprintf(stderr,"Directory does not fit in FAT%d (capacity %.2f MB)\n",
- s->fat_type, s->sector_count / 2000.0);
- return -EINVAL;
+ error_setg(errp,
+ "Directory does not fit in FAT%d (capacity %.2f MB)",
+ s->fat_type, s->sector_count / 2000.0);
+ return -1;
}
/* fix fat for entry */
bootsector->u.fat16.signature=0x29;
bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
- memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
+ memcpy(bootsector->u.fat16.volume_label, s->volume_label,
+ sizeof(bootsector->u.fat16.volume_label));
memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8);
bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
static BDRVVVFATState *vvv = NULL;
#endif
-static int enable_write_target(BDRVVVFATState *s);
+static int enable_write_target(BlockDriverState *bs, Error **errp);
static int is_consistent(BDRVVVFATState *s);
-static void vvfat_rebind(BlockDriverState *bs)
-{
- BDRVVVFATState *s = bs->opaque;
- s->bs = bs;
-}
-
static QemuOptsList runtime_opts = {
.name = "vvfat",
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
.type = QEMU_OPT_BOOL,
.help = "Create a floppy rather than a hard disk image",
},
+ {
+ .name = "label",
+ .type = QEMU_OPT_STRING,
+ .help = "Use a volume label other than QEMU VVFAT",
+ },
{
.name = "rw",
.type = QEMU_OPT_BOOL,
/* Fill in the options QDict */
qdict_put(options, "dir", qstring_from_str(filename));
qdict_put(options, "fat-type", qint_from_int(fat_type));
- qdict_put(options, "floppy", qbool_from_int(floppy));
- qdict_put(options, "rw", qbool_from_int(rw));
+ qdict_put(options, "floppy", qbool_from_bool(floppy));
+ qdict_put(options, "rw", qbool_from_bool(rw));
}
static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
BDRVVVFATState *s = bs->opaque;
int cyls, heads, secs;
bool floppy;
- const char *dirname;
+ const char *dirname, *label;
QemuOpts *opts;
Error *local_err = NULL;
int ret;
vvv = s;
#endif
-DLOG(if (stderr == NULL) {
- stderr = fopen("vvfat.log", "a");
- setbuf(stderr, NULL);
-})
-
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
dirname = qemu_opt_get(opts, "dir");
if (!dirname) {
- qerror_report(ERROR_CLASS_GENERIC_ERROR, "vvfat block driver requires "
- "a 'dir' option");
+ error_setg(errp, "vvfat block driver requires a 'dir' option");
ret = -EINVAL;
goto fail;
}
s->fat_type = qemu_opt_get_number(opts, "fat-type", 0);
floppy = qemu_opt_get_bool(opts, "floppy", false);
+ memset(s->volume_label, ' ', sizeof(s->volume_label));
+ label = qemu_opt_get(opts, "label");
+ if (label) {
+ size_t label_length = strlen(label);
+ if (label_length > 11) {
+ error_setg(errp, "vvfat label cannot be longer than 11 bytes");
+ ret = -EINVAL;
+ goto fail;
+ }
+ memcpy(s->volume_label, label, label_length);
+ } else {
+ memcpy(s->volume_label, "QEMU VVFAT", 10);
+ }
+
if (floppy) {
/* 1.44MB or 2.88MB floppy. 2.88MB can be FAT12 (default) or FAT16. */
if (!s->fat_type) {
if (!s->fat_type) {
s->fat_type = 16;
}
+ s->first_sectors_number = 0x40;
cyls = s->fat_type == 12 ? 64 : 1024;
heads = 16;
secs = 63;
case 12:
break;
default:
- qerror_report(ERROR_CLASS_GENERIC_ERROR, "Valid FAT types are only "
- "12, 16 and 32");
+ error_setg(errp, "Valid FAT types are only 12, 16 and 32");
ret = -EINVAL;
goto fail;
}
s->current_cluster=0xffffffff;
- s->first_sectors_number=0x40;
/* read only is the default for safety */
- bs->read_only = 1;
- s->qcow = s->write_target = NULL;
+ bs->read_only = true;
+ s->qcow = NULL;
s->qcow_filename = NULL;
s->fat2 = NULL;
s->downcase_short_names = 1;
s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
if (qemu_opt_get_bool(opts, "rw", false)) {
- ret = enable_write_target(s);
+ ret = enable_write_target(bs, errp);
if (ret < 0) {
goto fail;
}
- bs->read_only = 0;
+ bs->read_only = false;
}
bs->total_sectors = cyls * heads * secs;
- if (init_directories(s, dirname, heads, secs)) {
+ if (init_directories(s, dirname, heads, secs, errp)) {
ret = -EIO;
goto fail;
}
/* Disable migration when vvfat is used rw */
if (s->qcow) {
- error_set(&s->migration_blocker,
- QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
- "vvfat (rw)", bs->device_name, "live migration");
+ error_setg(&s->migration_blocker,
+ "The vvfat (rw) format used by node '%s' "
+ "does not support live migration",
+ bdrv_get_device_or_node_name(bs));
migrate_add_blocker(s->migration_blocker);
}
return ret;
}
+static void vvfat_refresh_limits(BlockDriverState *bs, Error **errp)
+{
+ bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */
+}
+
static inline void vvfat_close_current_file(BDRVVVFATState *s)
{
if(s->current_mapping) {
return -1;
if (s->qcow) {
int n;
- if (bdrv_is_allocated(s->qcow, sector_num, nb_sectors-i, &n)) {
-DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
- if (bdrv_read(s->qcow, sector_num, buf + i*0x200, n)) {
+ if (bdrv_is_allocated(s->qcow->bs, sector_num, nb_sectors-i, &n)) {
+ DLOG(fprintf(stderr, "sectors %d+%d allocated\n",
+ (int)sector_num, n));
+ if (bdrv_read(s->qcow, sector_num, buf + i * 0x200, n)) {
return -1;
}
i += n - 1;
return 0;
}
-static coroutine_fn int vvfat_co_read(BlockDriverState *bs, int64_t sector_num,
- uint8_t *buf, int nb_sectors)
+static int coroutine_fn
+vvfat_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
{
int ret;
BDRVVVFATState *s = bs->opaque;
+ uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
+ int nb_sectors = bytes >> BDRV_SECTOR_BITS;
+ void *buf;
+
+ assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
+ assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
+
+ buf = g_try_malloc(bytes);
+ if (bytes && buf == NULL) {
+ return -ENOMEM;
+ }
+
qemu_co_mutex_lock(&s->lock);
ret = vvfat_read(bs, sector_num, buf, nb_sectors);
qemu_co_mutex_unlock(&s->lock);
+
+ qemu_iovec_from_buf(qiov, 0, buf, bytes);
+ g_free(buf);
+
return ret;
}
int was_modified = 0;
int i, dummy;
- if (s->qcow == NULL)
- return 0;
+ if (s->qcow == NULL) {
+ return 0;
+ }
- for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
- was_modified = bdrv_is_allocated(s->qcow,
- cluster2sector(s, cluster_num) + i, 1, &dummy);
+ for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) {
+ was_modified = bdrv_is_allocated(s->qcow->bs,
+ cluster2sector(s, cluster_num) + i,
+ 1, &dummy);
+ }
return was_modified;
}
vvfat_close_current_file(s);
for (i = 0; i < s->sectors_per_cluster; i++) {
- if (!bdrv_is_allocated(s->qcow, offset + i, 1, &dummy)) {
- if (vvfat_read(s->bs, offset, s->cluster_buffer, 1)) {
+ int res;
+
+ res = bdrv_is_allocated(s->qcow->bs, offset + i, 1, &dummy);
+ if (!res) {
+ res = vvfat_read(s->bs, offset, s->cluster_buffer, 1);
+ if (res) {
return -1;
}
- if (bdrv_write(s->qcow, offset, s->cluster_buffer, 1)) {
+ res = bdrv_write(s->qcow, offset, s->cluster_buffer, 1);
+ if (res) {
return -2;
}
}
if (s->used_clusters[cluster_num] & USED_ANY) {
fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
- return 0;
+ goto fail;
}
s->used_clusters[cluster_num] = USED_DIRECTORY;
/* check file size with FAT */
cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
if (cluster_count !=
- (le32_to_cpu(direntries[i].size) + s->cluster_size
- - 1) / s->cluster_size) {
+ DIV_ROUND_UP(le32_to_cpu(direntries[i].size), s->cluster_size)) {
DLOG(fprintf(stderr, "Cluster count mismatch\n"));
goto fail;
}
factor * (old_cluster_count - new_cluster_count));
for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
+ direntry_t *first_direntry;
void* direntry = array_get(&(s->directory), current_dir_index);
int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
s->sectors_per_cluster);
if (ret)
return ret;
- assert(!strncmp(s->directory.pointer, "QEMU", 4));
+
+ /* The first directory entry on the filesystem is the volume name */
+ first_direntry = (direntry_t*) s->directory.pointer;
+ assert(!memcmp(first_direntry->name, s->volume_label, 11));
+
current_dir_index += factor;
}
return ret;
}
- if (s->qcow->drv->bdrv_make_empty) {
- s->qcow->drv->bdrv_make_empty(s->qcow);
+ if (s->qcow->bs->drv->bdrv_make_empty) {
+ s->qcow->bs->drv->bdrv_make_empty(s->qcow->bs);
}
memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
return 0;
}
-static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
+static int coroutine_fn
+vvfat_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
{
int ret;
BDRVVVFATState *s = bs->opaque;
+ uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
+ int nb_sectors = bytes >> BDRV_SECTOR_BITS;
+ void *buf;
+
+ assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
+ assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
+
+ buf = g_try_malloc(bytes);
+ if (bytes && buf == NULL) {
+ return -ENOMEM;
+ }
+ qemu_iovec_to_buf(qiov, 0, buf, bytes);
+
qemu_co_mutex_lock(&s->lock);
ret = vvfat_write(bs, sector_num, buf, nb_sectors);
qemu_co_mutex_unlock(&s->lock);
+
+ g_free(buf);
+
return ret;
}
static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, int* n)
+ int64_t sector_num, int nb_sectors, int *n, BlockDriverState **file)
{
BDRVVVFATState* s = bs->opaque;
*n = s->sector_count - sector_num;
return BDRV_BLOCK_DATA;
}
-static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
- const uint8_t* buffer, int nb_sectors) {
+static int coroutine_fn
+write_target_commit(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
return try_commit(s);
}
static void write_target_close(BlockDriverState *bs) {
BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
- bdrv_unref(s->qcow);
+ bdrv_unref_child(s->bs, s->qcow);
g_free(s->qcow_filename);
}
static BlockDriver vvfat_write_target = {
.format_name = "vvfat_write_target",
- .bdrv_write = write_target_commit,
+ .bdrv_co_pwritev = write_target_commit,
.bdrv_close = write_target_close,
};
-static int enable_write_target(BDRVVVFATState *s)
+static void vvfat_qcow_options(int *child_flags, QDict *child_options,
+ int parent_flags, QDict *parent_options)
{
- BlockDriver *bdrv_qcow;
- QEMUOptionParameter *options;
- Error *local_err = NULL;
+ qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off");
+ *child_flags = BDRV_O_NO_FLUSH;
+}
+
+static const BdrvChildRole child_vvfat_qcow = {
+ .inherit_options = vvfat_qcow_options,
+};
+
+static int enable_write_target(BlockDriverState *bs, Error **errp)
+{
+ BDRVVVFATState *s = bs->opaque;
+ BlockDriver *bdrv_qcow = NULL;
+ BlockDriverState *backing;
+ QemuOpts *opts = NULL;
int ret;
int size = sector2cluster(s, s->sector_count);
+ QDict *options;
+
s->used_clusters = calloc(size, 1);
array_init(&(s->commits), sizeof(commit_t));
- s->qcow_filename = g_malloc(1024);
- ret = get_tmp_filename(s->qcow_filename, 1024);
+ s->qcow_filename = g_malloc(PATH_MAX);
+ ret = get_tmp_filename(s->qcow_filename, PATH_MAX);
if (ret < 0) {
+ error_setg_errno(errp, -ret, "can't create temporary file");
goto err;
}
bdrv_qcow = bdrv_find_format("qcow");
- options = parse_option_parameters("", bdrv_qcow->create_options, NULL);
- set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
- set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
-
- ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, &local_err);
- if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ if (!bdrv_qcow) {
+ error_setg(errp, "Failed to locate qcow driver");
+ ret = -ENOENT;
goto err;
}
- s->qcow = bdrv_new("");
+ opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort);
+ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s->sector_count * 512,
+ &error_abort);
+ qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort);
- ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
- BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow,
- &local_err);
+ ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
+ qemu_opts_del(opts);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
- bdrv_unref(s->qcow);
+ goto err;
+ }
+
+ options = qdict_new();
+ qdict_put(options, "write-target.driver", qstring_from_str("qcow"));
+ s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs,
+ &child_vvfat_qcow, false, errp);
+ QDECREF(options);
+ if (!s->qcow) {
+ ret = -EINVAL;
goto err;
}
unlink(s->qcow_filename);
#endif
- s->bs->backing_hd = bdrv_new("");
- s->bs->backing_hd->drv = &vvfat_write_target;
- s->bs->backing_hd->opaque = g_malloc(sizeof(void*));
- *(void**)s->bs->backing_hd->opaque = s;
+ backing = bdrv_new();
+ bdrv_set_backing_hd(s->bs, backing);
+ bdrv_unref(backing);
+
+ s->bs->backing->bs->drv = &vvfat_write_target;
+ s->bs->backing->bs->opaque = g_new(void *, 1);
+ *(void**)s->bs->backing->bs->opaque = s;
return 0;
.bdrv_parse_filename = vvfat_parse_filename,
.bdrv_file_open = vvfat_open,
+ .bdrv_refresh_limits = vvfat_refresh_limits,
.bdrv_close = vvfat_close,
- .bdrv_rebind = vvfat_rebind,
- .bdrv_read = vvfat_co_read,
- .bdrv_write = vvfat_co_write,
+ .bdrv_co_preadv = vvfat_co_preadv,
+ .bdrv_co_pwritev = vvfat_co_pwritev,
.bdrv_co_get_block_status = vvfat_co_get_block_status,
};