#include <sys/stat.h>
#include <dirent.h>
#include "qemu-common.h"
-#include "block_int.h"
+#include "block/block_int.h"
#include "module.h"
+#include "migration/migration.h"
#ifndef S_IWGRP
#define S_IWGRP 0
/* here begins the real VVFAT driver */
typedef struct BDRVVVFATState {
+ CoMutex lock;
BlockDriverState* bs; /* pointer to parent */
unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
unsigned char first_sectors[0x40*0x200];
array_t commits;
const char* path;
int downcase_short_names;
+
+ Error *migration_blocker;
} BDRVVVFATState;
/* take the sector position spos and convert it to Cylinder/Head/Sector position
* if the position is outside the specified geometry, fill maximum value for CHS
* and return 1 to signal overflow.
*/
-static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
+static int sector2CHS(mbr_chs_t *chs, int spos, int cyls, int heads, int secs)
+{
int head,sector;
- sector = spos % (bs->secs); spos/= bs->secs;
- head = spos % (bs->heads); spos/= bs->heads;
- if(spos >= bs->cyls){
+ sector = spos % secs; spos /= secs;
+ head = spos % heads; spos /= heads;
+ if (spos >= cyls) {
/* Overflow,
it happens if 32bit sector positions are used, while CHS is only 24bit.
Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
return 0;
}
-static void init_mbr(BDRVVVFATState* s)
+static void init_mbr(BDRVVVFATState *s, int cyls, int heads, int secs)
{
/* TODO: if the files mbr.img and bootsect.img exist, use them */
mbr_t* real_mbr=(mbr_t*)s->first_sectors;
partition->attributes=0x80; /* bootable */
/* LBA is used when partition is outside the CHS geometry */
- lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1);
- lba|= sector2CHS(s->bs, &partition->end_CHS, s->sector_count);
+ lba = sector2CHS(&partition->start_CHS, s->first_sectors_number - 1,
+ cyls, heads, secs);
+ lba |= sector2CHS(&partition->end_CHS, s->bs->total_sectors - 1,
+ cyls, heads, secs);
/*LBA partitions are identified only by start/length_sector_long not by CHS*/
- partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1);
- partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1);
+ partition->start_sector_long = cpu_to_le32(s->first_sectors_number - 1);
+ partition->length_sector_long = cpu_to_le32(s->bs->total_sectors
+ - s->first_sectors_number + 1);
/* FAT12/FAT16/FAT32 */
/* DOS uses different types when partition is LBA,
/* root directory */
int cur = s->directory.next;
array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
+ s->directory.next = ROOT_ENTRIES;
memset(array_get(&(s->directory), cur), 0,
(ROOT_ENTRIES - cur) * sizeof(direntry_t));
}
}
static int init_directories(BDRVVVFATState* s,
- const char* dirname)
+ const char *dirname, int heads, int secs)
{
bootsector_t* bootsector;
mapping_t* mapping;
cluster = mapping->end;
if(cluster > s->cluster_count) {
- fprintf(stderr,"Directory does not fit in FAT%d (capacity %s)\n",
- s->fat_type,
- s->fat_type == 12 ? s->sector_count == 2880 ? "1.44 MB"
- : "2.88 MB"
- : "504MB");
+ fprintf(stderr,"Directory does not fit in FAT%d (capacity %.2f MB)\n",
+ s->fat_type, s->sector_count / 2000.0);
return -EINVAL;
}
bootsector->number_of_fats=0x2; /* number of FATs */
bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
- bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
+ bootsector->media_type=(s->first_sectors_number>1?0xf8:0xf0); /* media descriptor (f8=hd, f0=3.5 fd)*/
s->fat.pointer[0] = bootsector->media_type;
bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
- bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
- bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
+ bootsector->sectors_per_track = cpu_to_le16(secs);
+ bootsector->number_of_heads = cpu_to_le16(heads);
bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
/* LATER TODO: if FAT32, this is wrong */
- bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
+ bootsector->u.fat16.drive_number=s->first_sectors_number==1?0:0x80; /* fda=0, hda=0x80 */
bootsector->u.fat16.current_head=0;
bootsector->u.fat16.signature=0x29;
bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
static int enable_write_target(BDRVVVFATState *s);
static int is_consistent(BDRVVVFATState *s);
+static void vvfat_rebind(BlockDriverState *bs)
+{
+ BDRVVVFATState *s = bs->opaque;
+ s->bs = bs;
+}
+
static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
{
BDRVVVFATState *s = bs->opaque;
- int floppy = 0;
- int i;
+ int i, cyls, heads, secs;
#ifdef DEBUG
vvv = s;
s->bs = bs;
- s->fat_type=16;
/* LATER TODO: if FAT32, adjust */
s->sectors_per_cluster=0x10;
- /* 504MB disk*/
- bs->cyls=1024; bs->heads=16; bs->secs=63;
s->current_cluster=0xffffffff;
if (!strstart(dirname, "fat:", NULL))
return -1;
- if (strstr(dirname, ":floppy:")) {
- floppy = 1;
- s->fat_type = 12;
- s->first_sectors_number = 1;
- s->sectors_per_cluster=2;
- bs->cyls = 80; bs->heads = 2; bs->secs = 36;
- }
-
- s->sector_count=bs->cyls*bs->heads*bs->secs;
-
if (strstr(dirname, ":32:")) {
fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
s->fat_type = 32;
s->fat_type = 16;
} else if (strstr(dirname, ":12:")) {
s->fat_type = 12;
- s->sector_count=2880;
}
+ if (strstr(dirname, ":floppy:")) {
+ /* 1.44MB or 2.88MB floppy. 2.88MB can be FAT12 (default) or FAT16. */
+ if (!s->fat_type) {
+ s->fat_type = 12;
+ secs = 36;
+ s->sectors_per_cluster=2;
+ } else {
+ secs = s->fat_type == 12 ? 18 : 36;
+ s->sectors_per_cluster=1;
+ }
+ s->first_sectors_number = 1;
+ cyls = 80;
+ heads = 2;
+ } else {
+ /* 32MB or 504MB disk*/
+ if (!s->fat_type) {
+ s->fat_type = 16;
+ }
+ cyls = s->fat_type == 12 ? 64 : 1024;
+ heads = 16;
+ secs = 63;
+ }
+ fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
+ dirname, cyls, heads, secs);
+
+ s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
+
if (strstr(dirname, ":rw:")) {
if (enable_write_target(s))
return -1;
else
dirname += i+1;
- bs->total_sectors=bs->cyls*bs->heads*bs->secs;
+ bs->total_sectors = cyls * heads * secs;
- if(init_directories(s, dirname))
+ if (init_directories(s, dirname, heads, secs)) {
return -1;
+ }
s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
- if(s->first_sectors_number==0x40)
- init_mbr(s);
-
- /* for some reason or other, MS-DOS does not like to know about CHS... */
- if (floppy)
- bs->heads = bs->cyls = bs->secs = 0;
+ if (s->first_sectors_number == 0x40) {
+ init_mbr(s, cyls, heads, secs);
+ }
// assert(is_consistent(s));
+ qemu_co_mutex_init(&s->lock);
+
+ /* 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");
+ migrate_add_blocker(s->migration_blocker);
+ }
+
return 0;
}
if(s->current_mapping) {
s->current_mapping = NULL;
if (s->current_fd) {
- close(s->current_fd);
+ qemu_close(s->current_fd);
s->current_fd = 0;
}
}
if(!s->current_mapping ||
strcmp(s->current_mapping->path,mapping->path)) {
/* open file */
- int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
+ int fd = qemu_open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
if(fd<0)
return -1;
vvfat_close_current_file(s);
int i;
for(i=0;i<nb_sectors;i++,sector_num++) {
- if (sector_num >= s->sector_count)
+ if (sector_num >= bs->total_sectors)
return -1;
if (s->qcow) {
int n;
- if (s->qcow->drv->bdrv_is_allocated(s->qcow,
- sector_num, nb_sectors-i, &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 (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
- return -1;
- i += n - 1;
- sector_num += n - 1;
- continue;
- }
+ if (bdrv_read(s->qcow, sector_num, buf + i*0x200, n)) {
+ return -1;
+ }
+ i += n - 1;
+ sector_num += n - 1;
+ continue;
+ }
DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
}
if(sector_num<s->faked_sectors) {
uint32_t sector=sector_num-s->faked_sectors,
sector_offset_in_cluster=(sector%s->sectors_per_cluster),
cluster_num=sector/s->sectors_per_cluster;
- if(read_cluster(s, cluster_num) != 0) {
+ if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) {
/* LATER TODO: strict: return -1; */
memset(buf+i*0x200,0,0x200);
continue;
return 0;
}
+static coroutine_fn int vvfat_co_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ int ret;
+ BDRVVVFATState *s = bs->opaque;
+ qemu_co_mutex_lock(&s->lock);
+ ret = vvfat_read(bs, sector_num, buf, nb_sectors);
+ qemu_co_mutex_unlock(&s->lock);
+ return ret;
+}
+
/* LATER TODO: statify all functions */
/*
return 0;
for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
- was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
+ was_modified = bdrv_is_allocated(s->qcow,
cluster2sector(s, cluster_num) + i, 1, &dummy);
return was_modified;
int64_t offset = cluster2sector(s, cluster_num);
vvfat_close_current_file(s);
- for (i = 0; i < s->sectors_per_cluster; i++)
- if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
- offset + i, 1, &dummy)) {
- if (vvfat_read(s->bs,
- offset, s->cluster_buffer, 1))
- return -1;
- if (s->qcow->drv->bdrv_write(s->qcow,
- offset, s->cluster_buffer, 1))
- return -2;
- }
+ 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)) {
+ return -1;
+ }
+ if (bdrv_write(s->qcow, offset, s->cluster_buffer, 1)) {
+ return -2;
+ }
+ }
+ }
}
}
for (i = s->cluster_size; i < offset; i += s->cluster_size)
c = modified_fat_get(s, c);
- fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
+ fd = qemu_open(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);
}
if (offset > 0) {
if (lseek(fd, offset, SEEK_SET) != offset) {
+ qemu_close(fd);
g_free(cluster);
return -3;
}
(uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
if (ret < 0) {
+ qemu_close(fd);
g_free(cluster);
return ret;
}
if (write(fd, cluster, rest_size) < 0) {
+ qemu_close(fd);
g_free(cluster);
return -2;
}
if (ftruncate(fd, size)) {
perror("ftruncate()");
- close(fd);
+ qemu_close(fd);
g_free(cluster);
return -4;
}
- close(fd);
+ qemu_close(fd);
g_free(cluster);
return commit_mappings(s, first_cluster, dir_index);
return ret;
}
- s->qcow->drv->bdrv_make_empty(s->qcow);
+ if (s->qcow->drv->bdrv_make_empty) {
+ s->qcow->drv->bdrv_make_empty(s->qcow);
+ }
memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
* Use qcow backend. Commit later.
*/
DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
- ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
+ ret = bdrv_write(s->qcow, sector_num, buf, nb_sectors);
if (ret < 0) {
fprintf(stderr, "Error writing to qcow backend\n");
return ret;
return 0;
}
-static int vvfat_is_allocated(BlockDriverState *bs,
+static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ int ret;
+ BDRVVVFATState *s = bs->opaque;
+ qemu_co_mutex_lock(&s->lock);
+ ret = vvfat_write(bs, sector_num, buf, nb_sectors);
+ qemu_co_mutex_unlock(&s->lock);
+ return ret;
+}
+
+static int coroutine_fn vvfat_co_is_allocated(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int* n)
{
BDRVVVFATState* s = bs->opaque;
array_init(&(s->commits), sizeof(commit_t));
s->qcow_filename = g_malloc(1024);
- get_tmp_filename(s->qcow_filename, 1024);
+ ret = get_tmp_filename(s->qcow_filename, 1024);
+ if (ret < 0) {
+ g_free(s->qcow_filename);
+ s->qcow_filename = NULL;
+ return ret;
+ }
bdrv_qcow = bdrv_find_format("qcow");
options = parse_option_parameters("", bdrv_qcow->create_options, NULL);
array_free(&(s->directory));
array_free(&(s->mapping));
g_free(s->cluster_buffer);
+
+ if (s->qcow) {
+ migrate_del_blocker(s->migration_blocker);
+ error_free(s->migration_blocker);
+ }
}
static BlockDriver bdrv_vvfat = {
.format_name = "vvfat",
.instance_size = sizeof(BDRVVVFATState),
.bdrv_file_open = vvfat_open,
- .bdrv_read = vvfat_read,
- .bdrv_write = vvfat_write,
+ .bdrv_rebind = vvfat_rebind,
+ .bdrv_read = vvfat_co_read,
+ .bdrv_write = vvfat_co_write,
.bdrv_close = vvfat_close,
- .bdrv_is_allocated = vvfat_is_allocated,
+ .bdrv_co_is_allocated = vvfat_co_is_allocated,
.protocol_name = "fat",
};