📄 block-vvfat.c
字号:
const char* dirname){ bootsector_t* bootsector; mapping_t* mapping; unsigned int i; unsigned int cluster; memset(&(s->first_sectors[0]),0,0x40*0x200); s->cluster_size=s->sectors_per_cluster*0x200; s->cluster_buffer=malloc(s->cluster_size); assert(s->cluster_buffer); /* * The formula: sc = spf+1+spf*spc*(512*8/fat_type), * where sc is sector_count, * spf is sectors_per_fat, * spc is sectors_per_clusters, and * fat_type = 12, 16 or 32. */ i = 1+s->sectors_per_cluster*0x200*8/s->fat_type; s->sectors_per_fat=(s->sector_count+i)/i; /* round up */ array_init(&(s->mapping),sizeof(mapping_t)); array_init(&(s->directory),sizeof(direntry_t)); /* add volume label */ { direntry_t* entry=array_get_next(&(s->directory)); entry->attributes=0x28; /* archive | volume label */ snprintf(entry->name,11,"QEMU VVFAT"); } /* Now build FAT, and write back information into directory */ init_fat(s); s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2; s->cluster_count=sector2cluster(s, s->sector_count); mapping = array_get_next(&(s->mapping)); mapping->begin = 0; mapping->dir_index = 0; mapping->info.dir.parent_mapping_index = -1; mapping->first_mapping_index = -1; mapping->path = strdup(dirname); i = strlen(mapping->path); if (i > 0 && mapping->path[i - 1] == '/') mapping->path[i - 1] = '\0'; mapping->mode = MODE_DIRECTORY; mapping->read_only = 0; s->path = mapping->path; for (i = 0, cluster = 0; i < s->mapping.next; i++) { int j; /* MS-DOS expects the FAT to be 0 for the root directory * (except for the media byte). */ /* LATER TODO: still true for FAT32? */ int fix_fat = (i != 0); mapping = array_get(&(s->mapping), i); if (mapping->mode & MODE_DIRECTORY) { mapping->begin = cluster; if(read_directory(s, i)) { fprintf(stderr, "Could not read directory %s\n", mapping->path); return -1; } mapping = array_get(&(s->mapping), i); } else { assert(mapping->mode == MODE_UNDEFINED); mapping->mode=MODE_NORMAL; mapping->begin = cluster; if (mapping->end > 0) { direntry_t* direntry = array_get(&(s->directory), mapping->dir_index); mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size; set_begin_of_direntry(direntry, mapping->begin); } else { mapping->end = cluster + 1; fix_fat = 0; } } assert(mapping->begin < mapping->end); /* fix fat for entry */ if (fix_fat) { for(j = mapping->begin; j < mapping->end - 1; j++) fat_set(s, j, j+1); fat_set(s, mapping->end - 1, s->max_fat_value); } /* next free cluster */ cluster = mapping->end; if(cluster > s->cluster_count) { fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type); return -1; } } mapping = array_get(&(s->mapping), 0); s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster; s->last_cluster_of_root_directory = mapping->end; /* the FAT signature */ fat_set(s,0,s->max_fat_value); fat_set(s,1,s->max_fat_value); s->current_mapping = NULL; bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200); bootsector->jump[0]=0xeb; bootsector->jump[1]=0x3e; bootsector->jump[2]=0x90; memcpy(bootsector->name,"QEMU ",8); bootsector->sector_size=cpu_to_le16(0x200); bootsector->sectors_per_cluster=s->sectors_per_cluster; bootsector->reserved_sectors=cpu_to_le16(1); 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 */ 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->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.current_head=0; bootsector->u.fat16.signature=0x29; bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd); memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11); memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8); bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa; return 0;}static BDRVVVFATState *vvv = NULL;static int enable_write_target(BDRVVVFATState *s);static int is_consistent(BDRVVVFATState *s);static int vvfat_open(BlockDriverState *bs, const char* dirname){ BDRVVVFATState *s = bs->opaque; int floppy = 0; int i; vvv = s;DLOG(if (stderr == NULL) { stderr = fopen("vvfat.log", "a"); setbuf(stderr, NULL);}) s->bs = bs; 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; 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; s->qcow_filename = NULL; s->fat2 = NULL; s->downcase_short_names = 1; 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; s->first_sectors_number = 1; s->sectors_per_cluster=2; bs->cyls = 80; bs->heads = 2; bs->secs = 36; } 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; } else if (strstr(dirname, ":16:")) { s->fat_type = 16; } else if (strstr(dirname, ":12:")) { s->fat_type = 12; s->sector_count=2880; } i = strrchr(dirname, ':') - dirname; assert(i >= 3); if (dirname[i-2] == ':' && isalpha(dirname[i-1])) /* workaround for DOS drive names */ dirname += i-1; else 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; 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; // assert(is_consistent(s)); return 0;}static inline void vvfat_close_current_file(BDRVVVFATState *s){ if(s->current_mapping) { s->current_mapping = NULL; if (s->current_fd) { close(s->current_fd); s->current_fd = 0; } } s->current_cluster = -1;}/* mappings between index1 and index2-1 are supposed to be ordered * return value is the index of the last mapping for which end>cluster_num */static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2){ int index3=index1+1; while(1) { mapping_t* mapping; index3=(index1+index2)/2; mapping=array_get(&(s->mapping),index3); assert(mapping->begin < mapping->end); if(mapping->begin>=cluster_num) { assert(index2!=index3 || index2==0); if(index2==index3) return index1; index2=index3; } else { if(index1==index3) return mapping->end<=cluster_num ? index2 : index1; index1=index3; } assert(index1<=index2); DLOG(mapping=array_get(&(s->mapping),index1); assert(mapping->begin<=cluster_num); assert(index2 >= s->mapping.next || ((mapping = array_get(&(s->mapping),index2)) && mapping->end>cluster_num))); }}static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num){ int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next); mapping_t* mapping; if(index>=s->mapping.next) return 0; mapping=array_get(&(s->mapping),index); if(mapping->begin>cluster_num) return 0; assert(mapping->begin<=cluster_num && mapping->end>cluster_num); return mapping;}/* * This function simply compares path == mapping->path. Since the mappings * are sorted by cluster, this is expensive: O(n). */static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s, const char* path){ int i; for (i = 0; i < s->mapping.next; i++) { mapping_t* mapping = array_get(&(s->mapping), i); if (mapping->first_mapping_index < 0 && !strcmp(path, mapping->path)) return mapping; } return NULL;}static int open_file(BDRVVVFATState* s,mapping_t* mapping){ if(!mapping) return -1; if(!s->current_mapping || strcmp(s->current_mapping->path,mapping->path)) { /* open file */ int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE); if(fd<0) return -1; vvfat_close_current_file(s); s->current_fd = fd; s->current_mapping = mapping; } return 0;}static inline int read_cluster(BDRVVVFATState *s,int cluster_num){ if(s->current_cluster != cluster_num) { int result=0; off_t offset; assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY)); if(!s->current_mapping || s->current_mapping->begin>cluster_num || s->current_mapping->end<=cluster_num) { /* binary search of mappings for file */ mapping_t* mapping=find_mapping_for_cluster(s,cluster_num); assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end)); if (mapping && mapping->mode & MODE_DIRECTORY) { vvfat_close_current_file(s); s->current_mapping = mapping;read_cluster_directory: offset = s->cluster_size*(cluster_num-s->current_mapping->begin); s->cluster = 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); s->current_cluster = cluster_num; return 0; } if(open_file(s,mapping)) return -2; } else if (s->current_mapping->mode & MODE_DIRECTORY) goto read_cluster_directory; assert(s->current_fd); offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset; if(lseek(s->current_fd, offset, SEEK_SET)!=offset) return -3; s->cluster=s->cluster_buffer; result=read(s->current_fd,s->cluster,s->cluster_size); if(result<0) { s->current_cluster = -1; return -1; } s->current_cluster = cluster_num; } return 0;}#ifdef DEBUGstatic void hexdump(const void* address, uint32_t len){ const unsigned char* p = address; int i, j; for (i = 0; i < len; i += 16) { for (j = 0; j < 16 && i + j < len; j++) fprintf(stderr, "%02x ", p[i + j]); for (; j < 16; j++) fprintf(stderr, " "); fprintf(stderr, " "); for (j = 0; j < 16 && i + j < len; j++) fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]); fprintf(stderr, "\n"); }}static void print_direntry(const direntry_t* direntry){ int j = 0; char buffer[1024]; fprintf(stderr, "direntry 0x%x: ", (int)direntry); if(!direntry) return; if(is_long_name(direntry)) { unsigned char* c=(unsigned char*)direntry; int i; for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = '
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -