#include "qemu/osdep.h"
#include <dirent.h>
+#include <glib/gstdio.h>
#include "qapi/error.h"
+#include "block/block-io.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "qemu/module.h"
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
c > 127 ||
- strchr("$%'-_@~`!(){}^#&.+,;=[]", c) != NULL))
+ strchr(" $%'-_@~`!(){}^#&.+,;=[]", c) != NULL))
{
return false;
}
return 0;
}
-static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
+static inline int32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
{
return (sector_num - s->offset_to_root_dir) / s->sectors_per_cluster;
}
#endif
static int enable_write_target(BlockDriverState *bs, Error **errp);
-static int is_consistent(BDRVVVFATState *s);
+static int coroutine_fn is_consistent(BDRVVVFATState *s);
static QemuOptsList runtime_opts = {
.name = "vvfat",
dirname, cyls, heads, secs));
s->sector_count = cyls * heads * secs - s->offset_to_bootsector;
+ bs->total_sectors = cyls * heads * secs;
if (qemu_opt_get_bool(opts, "rw", false)) {
if (!bdrv_is_read_only(bs)) {
}
}
- bs->total_sectors = cyls * heads * secs;
-
if (init_directories(s, dirname, heads, secs, errp)) {
ret = -EIO;
goto fail;
qemu_co_mutex_init(&s->lock);
- ret = 0;
+ qemu_opts_del(opts);
+
+ return 0;
+
fail:
+ g_free(s->qcow_filename);
+ s->qcow_filename = NULL;
+ g_free(s->cluster_buffer);
+ s->cluster_buffer = NULL;
+ g_free(s->used_clusters);
+ s->used_clusters = NULL;
+
qemu_opts_del(opts);
return ret;
}
if(!s->current_mapping ||
strcmp(s->current_mapping->path,mapping->path)) {
/* open file */
- int fd = qemu_open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
+ int fd = qemu_open_old(mapping->path,
+ O_RDONLY | O_BINARY | O_LARGEFILE);
if(fd<0)
return -1;
vvfat_close_current_file(s);
for(i=0;i<11;i++)
ADD_CHAR(direntry->name[i]);
buffer[j] = 0;
- fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
+ fprintf(stderr, "%s attributes=0x%02x begin=%u size=%u\n",
buffer,
direntry->attributes,
begin_of_direntry(direntry),le32_to_cpu(direntry->size));
static void print_mapping(const mapping_t* mapping)
{
- fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, "
+ fprintf(stderr, "mapping (%p): begin, end = %u, %u, dir_index = %u, "
"first_mapping_index = %d, name = %s, mode = 0x%x, " ,
mapping, mapping->begin, mapping->end, mapping->dir_index,
mapping->first_mapping_index, mapping->path, mapping->mode);
if (mapping->mode & MODE_DIRECTORY)
fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
else
- fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
+ fprintf(stderr, "offset = %u\n", mapping->info.file.offset);
}
#endif
-static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
- uint8_t *buf, int nb_sectors)
+static int coroutine_fn GRAPH_RDLOCK
+vvfat_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors)
{
BDRVVVFATState *s = bs->opaque;
int i;
DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64
" allocated\n", sector_num,
n >> BDRV_SECTOR_BITS));
- if (bdrv_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE,
- buf + i * 0x200, n) < 0) {
+ if (bdrv_co_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE, n,
+ buf + i * 0x200, 0) < 0) {
return -1;
}
i += (n >> BDRV_SECTOR_BITS) - 1;
return 0;
}
-static int coroutine_fn
-vvfat_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags)
+static int coroutine_fn GRAPH_RDLOCK
+vvfat_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
+ QEMUIOVector *qiov, BdrvRequestFlags flags)
{
int ret;
BDRVVVFATState *s = bs->opaque;
static void clear_commits(BDRVVVFATState* s)
{
int i;
-DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
+DLOG(fprintf(stderr, "clear_commits (%u commits)\n", s->commits.next));
for (i = 0; i < s->commits.next; i++) {
commit_t* commit = array_get(&(s->commits), i);
assert(commit->path || commit->action == ACTION_WRITEOUT);
}
}
-static inline bool cluster_was_modified(BDRVVVFATState *s,
- uint32_t cluster_num)
+static inline bool coroutine_fn GRAPH_RDLOCK
+cluster_was_modified(BDRVVVFATState *s, uint32_t cluster_num)
{
int was_modified = 0;
int i;
* Further, the files/directories handled by this function are
* assumed to be *not* deleted (and *only* those).
*/
-static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
- direntry_t* direntry, const char* path)
+static uint32_t coroutine_fn GRAPH_RDLOCK
+get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const char* path)
{
/*
* This is a little bit tricky:
if (res) {
return -1;
}
- res = bdrv_pwrite(s->qcow, offset * BDRV_SECTOR_SIZE,
- s->cluster_buffer, BDRV_SECTOR_SIZE);
+ res = bdrv_co_pwrite(s->qcow, offset * BDRV_SECTOR_SIZE,
+ BDRV_SECTOR_SIZE, s->cluster_buffer,
+ 0);
if (res < 0) {
return -2;
}
* It returns 0 upon inconsistency or error, and the number of clusters
* used by the directory, its subdirectories and their files.
*/
-static int check_directory_consistency(BDRVVVFATState *s,
- int cluster_num, const char* path)
+static int coroutine_fn GRAPH_RDLOCK
+check_directory_consistency(BDRVVVFATState *s, int cluster_num, const char* path)
{
int ret = 0;
unsigned char* cluster = g_malloc(s->cluster_size);
}
/* returns 1 on success */
-static int is_consistent(BDRVVVFATState* s)
+static int coroutine_fn GRAPH_RDLOCK
+is_consistent(BDRVVVFATState* s)
{
int i, check;
int used_clusters_count = 0;
return 0;
}
-static int commit_direntries(BDRVVVFATState* s,
- int dir_index, int parent_mapping_index)
+static int coroutine_fn GRAPH_RDLOCK
+commit_direntries(BDRVVVFATState* s, int dir_index, int parent_mapping_index)
{
direntry_t* direntry = array_get(&(s->directory), dir_index);
uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
/* commit one file (adjust contents, adjust mapping),
return first_mapping_index */
-static int commit_one_file(BDRVVVFATState* s,
- int dir_index, uint32_t offset)
+static int coroutine_fn GRAPH_RDLOCK
+commit_one_file(BDRVVVFATState* s, int dir_index, uint32_t offset)
{
direntry_t* direntry = array_get(&(s->directory), dir_index);
uint32_t c = begin_of_direntry(direntry);
for (i = s->cluster_size; i < offset; i += s->cluster_size)
c = modified_fat_get(s, c);
- fd = qemu_open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
+ fd = qemu_open_old(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
if (fd < 0) {
fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
strerror(errno), errno);
fprintf(stderr, "handle_renames\n");
for (i = 0; i < s->commits.next; i++) {
commit_t* commit = array_get(&(s->commits), i);
- fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
+ fprintf(stderr, "%d, %s (%u, %d)\n", i,
+ commit->path ? commit->path : "(null)",
+ commit->param.rename.cluster, commit->action);
}
#endif
mapping_t* mapping;
int j, parent_path_len;
-#ifdef __MINGW32__
- if (mkdir(commit->path))
- return -5;
-#else
- if (mkdir(commit->path, 0755))
+ if (g_mkdir(commit->path, 0755)) {
return -5;
-#endif
+ }
mapping = insert_mapping(s, commit->param.mkdir.cluster,
commit->param.mkdir.cluster + 1);
/*
* TODO: make sure that the short name is not matching *another* file
*/
-static int handle_commits(BDRVVVFATState* s)
+static int coroutine_fn GRAPH_RDLOCK handle_commits(BDRVVVFATState* s)
{
int i, fail = 0;
fail = -2;
break;
case ACTION_WRITEOUT: {
-#ifndef NDEBUG
- /* these variables are only used by assert() below */
direntry_t* entry = array_get(&(s->directory),
commit->param.writeout.dir_index);
uint32_t begin = begin_of_direntry(entry);
mapping_t* mapping = find_mapping_for_cluster(s, begin);
-#endif
assert(mapping);
assert(mapping->begin == begin);
* - recurse direntries from root (using bs->bdrv_pread)
* - delete files corresponding to mappings marked as deleted
*/
-static int do_commit(BDRVVVFATState* s)
+static int coroutine_fn GRAPH_RDLOCK do_commit(BDRVVVFATState* s)
{
int ret = 0;
return 0;
}
-static int try_commit(BDRVVVFATState* s)
+static int coroutine_fn GRAPH_RDLOCK try_commit(BDRVVVFATState* s)
{
vvfat_close_current_file(s);
DLOG(checkpoint());
return do_commit(s);
}
-static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
- const uint8_t *buf, int nb_sectors)
+static int coroutine_fn GRAPH_RDLOCK
+vvfat_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
{
BDRVVVFATState *s = bs->opaque;
int i, ret;
+ int first_cluster, last_cluster;
DLOG(checkpoint());
vvfat_close_current_file(s);
+ if (sector_num == s->offset_to_bootsector && nb_sectors == 1) {
+ /*
+ * Write on bootsector. Allow only changing the reserved1 field,
+ * used to mark volume dirtiness
+ */
+ unsigned char *bootsector = s->first_sectors
+ + s->offset_to_bootsector * 0x200;
+ /*
+ * LATER TODO: if FAT32, this is wrong (see init_directories(),
+ * which always creates a FAT16 bootsector)
+ */
+ const int reserved1_offset = offsetof(bootsector_t, u.fat16.reserved1);
+
+ for (i = 0; i < 0x200; i++) {
+ if (i != reserved1_offset && bootsector[i] != buf[i]) {
+ fprintf(stderr, "Tried to write to protected bootsector\n");
+ return -1;
+ }
+ }
+
+ /* Update bootsector with the only updatable byte, and return success */
+ bootsector[reserved1_offset] = buf[reserved1_offset];
+ return 0;
+ }
+
/*
* Some sanity checks:
* - do not allow writing to the boot sector
*/
-
if (sector_num < s->offset_to_fat)
return -1;
- for (i = sector2cluster(s, sector_num);
- i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
- mapping_t* mapping = find_mapping_for_cluster(s, i);
+ /*
+ * Values will be negative for writes to the FAT, which is located before
+ * the root directory.
+ */
+ first_cluster = sector2cluster(s, sector_num);
+ last_cluster = sector2cluster(s, sector_num + nb_sectors - 1);
+
+ for (i = first_cluster; i <= last_cluster;) {
+ mapping_t *mapping = NULL;
+
+ if (i >= 0) {
+ mapping = find_mapping_for_cluster(s, i);
+ }
+
if (mapping) {
if (mapping->read_only) {
fprintf(stderr, "Tried to write to write-protected file %s\n",
}
}
i = mapping->end;
- } else
+ } else {
i++;
+ }
}
/*
* Use qcow backend. Commit later.
*/
DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
- ret = bdrv_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE, buf,
- nb_sectors * BDRV_SECTOR_SIZE);
+ ret = bdrv_co_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE,
+ nb_sectors * BDRV_SECTOR_SIZE, buf, 0);
if (ret < 0) {
fprintf(stderr, "Error writing to qcow backend\n");
return ret;
}
- for (i = sector2cluster(s, sector_num);
- i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
- if (i >= 0)
+ for (i = first_cluster; i <= last_cluster; i++) {
+ if (i >= 0) {
s->used_clusters[i] |= USED_ALLOCATED;
+ }
+ }
DLOG(checkpoint());
/* TODO: add timeout */
return 0;
}
-static int coroutine_fn
-vvfat_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags)
+static int coroutine_fn GRAPH_RDLOCK
+vvfat_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
+ QEMUIOVector *qiov, BdrvRequestFlags flags)
{
int ret;
BDRVVVFATState *s = bs->opaque;
return BDRV_BLOCK_DATA;
}
-static int coroutine_fn
-write_target_commit(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags)
-{
- int ret;
-
- BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
- qemu_co_mutex_lock(&s->lock);
- ret = try_commit(s);
- qemu_co_mutex_unlock(&s->lock);
-
- return ret;
-}
-
-static BlockDriver vvfat_write_target = {
- .format_name = "vvfat_write_target",
- .instance_size = sizeof(void*),
- .bdrv_co_pwritev = write_target_commit,
-};
-
static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format,
int *child_flags, QDict *child_options,
int parent_flags, QDict *parent_options)
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
}
-static const BdrvChildClass child_vvfat_qcow = {
- .parent_is_bds = true,
- .inherit_options = vvfat_qcow_options,
-};
+static BdrvChildClass child_vvfat_qcow;
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);
+ s->used_clusters = g_malloc0(size);
array_init(&(s->commits), sizeof(commit_t));
- 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");
+ s->qcow_filename = create_tmp_file(errp);
+ if (!s->qcow_filename) {
+ ret = -ENOENT;
goto err;
}
}
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_number(opts, BLOCK_OPT_SIZE,
+ bs->total_sectors * BDRV_SECTOR_SIZE, &error_abort);
qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort);
ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
unlink(s->qcow_filename);
#endif
- backing = bdrv_new_open_driver(&vvfat_write_target, NULL, BDRV_O_ALLOW_RDWR,
- &error_abort);
- *(void**) backing->opaque = s;
-
- bdrv_set_backing_hd(s->bs, backing, &error_abort);
- bdrv_unref(backing);
-
return 0;
err:
- g_free(s->qcow_filename);
- s->qcow_filename = NULL;
return ret;
}
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
- BDRVVVFATState *s = bs->opaque;
-
- assert(c == s->qcow || (role & BDRV_CHILD_COW));
-
- if (c == s->qcow) {
- /* This is a private node, nobody should try to attach to it */
- *nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
- *nshared = BLK_PERM_WRITE_UNCHANGED;
- } else {
- /* The backing file is there so 'commit' can use it. vvfat doesn't
- * access it in any way. */
- *nperm = 0;
- *nshared = BLK_PERM_ALL;
- }
+ assert(role & BDRV_CHILD_DATA);
+ /* This is a private node, nobody should try to attach to it */
+ *nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
+ *nshared = BLK_PERM_WRITE_UNCHANGED;
}
static void vvfat_close(BlockDriverState *bs)
static void bdrv_vvfat_init(void)
{
+ child_vvfat_qcow = child_of_bds;
+ child_vvfat_qcow.inherit_options = vvfat_qcow_options;
bdrv_register(&bdrv_vvfat);
}