aboutsummaryrefslogtreecommitdiffstats
path: root/block-vvfat.c
diff options
context:
space:
mode:
Diffstat (limited to 'block-vvfat.c')
-rw-r--r--block-vvfat.c183
1 files changed, 111 insertions, 72 deletions
diff --git a/block-vvfat.c b/block-vvfat.c
index c365b9c..79804a7 100644
--- a/block-vvfat.c
+++ b/block-vvfat.c
@@ -25,7 +25,7 @@
#include <sys/stat.h>
#include <dirent.h>
#include <assert.h>
-#include "vl.h"
+#include "qemu-common.h"
#include "block_int.h"
#ifndef S_IWGRP
@@ -53,7 +53,7 @@
#define stderr STDERR
FILE* stderr = NULL;
-static void checkpoint();
+static void checkpoint(void);
#ifdef __MINGW32__
void nonono(const char* file, int line, const char* msg) {
@@ -61,7 +61,7 @@ void nonono(const char* file, int line, const char* msg) {
exit(-5);
}
#undef assert
-#define assert(a) if (!(a)) nonono(__FILE__, __LINE__, #a)
+#define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0)
#endif
#else
@@ -93,7 +93,6 @@ static inline void array_free(array_t* array)
/* does not automatically grow */
static inline void* array_get(array_t* array,unsigned int index) {
- assert(index >= 0);
assert(index < array->next);
return array->pointer + index * array->item_size;
}
@@ -102,7 +101,7 @@ static inline int array_ensure_allocated(array_t* array, int index)
{
if((index + 1) * array->item_size > array->size) {
int new_size = (index + 32) * array->item_size;
- array->pointer = realloc(array->pointer, new_size);
+ array->pointer = qemu_realloc(array->pointer, new_size);
if (!array->pointer)
return -1;
array->size = new_size;
@@ -128,7 +127,7 @@ static inline void* array_get_next(array_t* array) {
static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
if((array->next+count)*array->item_size>array->size) {
int increment=count*array->item_size;
- array->pointer=realloc(array->pointer,array->size+increment);
+ array->pointer=qemu_realloc(array->pointer,array->size+increment);
if(!array->pointer)
return 0;
array->size+=increment;
@@ -175,7 +174,7 @@ static inline int array_roll(array_t* array,int index_to,int index_from,int coun
return 0;
}
-inline int array_remove_slice(array_t* array,int index, int count)
+static inline int array_remove_slice(array_t* array,int index, int count)
{
assert(index >=0);
assert(count > 0);
@@ -186,16 +185,15 @@ inline int array_remove_slice(array_t* array,int index, int count)
return 0;
}
-int array_remove(array_t* array,int index)
+static int array_remove(array_t* array,int index)
{
return array_remove_slice(array, index, 1);
}
/* return the index for a given member */
-int array_index(array_t* array, void* pointer)
+static int array_index(array_t* array, void* pointer)
{
size_t offset = (char*)pointer - array->pointer;
- assert(offset >= 0);
assert((offset % array->item_size) == 0);
assert(offset/array->item_size < array->next);
return offset/array->item_size;
@@ -242,21 +240,25 @@ typedef struct bootsector_t {
uint8_t magic[2];
} __attribute__((packed)) bootsector_t;
+typedef struct {
+ uint8_t head;
+ uint8_t sector;
+ uint8_t cylinder;
+} mbr_chs_t;
+
typedef struct partition_t {
uint8_t attributes; /* 0x80 = bootable */
- uint8_t start_head;
- uint8_t start_sector;
- uint8_t start_cylinder;
- uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xb = FAT32 */
- uint8_t end_head;
- uint8_t end_sector;
- uint8_t end_cylinder;
+ mbr_chs_t start_CHS;
+ uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
+ mbr_chs_t end_CHS;
uint32_t start_sector_long;
- uint32_t end_sector_long;
+ uint32_t length_sector_long;
} __attribute__((packed)) partition_t;
typedef struct mbr_t {
- uint8_t ignored[0x1be];
+ uint8_t ignored[0x1b8];
+ uint32_t nt_id;
+ uint8_t ignored2[2];
partition_t partition[4];
uint8_t magic[2];
} __attribute__((packed)) mbr_t;
@@ -350,11 +352,26 @@ typedef struct BDRVVVFATState {
int downcase_short_names;
} BDRVVVFATState;
-
-static int vvfat_probe(const uint8_t *buf, int buf_size, const char *filename)
-{
- if (strstart(filename, "fat:", NULL))
- return 100;
+/* 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){
+ int head,sector;
+ sector = spos % (bs->secs); spos/= bs->secs;
+ head = spos % (bs->heads); spos/= bs->heads;
+ if(spos >= bs->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 */
+ chs->head = 0xFF;
+ chs->sector = 0xFF;
+ chs->cylinder = 0xFF;
+ return 1;
+ }
+ chs->head = (uint8_t)head;
+ chs->sector = (uint8_t)( (sector+1) | ((spos>>8)<<6) );
+ chs->cylinder = (uint8_t)spos;
return 0;
}
@@ -363,20 +380,29 @@ static void init_mbr(BDRVVVFATState* s)
/* TODO: if the files mbr.img and bootsect.img exist, use them */
mbr_t* real_mbr=(mbr_t*)s->first_sectors;
partition_t* partition=&(real_mbr->partition[0]);
+ int lba;
memset(s->first_sectors,0,512);
+ /* Win NT Disk Signature */
+ real_mbr->nt_id= cpu_to_le32(0xbe1afdfa);
+
partition->attributes=0x80; /* bootable */
- partition->start_head=1;
- partition->start_sector=1;
- partition->start_cylinder=0;
+
+ /* 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 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);
+
/* FAT12/FAT16/FAT32 */
- partition->fs_type=(s->fat_type==12?0x1:s->fat_type==16?0x6:0xb);
- partition->end_head=s->bs->heads-1;
- partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */;
- partition->end_cylinder=0xff; /* lower 8 bits of end cylinder */;
- partition->start_sector_long=cpu_to_le32(s->bs->secs);
- partition->end_sector_long=cpu_to_le32(s->sector_count);
+ /* DOS uses different types when partition is LBA,
+ probably to prevent older versions from using CHS on them */
+ partition->fs_type= s->fat_type==12 ? 0x1:
+ s->fat_type==16 ? (lba?0xe:0x06):
+ /*fat_tyoe==32*/ (lba?0xc:0x0b);
real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
}
@@ -384,17 +410,19 @@ static void init_mbr(BDRVVVFATState* s)
/* direntry functions */
/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
-static inline int short2long_name(unsigned char* dest,const char* src)
+static inline int short2long_name(char* dest,const char* src)
{
int i;
+ int len;
for(i=0;i<129 && src[i];i++) {
dest[2*i]=src[i];
dest[2*i+1]=0;
}
+ len=2*i;
dest[2*i]=dest[2*i+1]=0;
for(i=2*i+2;(i%26);i++)
dest[i]=0xff;
- return i;
+ return len;
}
static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
@@ -411,7 +439,7 @@ static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* fil
entry->begin=0;
entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
}
- for(i=0;i<length;i++) {
+ for(i=0;i<26*number_of_entries;i++) {
int offset=(i%26);
if(offset<10) offset=1+offset;
else if(offset<22) offset=14+offset-10;
@@ -424,8 +452,7 @@ static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* fil
static char is_free(const direntry_t* direntry)
{
- /* return direntry->name[0]==0 ; */
- return direntry->attributes == 0 || direntry->name[0]==0xe5;
+ return direntry->name[0]==0xe5 || direntry->name[0]==0x00;
}
static char is_volume_label(const direntry_t* direntry)
@@ -537,7 +564,7 @@ static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
uint16_t* entry=array_get(&(s->fat),cluster);
return le16_to_cpu(*entry);
} else {
- const uint8_t* x=s->fat.pointer+cluster*3/2;
+ const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2;
return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
}
}
@@ -598,7 +625,7 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
entry=array_get_next(&(s->directory));
memset(entry->name,0x20,11);
- strncpy(entry->name,filename,i);
+ strncpy((char*)entry->name,filename,i);
if(j > 0)
for (i = 0; i < 3 && filename[j+1+i]; i++)
@@ -840,7 +867,7 @@ static int init_directories(BDRVVVFATState* s,
{
direntry_t* entry=array_get_next(&(s->directory));
entry->attributes=0x28; /* archive | volume label */
- snprintf(entry->name,11,"QEMU VVFAT");
+ snprintf((char*)entry->name,11,"QEMU VVFAT");
}
/* Now build FAT, and write back information into directory */
@@ -954,18 +981,22 @@ static int init_directories(BDRVVVFATState* s,
return 0;
}
+#ifdef DEBUG
static BDRVVVFATState *vvv = NULL;
+#endif
static int enable_write_target(BDRVVVFATState *s);
static int is_consistent(BDRVVVFATState *s);
-static int vvfat_open(BlockDriverState *bs, const char* dirname)
+static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
{
BDRVVVFATState *s = bs->opaque;
int floppy = 0;
int i;
+#ifdef DEBUG
vvv = s;
+#endif
DLOG(if (stderr == NULL) {
stderr = fopen("vvfat.log", "a");
@@ -976,10 +1007,9 @@ DLOG(if (stderr == NULL) {
s->fat_type=16;
/* LATER TODO: if FAT32, adjust */
- s->sector_count=0xec04f;
s->sectors_per_cluster=0x10;
- /* LATER TODO: this could be wrong for FAT32 */
- bs->cyls=1023; bs->heads=15; bs->secs=63;
+ /* 504MB disk*/
+ bs->cyls=1024; bs->heads=16; bs->secs=63;
s->current_cluster=0xffffffff;
@@ -994,12 +1024,6 @@ DLOG(if (stderr == NULL) {
if (!strstart(dirname, "fat:", NULL))
return -1;
- if (strstr(dirname, ":rw:")) {
- if (enable_write_target(s))
- return -1;
- bs->read_only = 0;
- }
-
if (strstr(dirname, ":floppy:")) {
floppy = 1;
s->fat_type = 12;
@@ -1008,6 +1032,8 @@ DLOG(if (stderr == NULL) {
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;
@@ -1018,6 +1044,12 @@ DLOG(if (stderr == NULL) {
s->sector_count=2880;
}
+ if (strstr(dirname, ":rw:")) {
+ if (enable_write_target(s))
+ return -1;
+ bs->read_only = 0;
+ }
+
i = strrchr(dirname, ':') - dirname;
assert(i >= 3);
if (dirname[i-2] == ':' && isalpha(dirname[i-1]))
@@ -1027,11 +1059,12 @@ DLOG(if (stderr == NULL) {
dirname += i+1;
bs->total_sectors=bs->cyls*bs->heads*bs->secs;
- if (s->sector_count > bs->total_sectors)
- s->sector_count = bs->total_sectors;
+
if(init_directories(s, dirname))
return -1;
+ s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
+
if(s->first_sectors_number==0x40)
init_mbr(s);
@@ -1040,7 +1073,6 @@ DLOG(if (stderr == NULL) {
bs->heads = bs->cyls = bs->secs = 0;
// assert(is_consistent(s));
-
return 0;
}
@@ -1154,7 +1186,7 @@ static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
s->current_mapping = mapping;
read_cluster_directory:
offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
- s->cluster = s->directory.pointer+offset
+ s->cluster = (unsigned char*)s->directory.pointer+offset
+ 0x20*s->current_mapping->info.dir.first_dir_index;
assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
@@ -1376,7 +1408,12 @@ static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
}
typedef struct {
- unsigned char name[1024];
+ /*
+ * Since the sequence number is at most 0x3f, and the filename
+ * length is at most 13 times the sequence number, the maximal
+ * filename length is 0x3f * 13 bytes.
+ */
+ unsigned char name[0x3f * 13 + 1];
int checksum, len;
int sequence_number;
} long_file_name;
@@ -1401,6 +1438,7 @@ static int parse_long_name(long_file_name* lfn,
lfn->sequence_number = pointer[0] & 0x3f;
lfn->checksum = pointer[13];
lfn->name[0] = 0;
+ lfn->name[lfn->sequence_number * 13] = 0;
} else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
return -1;
else if (pointer[13] != lfn->checksum)
@@ -1424,7 +1462,7 @@ static int parse_long_name(long_file_name* lfn,
}
if (pointer[0] & 0x40)
- lfn->len = offset + strlen(lfn->name + offset);
+ lfn->len = offset + strlen((char*)lfn->name + offset);
return 0;
}
@@ -1463,7 +1501,7 @@ static int parse_short_name(BDRVVVFATState* s,
} else
lfn->name[i + j + 1] = '\0';
- lfn->len = strlen(lfn->name);
+ lfn->len = strlen((char*)lfn->name);
return 0;
}
@@ -1695,7 +1733,7 @@ static int check_directory_consistency(BDRVVVFATState *s,
char path2[PATH_MAX];
assert(path_len < PATH_MAX); /* len was tested before! */
- strcpy(path2, path);
+ pstrcpy(path2, sizeof(path2), path);
path2[path_len] = '/';
path2[path_len + 1] = '\0';
@@ -1759,8 +1797,8 @@ DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i)
fprintf(stderr, "Error in short name (%d)\n", subret);
goto fail;
}
- if (subret > 0 || !strcmp(lfn.name, ".")
- || !strcmp(lfn.name, ".."))
+ if (subret > 0 || !strcmp((char*)lfn.name, ".")
+ || !strcmp((char*)lfn.name, ".."))
continue;
}
lfn.checksum = 0x100; /* cannot use long name twice */
@@ -1769,7 +1807,8 @@ DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i)
fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
goto fail;
}
- strcpy(path2 + path_len + 1, lfn.name);
+ pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
+ (char*)lfn.name);
if (is_directory(direntries + i)) {
if (begin_of_direntry(direntries + i) == 0) {
@@ -2178,7 +2217,7 @@ static int commit_one_file(BDRVVVFATState* s,
for (i = s->cluster_size; i < offset; i += s->cluster_size)
c = modified_fat_get(s, c);
- fd = open(mapping->path, O_BINARY | O_RDWR | O_CREAT, 0666);
+ fd = 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);
@@ -2198,10 +2237,9 @@ static int commit_one_file(BDRVVVFATState* s,
assert((size - offset == 0 && fat_eof(s, c)) ||
(size > offset && c >=2 && !fat_eof(s, c)));
- assert(size >= 0);
ret = vvfat_read(s->bs, cluster2sector(s, c),
- cluster, (rest_size + 0x1ff) / 0x200);
+ (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
if (ret < 0)
return ret;
@@ -2335,8 +2373,9 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
assert(!strncmp(m->path, mapping->path, l2));
- strcpy(new_path, mapping->path);
- strcpy(new_path + l1, m->path + l2);
+ pstrcpy(new_path, l + diff + 1, mapping->path);
+ pstrcpy(new_path + l1, l + diff + 1 - l1,
+ m->path + l2);
schedule_rename(s, m->begin, new_path);
}
@@ -2732,8 +2771,7 @@ static int enable_write_target(BDRVVVFATState *s)
array_init(&(s->commits), sizeof(commit_t));
s->qcow_filename = malloc(1024);
- strcpy(s->qcow_filename, "/tmp/vl.XXXXXX");
- get_tmp_filename(s->qcow_filename, strlen(s->qcow_filename) + 1);
+ get_tmp_filename(s->qcow_filename, 1024);
if (bdrv_create(&bdrv_qcow,
s->qcow_filename, s->sector_count, "fat:", 0) < 0)
return -1;
@@ -2767,18 +2805,19 @@ static void vvfat_close(BlockDriverState *bs)
BlockDriver bdrv_vvfat = {
"vvfat",
sizeof(BDRVVVFATState),
- vvfat_probe,
+ NULL, /* no probe for protocols */
vvfat_open,
vvfat_read,
vvfat_write,
vvfat_close,
NULL, /* ??? Not sure if we can do any meaningful flushing. */
NULL,
- vvfat_is_allocated
+ vvfat_is_allocated,
+ .protocol_name = "fat",
};
#ifdef DEBUG
-static void checkpoint() {
+static void checkpoint(void) {
assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
check1(vvv);
check2(vvv);