diff options
Diffstat (limited to 'block-vvfat.c')
-rw-r--r-- | block-vvfat.c | 183 |
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); |