|
|
1.1 ! root 1: /* vim:set shiftwidth=4 ts=8: */ ! 2: /* ! 3: * QEMU Block driver for virtual VFAT (shadows a local directory) ! 4: * ! 5: * Copyright (c) 2004,2005 Johannes E. Schindelin ! 6: * ! 7: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 8: * of this software and associated documentation files (the "Software"), to deal ! 9: * in the Software without restriction, including without limitation the rights ! 10: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 11: * copies of the Software, and to permit persons to whom the Software is ! 12: * furnished to do so, subject to the following conditions: ! 13: * ! 14: * The above copyright notice and this permission notice shall be included in ! 15: * all copies or substantial portions of the Software. ! 16: * ! 17: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 18: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 19: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 20: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 21: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 22: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 23: * THE SOFTWARE. ! 24: */ ! 25: #include <sys/stat.h> ! 26: #include <dirent.h> ! 27: #include "qemu-common.h" ! 28: #include "block_int.h" ! 29: #include "module.h" ! 30: ! 31: #ifndef S_IWGRP ! 32: #define S_IWGRP 0 ! 33: #endif ! 34: #ifndef S_IWOTH ! 35: #define S_IWOTH 0 ! 36: #endif ! 37: ! 38: /* TODO: add ":bootsector=blabla.img:" */ ! 39: /* LATER TODO: add automatic boot sector generation from ! 40: BOOTEASY.ASM and Ranish Partition Manager ! 41: Note that DOS assumes the system files to be the first files in the ! 42: file system (test if the boot sector still relies on that fact)! */ ! 43: /* MAYBE TODO: write block-visofs.c */ ! 44: /* TODO: call try_commit() only after a timeout */ ! 45: ! 46: /* #define DEBUG */ ! 47: ! 48: #ifdef DEBUG ! 49: ! 50: #define DLOG(a) a ! 51: ! 52: #undef stderr ! 53: #define stderr STDERR ! 54: FILE* stderr = NULL; ! 55: ! 56: static void checkpoint(void); ! 57: ! 58: #ifdef __MINGW32__ ! 59: void nonono(const char* file, int line, const char* msg) { ! 60: fprintf(stderr, "Nonono! %s:%d %s\n", file, line, msg); ! 61: exit(-5); ! 62: } ! 63: #undef assert ! 64: #define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0) ! 65: #endif ! 66: ! 67: #else ! 68: ! 69: #define DLOG(a) ! 70: ! 71: #endif ! 72: ! 73: /* dynamic array functions */ ! 74: typedef struct array_t { ! 75: char* pointer; ! 76: unsigned int size,next,item_size; ! 77: } array_t; ! 78: ! 79: static inline void array_init(array_t* array,unsigned int item_size) ! 80: { ! 81: array->pointer = NULL; ! 82: array->size=0; ! 83: array->next=0; ! 84: array->item_size=item_size; ! 85: } ! 86: ! 87: static inline void array_free(array_t* array) ! 88: { ! 89: if(array->pointer) ! 90: free(array->pointer); ! 91: array->size=array->next=0; ! 92: } ! 93: ! 94: /* does not automatically grow */ ! 95: static inline void* array_get(array_t* array,unsigned int index) { ! 96: assert(index < array->next); ! 97: return array->pointer + index * array->item_size; ! 98: } ! 99: ! 100: static inline int array_ensure_allocated(array_t* array, int index) ! 101: { ! 102: if((index + 1) * array->item_size > array->size) { ! 103: int new_size = (index + 32) * array->item_size; ! 104: array->pointer = qemu_realloc(array->pointer, new_size); ! 105: if (!array->pointer) ! 106: return -1; ! 107: array->size = new_size; ! 108: array->next = index + 1; ! 109: } ! 110: ! 111: return 0; ! 112: } ! 113: ! 114: static inline void* array_get_next(array_t* array) { ! 115: unsigned int next = array->next; ! 116: void* result; ! 117: ! 118: if (array_ensure_allocated(array, next) < 0) ! 119: return NULL; ! 120: ! 121: array->next = next + 1; ! 122: result = array_get(array, next); ! 123: ! 124: return result; ! 125: } ! 126: ! 127: static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) { ! 128: if((array->next+count)*array->item_size>array->size) { ! 129: int increment=count*array->item_size; ! 130: array->pointer=qemu_realloc(array->pointer,array->size+increment); ! 131: if(!array->pointer) ! 132: return NULL; ! 133: array->size+=increment; ! 134: } ! 135: memmove(array->pointer+(index+count)*array->item_size, ! 136: array->pointer+index*array->item_size, ! 137: (array->next-index)*array->item_size); ! 138: array->next+=count; ! 139: return array->pointer+index*array->item_size; ! 140: } ! 141: ! 142: /* this performs a "roll", so that the element which was at index_from becomes ! 143: * index_to, but the order of all other elements is preserved. */ ! 144: static inline int array_roll(array_t* array,int index_to,int index_from,int count) ! 145: { ! 146: char* buf; ! 147: char* from; ! 148: char* to; ! 149: int is; ! 150: ! 151: if(!array || ! 152: index_to<0 || index_to>=array->next || ! 153: index_from<0 || index_from>=array->next) ! 154: return -1; ! 155: ! 156: if(index_to==index_from) ! 157: return 0; ! 158: ! 159: is=array->item_size; ! 160: from=array->pointer+index_from*is; ! 161: to=array->pointer+index_to*is; ! 162: buf=qemu_malloc(is*count); ! 163: memcpy(buf,from,is*count); ! 164: ! 165: if(index_to<index_from) ! 166: memmove(to+is*count,to,from-to); ! 167: else ! 168: memmove(from,from+is*count,to-from); ! 169: ! 170: memcpy(to,buf,is*count); ! 171: ! 172: free(buf); ! 173: ! 174: return 0; ! 175: } ! 176: ! 177: static inline int array_remove_slice(array_t* array,int index, int count) ! 178: { ! 179: assert(index >=0); ! 180: assert(count > 0); ! 181: assert(index + count <= array->next); ! 182: if(array_roll(array,array->next-1,index,count)) ! 183: return -1; ! 184: array->next -= count; ! 185: return 0; ! 186: } ! 187: ! 188: static int array_remove(array_t* array,int index) ! 189: { ! 190: return array_remove_slice(array, index, 1); ! 191: } ! 192: ! 193: /* return the index for a given member */ ! 194: static int array_index(array_t* array, void* pointer) ! 195: { ! 196: size_t offset = (char*)pointer - array->pointer; ! 197: assert((offset % array->item_size) == 0); ! 198: assert(offset/array->item_size < array->next); ! 199: return offset/array->item_size; ! 200: } ! 201: ! 202: /* These structures are used to fake a disk and the VFAT filesystem. ! 203: * For this reason we need to use __attribute__((packed)). */ ! 204: ! 205: typedef struct bootsector_t { ! 206: uint8_t jump[3]; ! 207: uint8_t name[8]; ! 208: uint16_t sector_size; ! 209: uint8_t sectors_per_cluster; ! 210: uint16_t reserved_sectors; ! 211: uint8_t number_of_fats; ! 212: uint16_t root_entries; ! 213: uint16_t total_sectors16; ! 214: uint8_t media_type; ! 215: uint16_t sectors_per_fat; ! 216: uint16_t sectors_per_track; ! 217: uint16_t number_of_heads; ! 218: uint32_t hidden_sectors; ! 219: uint32_t total_sectors; ! 220: union { ! 221: struct { ! 222: uint8_t drive_number; ! 223: uint8_t current_head; ! 224: uint8_t signature; ! 225: uint32_t id; ! 226: uint8_t volume_label[11]; ! 227: } __attribute__((packed)) fat16; ! 228: struct { ! 229: uint32_t sectors_per_fat; ! 230: uint16_t flags; ! 231: uint8_t major,minor; ! 232: uint32_t first_cluster_of_root_directory; ! 233: uint16_t info_sector; ! 234: uint16_t backup_boot_sector; ! 235: uint16_t ignored; ! 236: } __attribute__((packed)) fat32; ! 237: } u; ! 238: uint8_t fat_type[8]; ! 239: uint8_t ignored[0x1c0]; ! 240: uint8_t magic[2]; ! 241: } __attribute__((packed)) bootsector_t; ! 242: ! 243: typedef struct { ! 244: uint8_t head; ! 245: uint8_t sector; ! 246: uint8_t cylinder; ! 247: } mbr_chs_t; ! 248: ! 249: typedef struct partition_t { ! 250: uint8_t attributes; /* 0x80 = bootable */ ! 251: mbr_chs_t start_CHS; ! 252: uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */ ! 253: mbr_chs_t end_CHS; ! 254: uint32_t start_sector_long; ! 255: uint32_t length_sector_long; ! 256: } __attribute__((packed)) partition_t; ! 257: ! 258: typedef struct mbr_t { ! 259: uint8_t ignored[0x1b8]; ! 260: uint32_t nt_id; ! 261: uint8_t ignored2[2]; ! 262: partition_t partition[4]; ! 263: uint8_t magic[2]; ! 264: } __attribute__((packed)) mbr_t; ! 265: ! 266: typedef struct direntry_t { ! 267: uint8_t name[8]; ! 268: uint8_t extension[3]; ! 269: uint8_t attributes; ! 270: uint8_t reserved[2]; ! 271: uint16_t ctime; ! 272: uint16_t cdate; ! 273: uint16_t adate; ! 274: uint16_t begin_hi; ! 275: uint16_t mtime; ! 276: uint16_t mdate; ! 277: uint16_t begin; ! 278: uint32_t size; ! 279: } __attribute__((packed)) direntry_t; ! 280: ! 281: /* this structure are used to transparently access the files */ ! 282: ! 283: typedef struct mapping_t { ! 284: /* begin is the first cluster, end is the last+1 */ ! 285: uint32_t begin,end; ! 286: /* as s->directory is growable, no pointer may be used here */ ! 287: unsigned int dir_index; ! 288: /* the clusters of a file may be in any order; this points to the first */ ! 289: int first_mapping_index; ! 290: union { ! 291: /* offset is ! 292: * - the offset in the file (in clusters) for a file, or ! 293: * - the next cluster of the directory for a directory, and ! 294: * - the address of the buffer for a faked entry ! 295: */ ! 296: struct { ! 297: uint32_t offset; ! 298: } file; ! 299: struct { ! 300: int parent_mapping_index; ! 301: int first_dir_index; ! 302: } dir; ! 303: } info; ! 304: /* path contains the full path, i.e. it always starts with s->path */ ! 305: char* path; ! 306: ! 307: enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2, ! 308: MODE_DIRECTORY = 4, MODE_FAKED = 8, ! 309: MODE_DELETED = 16, MODE_RENAMED = 32 } mode; ! 310: int read_only; ! 311: } mapping_t; ! 312: ! 313: #ifdef DEBUG ! 314: static void print_direntry(const struct direntry_t*); ! 315: static void print_mapping(const struct mapping_t* mapping); ! 316: #endif ! 317: ! 318: /* here begins the real VVFAT driver */ ! 319: ! 320: typedef struct BDRVVVFATState { ! 321: BlockDriverState* bs; /* pointer to parent */ ! 322: unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */ ! 323: unsigned char first_sectors[0x40*0x200]; ! 324: ! 325: int fat_type; /* 16 or 32 */ ! 326: array_t fat,directory,mapping; ! 327: ! 328: unsigned int cluster_size; ! 329: unsigned int sectors_per_cluster; ! 330: unsigned int sectors_per_fat; ! 331: unsigned int sectors_of_root_directory; ! 332: uint32_t last_cluster_of_root_directory; ! 333: unsigned int faked_sectors; /* how many sectors are faked before file data */ ! 334: uint32_t sector_count; /* total number of sectors of the partition */ ! 335: uint32_t cluster_count; /* total number of clusters of this partition */ ! 336: uint32_t max_fat_value; ! 337: ! 338: int current_fd; ! 339: mapping_t* current_mapping; ! 340: unsigned char* cluster; /* points to current cluster */ ! 341: unsigned char* cluster_buffer; /* points to a buffer to hold temp data */ ! 342: unsigned int current_cluster; ! 343: ! 344: /* write support */ ! 345: BlockDriverState* write_target; ! 346: char* qcow_filename; ! 347: BlockDriverState* qcow; ! 348: void* fat2; ! 349: char* used_clusters; ! 350: array_t commits; ! 351: const char* path; ! 352: int downcase_short_names; ! 353: } BDRVVVFATState; ! 354: ! 355: /* take the sector position spos and convert it to Cylinder/Head/Sector position ! 356: * if the position is outside the specified geometry, fill maximum value for CHS ! 357: * and return 1 to signal overflow. ! 358: */ ! 359: static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){ ! 360: int head,sector; ! 361: sector = spos % (bs->secs); spos/= bs->secs; ! 362: head = spos % (bs->heads); spos/= bs->heads; ! 363: if(spos >= bs->cyls){ ! 364: /* Overflow, ! 365: it happens if 32bit sector positions are used, while CHS is only 24bit. ! 366: Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */ ! 367: chs->head = 0xFF; ! 368: chs->sector = 0xFF; ! 369: chs->cylinder = 0xFF; ! 370: return 1; ! 371: } ! 372: chs->head = (uint8_t)head; ! 373: chs->sector = (uint8_t)( (sector+1) | ((spos>>8)<<6) ); ! 374: chs->cylinder = (uint8_t)spos; ! 375: return 0; ! 376: } ! 377: ! 378: static void init_mbr(BDRVVVFATState* s) ! 379: { ! 380: /* TODO: if the files mbr.img and bootsect.img exist, use them */ ! 381: mbr_t* real_mbr=(mbr_t*)s->first_sectors; ! 382: partition_t* partition=&(real_mbr->partition[0]); ! 383: int lba; ! 384: ! 385: memset(s->first_sectors,0,512); ! 386: ! 387: /* Win NT Disk Signature */ ! 388: real_mbr->nt_id= cpu_to_le32(0xbe1afdfa); ! 389: ! 390: partition->attributes=0x80; /* bootable */ ! 391: ! 392: /* LBA is used when partition is outside the CHS geometry */ ! 393: lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1); ! 394: lba|= sector2CHS(s->bs, &partition->end_CHS, s->sector_count); ! 395: ! 396: /*LBA partitions are identified only by start/length_sector_long not by CHS*/ ! 397: partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1); ! 398: partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1); ! 399: ! 400: /* FAT12/FAT16/FAT32 */ ! 401: /* DOS uses different types when partition is LBA, ! 402: probably to prevent older versions from using CHS on them */ ! 403: partition->fs_type= s->fat_type==12 ? 0x1: ! 404: s->fat_type==16 ? (lba?0xe:0x06): ! 405: /*fat_tyoe==32*/ (lba?0xc:0x0b); ! 406: ! 407: real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa; ! 408: } ! 409: ! 410: /* direntry functions */ ! 411: ! 412: /* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */ ! 413: static inline int short2long_name(char* dest,const char* src) ! 414: { ! 415: int i; ! 416: int len; ! 417: for(i=0;i<129 && src[i];i++) { ! 418: dest[2*i]=src[i]; ! 419: dest[2*i+1]=0; ! 420: } ! 421: len=2*i; ! 422: dest[2*i]=dest[2*i+1]=0; ! 423: for(i=2*i+2;(i%26);i++) ! 424: dest[i]=0xff; ! 425: return len; ! 426: } ! 427: ! 428: static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename) ! 429: { ! 430: char buffer[258]; ! 431: int length=short2long_name(buffer,filename), ! 432: number_of_entries=(length+25)/26,i; ! 433: direntry_t* entry; ! 434: ! 435: for(i=0;i<number_of_entries;i++) { ! 436: entry=array_get_next(&(s->directory)); ! 437: entry->attributes=0xf; ! 438: entry->reserved[0]=0; ! 439: entry->begin=0; ! 440: entry->name[0]=(number_of_entries-i)|(i==0?0x40:0); ! 441: } ! 442: for(i=0;i<26*number_of_entries;i++) { ! 443: int offset=(i%26); ! 444: if(offset<10) offset=1+offset; ! 445: else if(offset<22) offset=14+offset-10; ! 446: else offset=28+offset-22; ! 447: entry=array_get(&(s->directory),s->directory.next-1-(i/26)); ! 448: entry->name[offset]=buffer[i]; ! 449: } ! 450: return array_get(&(s->directory),s->directory.next-number_of_entries); ! 451: } ! 452: ! 453: static char is_free(const direntry_t* direntry) ! 454: { ! 455: return direntry->name[0]==0xe5 || direntry->name[0]==0x00; ! 456: } ! 457: ! 458: static char is_volume_label(const direntry_t* direntry) ! 459: { ! 460: return direntry->attributes == 0x28; ! 461: } ! 462: ! 463: static char is_long_name(const direntry_t* direntry) ! 464: { ! 465: return direntry->attributes == 0xf; ! 466: } ! 467: ! 468: static char is_short_name(const direntry_t* direntry) ! 469: { ! 470: return !is_volume_label(direntry) && !is_long_name(direntry) ! 471: && !is_free(direntry); ! 472: } ! 473: ! 474: static char is_directory(const direntry_t* direntry) ! 475: { ! 476: return direntry->attributes & 0x10 && direntry->name[0] != 0xe5; ! 477: } ! 478: ! 479: static inline char is_dot(const direntry_t* direntry) ! 480: { ! 481: return is_short_name(direntry) && direntry->name[0] == '.'; ! 482: } ! 483: ! 484: static char is_file(const direntry_t* direntry) ! 485: { ! 486: return is_short_name(direntry) && !is_directory(direntry); ! 487: } ! 488: ! 489: static inline uint32_t begin_of_direntry(const direntry_t* direntry) ! 490: { ! 491: return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16); ! 492: } ! 493: ! 494: static inline uint32_t filesize_of_direntry(const direntry_t* direntry) ! 495: { ! 496: return le32_to_cpu(direntry->size); ! 497: } ! 498: ! 499: static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin) ! 500: { ! 501: direntry->begin = cpu_to_le16(begin & 0xffff); ! 502: direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff); ! 503: } ! 504: ! 505: /* fat functions */ ! 506: ! 507: static inline uint8_t fat_chksum(const direntry_t* entry) ! 508: { ! 509: uint8_t chksum=0; ! 510: int i; ! 511: ! 512: for(i=0;i<11;i++) { ! 513: unsigned char c; ! 514: ! 515: c = (i <= 8) ? entry->name[i] : entry->extension[i-8]; ! 516: chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c; ! 517: } ! 518: ! 519: return chksum; ! 520: } ! 521: ! 522: /* if return_time==0, this returns the fat_date, else the fat_time */ ! 523: static uint16_t fat_datetime(time_t time,int return_time) { ! 524: struct tm* t; ! 525: #ifdef _WIN32 ! 526: t=localtime(&time); /* this is not thread safe */ ! 527: #else ! 528: struct tm t1; ! 529: t=&t1; ! 530: localtime_r(&time,t); ! 531: #endif ! 532: if(return_time) ! 533: return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11)); ! 534: return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9)); ! 535: } ! 536: ! 537: static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value) ! 538: { ! 539: if(s->fat_type==32) { ! 540: uint32_t* entry=array_get(&(s->fat),cluster); ! 541: *entry=cpu_to_le32(value); ! 542: } else if(s->fat_type==16) { ! 543: uint16_t* entry=array_get(&(s->fat),cluster); ! 544: *entry=cpu_to_le16(value&0xffff); ! 545: } else { ! 546: int offset = (cluster*3/2); ! 547: unsigned char* p = array_get(&(s->fat), offset); ! 548: switch (cluster&1) { ! 549: case 0: ! 550: p[0] = value&0xff; ! 551: p[1] = (p[1]&0xf0) | ((value>>8)&0xf); ! 552: break; ! 553: case 1: ! 554: p[0] = (p[0]&0xf) | ((value&0xf)<<4); ! 555: p[1] = (value>>4); ! 556: break; ! 557: } ! 558: } ! 559: } ! 560: ! 561: static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster) ! 562: { ! 563: if(s->fat_type==32) { ! 564: uint32_t* entry=array_get(&(s->fat),cluster); ! 565: return le32_to_cpu(*entry); ! 566: } else if(s->fat_type==16) { ! 567: uint16_t* entry=array_get(&(s->fat),cluster); ! 568: return le16_to_cpu(*entry); ! 569: } else { ! 570: const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2; ! 571: return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff; ! 572: } ! 573: } ! 574: ! 575: static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry) ! 576: { ! 577: if(fat_entry>s->max_fat_value-8) ! 578: return -1; ! 579: return 0; ! 580: } ! 581: ! 582: static inline void init_fat(BDRVVVFATState* s) ! 583: { ! 584: if (s->fat_type == 12) { ! 585: array_init(&(s->fat),1); ! 586: array_ensure_allocated(&(s->fat), ! 587: s->sectors_per_fat * 0x200 * 3 / 2 - 1); ! 588: } else { ! 589: array_init(&(s->fat),(s->fat_type==32?4:2)); ! 590: array_ensure_allocated(&(s->fat), ! 591: s->sectors_per_fat * 0x200 / s->fat.item_size - 1); ! 592: } ! 593: memset(s->fat.pointer,0,s->fat.size); ! 594: ! 595: switch(s->fat_type) { ! 596: case 12: s->max_fat_value=0xfff; break; ! 597: case 16: s->max_fat_value=0xffff; break; ! 598: case 32: s->max_fat_value=0x0fffffff; break; ! 599: default: s->max_fat_value=0; /* error... */ ! 600: } ! 601: ! 602: } ! 603: ! 604: /* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */ ! 605: /* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */ ! 606: static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s, ! 607: unsigned int directory_start, const char* filename, int is_dot) ! 608: { ! 609: int i,j,long_index=s->directory.next; ! 610: direntry_t* entry = NULL; ! 611: direntry_t* entry_long = NULL; ! 612: ! 613: if(is_dot) { ! 614: entry=array_get_next(&(s->directory)); ! 615: memset(entry->name,0x20,11); ! 616: memcpy(entry->name,filename,strlen(filename)); ! 617: return entry; ! 618: } ! 619: ! 620: entry_long=create_long_filename(s,filename); ! 621: ! 622: i = strlen(filename); ! 623: for(j = i - 1; j>0 && filename[j]!='.';j--); ! 624: if (j > 0) ! 625: i = (j > 8 ? 8 : j); ! 626: else if (i > 8) ! 627: i = 8; ! 628: ! 629: entry=array_get_next(&(s->directory)); ! 630: memset(entry->name,0x20,11); ! 631: memcpy(entry->name, filename, i); ! 632: ! 633: if(j > 0) ! 634: for (i = 0; i < 3 && filename[j+1+i]; i++) ! 635: entry->extension[i] = filename[j+1+i]; ! 636: ! 637: /* upcase & remove unwanted characters */ ! 638: for(i=10;i>=0;i--) { ! 639: if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--); ! 640: if(entry->name[i]<=' ' || entry->name[i]>0x7f ! 641: || strchr(".*?<>|\":/\\[];,+='",entry->name[i])) ! 642: entry->name[i]='_'; ! 643: else if(entry->name[i]>='a' && entry->name[i]<='z') ! 644: entry->name[i]+='A'-'a'; ! 645: } ! 646: ! 647: /* mangle duplicates */ ! 648: while(1) { ! 649: direntry_t* entry1=array_get(&(s->directory),directory_start); ! 650: int j; ! 651: ! 652: for(;entry1<entry;entry1++) ! 653: if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11)) ! 654: break; /* found dupe */ ! 655: if(entry1==entry) /* no dupe found */ ! 656: break; ! 657: ! 658: /* use all 8 characters of name */ ! 659: if(entry->name[7]==' ') { ! 660: int j; ! 661: for(j=6;j>0 && entry->name[j]==' ';j--) ! 662: entry->name[j]='~'; ! 663: } ! 664: ! 665: /* increment number */ ! 666: for(j=7;j>0 && entry->name[j]=='9';j--) ! 667: entry->name[j]='0'; ! 668: if(j>0) { ! 669: if(entry->name[j]<'0' || entry->name[j]>'9') ! 670: entry->name[j]='0'; ! 671: else ! 672: entry->name[j]++; ! 673: } ! 674: } ! 675: ! 676: /* calculate checksum; propagate to long name */ ! 677: if(entry_long) { ! 678: uint8_t chksum=fat_chksum(entry); ! 679: ! 680: /* calculate anew, because realloc could have taken place */ ! 681: entry_long=array_get(&(s->directory),long_index); ! 682: while(entry_long<entry && is_long_name(entry_long)) { ! 683: entry_long->reserved[1]=chksum; ! 684: entry_long++; ! 685: } ! 686: } ! 687: ! 688: return entry; ! 689: } ! 690: ! 691: /* ! 692: * Read a directory. (the index of the corresponding mapping must be passed). ! 693: */ ! 694: static int read_directory(BDRVVVFATState* s, int mapping_index) ! 695: { ! 696: mapping_t* mapping = array_get(&(s->mapping), mapping_index); ! 697: direntry_t* direntry; ! 698: const char* dirname = mapping->path; ! 699: int first_cluster = mapping->begin; ! 700: int parent_index = mapping->info.dir.parent_mapping_index; ! 701: mapping_t* parent_mapping = (mapping_t*) ! 702: (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : NULL); ! 703: int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1; ! 704: ! 705: DIR* dir=opendir(dirname); ! 706: struct dirent* entry; ! 707: int i; ! 708: ! 709: assert(mapping->mode & MODE_DIRECTORY); ! 710: ! 711: if(!dir) { ! 712: mapping->end = mapping->begin; ! 713: return -1; ! 714: } ! 715: ! 716: i = mapping->info.dir.first_dir_index = ! 717: first_cluster == 0 ? 0 : s->directory.next; ! 718: ! 719: /* actually read the directory, and allocate the mappings */ ! 720: while((entry=readdir(dir))) { ! 721: unsigned int length=strlen(dirname)+2+strlen(entry->d_name); ! 722: char* buffer; ! 723: direntry_t* direntry; ! 724: struct stat st; ! 725: int is_dot=!strcmp(entry->d_name,"."); ! 726: int is_dotdot=!strcmp(entry->d_name,".."); ! 727: ! 728: if(first_cluster == 0 && (is_dotdot || is_dot)) ! 729: continue; ! 730: ! 731: buffer=(char*)qemu_malloc(length); ! 732: snprintf(buffer,length,"%s/%s",dirname,entry->d_name); ! 733: ! 734: if(stat(buffer,&st)<0) { ! 735: free(buffer); ! 736: continue; ! 737: } ! 738: ! 739: /* create directory entry for this file */ ! 740: direntry=create_short_and_long_name(s, i, entry->d_name, ! 741: is_dot || is_dotdot); ! 742: direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20); ! 743: direntry->reserved[0]=direntry->reserved[1]=0; ! 744: direntry->ctime=fat_datetime(st.st_ctime,1); ! 745: direntry->cdate=fat_datetime(st.st_ctime,0); ! 746: direntry->adate=fat_datetime(st.st_atime,0); ! 747: direntry->begin_hi=0; ! 748: direntry->mtime=fat_datetime(st.st_mtime,1); ! 749: direntry->mdate=fat_datetime(st.st_mtime,0); ! 750: if(is_dotdot) ! 751: set_begin_of_direntry(direntry, first_cluster_of_parent); ! 752: else if(is_dot) ! 753: set_begin_of_direntry(direntry, first_cluster); ! 754: else ! 755: direntry->begin=0; /* do that later */ ! 756: if (st.st_size > 0x7fffffff) { ! 757: fprintf(stderr, "File %s is larger than 2GB\n", buffer); ! 758: free(buffer); ! 759: return -2; ! 760: } ! 761: direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size); ! 762: ! 763: /* create mapping for this file */ ! 764: if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) { ! 765: s->current_mapping=(mapping_t*)array_get_next(&(s->mapping)); ! 766: s->current_mapping->begin=0; ! 767: s->current_mapping->end=st.st_size; ! 768: /* ! 769: * we get the direntry of the most recent direntry, which ! 770: * contains the short name and all the relevant information. ! 771: */ ! 772: s->current_mapping->dir_index=s->directory.next-1; ! 773: s->current_mapping->first_mapping_index = -1; ! 774: if (S_ISDIR(st.st_mode)) { ! 775: s->current_mapping->mode = MODE_DIRECTORY; ! 776: s->current_mapping->info.dir.parent_mapping_index = ! 777: mapping_index; ! 778: } else { ! 779: s->current_mapping->mode = MODE_UNDEFINED; ! 780: s->current_mapping->info.file.offset = 0; ! 781: } ! 782: s->current_mapping->path=buffer; ! 783: s->current_mapping->read_only = ! 784: (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0; ! 785: } ! 786: } ! 787: closedir(dir); ! 788: ! 789: /* fill with zeroes up to the end of the cluster */ ! 790: while(s->directory.next%(0x10*s->sectors_per_cluster)) { ! 791: direntry_t* direntry=array_get_next(&(s->directory)); ! 792: memset(direntry,0,sizeof(direntry_t)); ! 793: } ! 794: ! 795: /* TODO: if there are more entries, bootsector has to be adjusted! */ ! 796: #define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster) ! 797: if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) { ! 798: /* root directory */ ! 799: int cur = s->directory.next; ! 800: array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1); ! 801: memset(array_get(&(s->directory), cur), 0, ! 802: (ROOT_ENTRIES - cur) * sizeof(direntry_t)); ! 803: } ! 804: ! 805: /* reget the mapping, since s->mapping was possibly realloc()ed */ ! 806: mapping = (mapping_t*)array_get(&(s->mapping), mapping_index); ! 807: first_cluster += (s->directory.next - mapping->info.dir.first_dir_index) ! 808: * 0x20 / s->cluster_size; ! 809: mapping->end = first_cluster; ! 810: ! 811: direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index); ! 812: set_begin_of_direntry(direntry, mapping->begin); ! 813: ! 814: return 0; ! 815: } ! 816: ! 817: static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num) ! 818: { ! 819: return (sector_num-s->faked_sectors)/s->sectors_per_cluster; ! 820: } ! 821: ! 822: static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num) ! 823: { ! 824: return s->faked_sectors + s->sectors_per_cluster * cluster_num; ! 825: } ! 826: ! 827: static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num) ! 828: { ! 829: return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster; ! 830: } ! 831: ! 832: #ifdef DBG ! 833: static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping) ! 834: { ! 835: if(mapping->mode==MODE_UNDEFINED) ! 836: return 0; ! 837: return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index); ! 838: } ! 839: #endif ! 840: ! 841: static int init_directories(BDRVVVFATState* s, ! 842: const char* dirname) ! 843: { ! 844: bootsector_t* bootsector; ! 845: mapping_t* mapping; ! 846: unsigned int i; ! 847: unsigned int cluster; ! 848: ! 849: memset(&(s->first_sectors[0]),0,0x40*0x200); ! 850: ! 851: s->cluster_size=s->sectors_per_cluster*0x200; ! 852: s->cluster_buffer=qemu_malloc(s->cluster_size); ! 853: ! 854: /* ! 855: * The formula: sc = spf+1+spf*spc*(512*8/fat_type), ! 856: * where sc is sector_count, ! 857: * spf is sectors_per_fat, ! 858: * spc is sectors_per_clusters, and ! 859: * fat_type = 12, 16 or 32. ! 860: */ ! 861: i = 1+s->sectors_per_cluster*0x200*8/s->fat_type; ! 862: s->sectors_per_fat=(s->sector_count+i)/i; /* round up */ ! 863: ! 864: array_init(&(s->mapping),sizeof(mapping_t)); ! 865: array_init(&(s->directory),sizeof(direntry_t)); ! 866: ! 867: /* add volume label */ ! 868: { ! 869: direntry_t* entry=array_get_next(&(s->directory)); ! 870: entry->attributes=0x28; /* archive | volume label */ ! 871: snprintf((char*)entry->name,11,"QEMU VVFAT"); ! 872: } ! 873: ! 874: /* Now build FAT, and write back information into directory */ ! 875: init_fat(s); ! 876: ! 877: s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2; ! 878: s->cluster_count=sector2cluster(s, s->sector_count); ! 879: ! 880: mapping = array_get_next(&(s->mapping)); ! 881: mapping->begin = 0; ! 882: mapping->dir_index = 0; ! 883: mapping->info.dir.parent_mapping_index = -1; ! 884: mapping->first_mapping_index = -1; ! 885: mapping->path = strdup(dirname); ! 886: i = strlen(mapping->path); ! 887: if (i > 0 && mapping->path[i - 1] == '/') ! 888: mapping->path[i - 1] = '\0'; ! 889: mapping->mode = MODE_DIRECTORY; ! 890: mapping->read_only = 0; ! 891: s->path = mapping->path; ! 892: ! 893: for (i = 0, cluster = 0; i < s->mapping.next; i++) { ! 894: /* MS-DOS expects the FAT to be 0 for the root directory ! 895: * (except for the media byte). */ ! 896: /* LATER TODO: still true for FAT32? */ ! 897: int fix_fat = (i != 0); ! 898: mapping = array_get(&(s->mapping), i); ! 899: ! 900: if (mapping->mode & MODE_DIRECTORY) { ! 901: mapping->begin = cluster; ! 902: if(read_directory(s, i)) { ! 903: fprintf(stderr, "Could not read directory %s\n", ! 904: mapping->path); ! 905: return -1; ! 906: } ! 907: mapping = array_get(&(s->mapping), i); ! 908: } else { ! 909: assert(mapping->mode == MODE_UNDEFINED); ! 910: mapping->mode=MODE_NORMAL; ! 911: mapping->begin = cluster; ! 912: if (mapping->end > 0) { ! 913: direntry_t* direntry = array_get(&(s->directory), ! 914: mapping->dir_index); ! 915: ! 916: mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size; ! 917: set_begin_of_direntry(direntry, mapping->begin); ! 918: } else { ! 919: mapping->end = cluster + 1; ! 920: fix_fat = 0; ! 921: } ! 922: } ! 923: ! 924: assert(mapping->begin < mapping->end); ! 925: ! 926: /* next free cluster */ ! 927: cluster = mapping->end; ! 928: ! 929: if(cluster > s->cluster_count) { ! 930: fprintf(stderr,"Directory does not fit in FAT%d (capacity %s)\n", ! 931: s->fat_type, ! 932: s->fat_type == 12 ? s->sector_count == 2880 ? "1.44 MB" ! 933: : "2.88 MB" ! 934: : "504MB"); ! 935: return -EINVAL; ! 936: } ! 937: ! 938: /* fix fat for entry */ ! 939: if (fix_fat) { ! 940: int j; ! 941: for(j = mapping->begin; j < mapping->end - 1; j++) ! 942: fat_set(s, j, j+1); ! 943: fat_set(s, mapping->end - 1, s->max_fat_value); ! 944: } ! 945: } ! 946: ! 947: mapping = array_get(&(s->mapping), 0); ! 948: s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster; ! 949: s->last_cluster_of_root_directory = mapping->end; ! 950: ! 951: /* the FAT signature */ ! 952: fat_set(s,0,s->max_fat_value); ! 953: fat_set(s,1,s->max_fat_value); ! 954: ! 955: s->current_mapping = NULL; ! 956: ! 957: bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200); ! 958: bootsector->jump[0]=0xeb; ! 959: bootsector->jump[1]=0x3e; ! 960: bootsector->jump[2]=0x90; ! 961: memcpy(bootsector->name,"QEMU ",8); ! 962: bootsector->sector_size=cpu_to_le16(0x200); ! 963: bootsector->sectors_per_cluster=s->sectors_per_cluster; ! 964: bootsector->reserved_sectors=cpu_to_le16(1); ! 965: bootsector->number_of_fats=0x2; /* number of FATs */ ! 966: bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10); ! 967: bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count); ! 968: bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */ ! 969: s->fat.pointer[0] = bootsector->media_type; ! 970: bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat); ! 971: bootsector->sectors_per_track=cpu_to_le16(s->bs->secs); ! 972: bootsector->number_of_heads=cpu_to_le16(s->bs->heads); ! 973: bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f); ! 974: bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0); ! 975: ! 976: /* LATER TODO: if FAT32, this is wrong */ ! 977: bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */ ! 978: bootsector->u.fat16.current_head=0; ! 979: bootsector->u.fat16.signature=0x29; ! 980: bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd); ! 981: ! 982: memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11); ! 983: memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8); ! 984: bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa; ! 985: ! 986: return 0; ! 987: } ! 988: ! 989: #ifdef DEBUG ! 990: static BDRVVVFATState *vvv = NULL; ! 991: #endif ! 992: ! 993: static int enable_write_target(BDRVVVFATState *s); ! 994: static int is_consistent(BDRVVVFATState *s); ! 995: ! 996: static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags) ! 997: { ! 998: BDRVVVFATState *s = bs->opaque; ! 999: int floppy = 0; ! 1000: int i; ! 1001: ! 1002: #ifdef DEBUG ! 1003: vvv = s; ! 1004: #endif ! 1005: ! 1006: DLOG(if (stderr == NULL) { ! 1007: stderr = fopen("vvfat.log", "a"); ! 1008: setbuf(stderr, NULL); ! 1009: }) ! 1010: ! 1011: s->bs = bs; ! 1012: ! 1013: s->fat_type=16; ! 1014: /* LATER TODO: if FAT32, adjust */ ! 1015: s->sectors_per_cluster=0x10; ! 1016: /* 504MB disk*/ ! 1017: bs->cyls=1024; bs->heads=16; bs->secs=63; ! 1018: ! 1019: s->current_cluster=0xffffffff; ! 1020: ! 1021: s->first_sectors_number=0x40; ! 1022: /* read only is the default for safety */ ! 1023: bs->read_only = 1; ! 1024: s->qcow = s->write_target = NULL; ! 1025: s->qcow_filename = NULL; ! 1026: s->fat2 = NULL; ! 1027: s->downcase_short_names = 1; ! 1028: ! 1029: if (!strstart(dirname, "fat:", NULL)) ! 1030: return -1; ! 1031: ! 1032: if (strstr(dirname, ":floppy:")) { ! 1033: floppy = 1; ! 1034: s->fat_type = 12; ! 1035: s->first_sectors_number = 1; ! 1036: s->sectors_per_cluster=2; ! 1037: bs->cyls = 80; bs->heads = 2; bs->secs = 36; ! 1038: } ! 1039: ! 1040: s->sector_count=bs->cyls*bs->heads*bs->secs; ! 1041: ! 1042: if (strstr(dirname, ":32:")) { ! 1043: fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n"); ! 1044: s->fat_type = 32; ! 1045: } else if (strstr(dirname, ":16:")) { ! 1046: s->fat_type = 16; ! 1047: } else if (strstr(dirname, ":12:")) { ! 1048: s->fat_type = 12; ! 1049: s->sector_count=2880; ! 1050: } ! 1051: ! 1052: if (strstr(dirname, ":rw:")) { ! 1053: if (enable_write_target(s)) ! 1054: return -1; ! 1055: bs->read_only = 0; ! 1056: } ! 1057: ! 1058: i = strrchr(dirname, ':') - dirname; ! 1059: assert(i >= 3); ! 1060: if (dirname[i-2] == ':' && qemu_isalpha(dirname[i-1])) ! 1061: /* workaround for DOS drive names */ ! 1062: dirname += i-1; ! 1063: else ! 1064: dirname += i+1; ! 1065: ! 1066: bs->total_sectors=bs->cyls*bs->heads*bs->secs; ! 1067: ! 1068: if(init_directories(s, dirname)) ! 1069: return -1; ! 1070: ! 1071: s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count; ! 1072: ! 1073: if(s->first_sectors_number==0x40) ! 1074: init_mbr(s); ! 1075: ! 1076: /* for some reason or other, MS-DOS does not like to know about CHS... */ ! 1077: if (floppy) ! 1078: bs->heads = bs->cyls = bs->secs = 0; ! 1079: ! 1080: // assert(is_consistent(s)); ! 1081: return 0; ! 1082: } ! 1083: ! 1084: static inline void vvfat_close_current_file(BDRVVVFATState *s) ! 1085: { ! 1086: if(s->current_mapping) { ! 1087: s->current_mapping = NULL; ! 1088: if (s->current_fd) { ! 1089: close(s->current_fd); ! 1090: s->current_fd = 0; ! 1091: } ! 1092: } ! 1093: s->current_cluster = -1; ! 1094: } ! 1095: ! 1096: /* mappings between index1 and index2-1 are supposed to be ordered ! 1097: * return value is the index of the last mapping for which end>cluster_num ! 1098: */ ! 1099: static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2) ! 1100: { ! 1101: int index3=index1+1; ! 1102: while(1) { ! 1103: mapping_t* mapping; ! 1104: index3=(index1+index2)/2; ! 1105: mapping=array_get(&(s->mapping),index3); ! 1106: assert(mapping->begin < mapping->end); ! 1107: if(mapping->begin>=cluster_num) { ! 1108: assert(index2!=index3 || index2==0); ! 1109: if(index2==index3) ! 1110: return index1; ! 1111: index2=index3; ! 1112: } else { ! 1113: if(index1==index3) ! 1114: return mapping->end<=cluster_num ? index2 : index1; ! 1115: index1=index3; ! 1116: } ! 1117: assert(index1<=index2); ! 1118: DLOG(mapping=array_get(&(s->mapping),index1); ! 1119: assert(mapping->begin<=cluster_num); ! 1120: assert(index2 >= s->mapping.next || ! 1121: ((mapping = array_get(&(s->mapping),index2)) && ! 1122: mapping->end>cluster_num))); ! 1123: } ! 1124: } ! 1125: ! 1126: static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num) ! 1127: { ! 1128: int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next); ! 1129: mapping_t* mapping; ! 1130: if(index>=s->mapping.next) ! 1131: return NULL; ! 1132: mapping=array_get(&(s->mapping),index); ! 1133: if(mapping->begin>cluster_num) ! 1134: return NULL; ! 1135: assert(mapping->begin<=cluster_num && mapping->end>cluster_num); ! 1136: return mapping; ! 1137: } ! 1138: ! 1139: /* ! 1140: * This function simply compares path == mapping->path. Since the mappings ! 1141: * are sorted by cluster, this is expensive: O(n). ! 1142: */ ! 1143: static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s, ! 1144: const char* path) ! 1145: { ! 1146: int i; ! 1147: ! 1148: for (i = 0; i < s->mapping.next; i++) { ! 1149: mapping_t* mapping = array_get(&(s->mapping), i); ! 1150: if (mapping->first_mapping_index < 0 && ! 1151: !strcmp(path, mapping->path)) ! 1152: return mapping; ! 1153: } ! 1154: ! 1155: return NULL; ! 1156: } ! 1157: ! 1158: static int open_file(BDRVVVFATState* s,mapping_t* mapping) ! 1159: { ! 1160: if(!mapping) ! 1161: return -1; ! 1162: if(!s->current_mapping || ! 1163: strcmp(s->current_mapping->path,mapping->path)) { ! 1164: /* open file */ ! 1165: int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE); ! 1166: if(fd<0) ! 1167: return -1; ! 1168: vvfat_close_current_file(s); ! 1169: s->current_fd = fd; ! 1170: s->current_mapping = mapping; ! 1171: } ! 1172: return 0; ! 1173: } ! 1174: ! 1175: static inline int read_cluster(BDRVVVFATState *s,int cluster_num) ! 1176: { ! 1177: if(s->current_cluster != cluster_num) { ! 1178: int result=0; ! 1179: off_t offset; ! 1180: assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY)); ! 1181: if(!s->current_mapping ! 1182: || s->current_mapping->begin>cluster_num ! 1183: || s->current_mapping->end<=cluster_num) { ! 1184: /* binary search of mappings for file */ ! 1185: mapping_t* mapping=find_mapping_for_cluster(s,cluster_num); ! 1186: ! 1187: assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end)); ! 1188: ! 1189: if (mapping && mapping->mode & MODE_DIRECTORY) { ! 1190: vvfat_close_current_file(s); ! 1191: s->current_mapping = mapping; ! 1192: read_cluster_directory: ! 1193: offset = s->cluster_size*(cluster_num-s->current_mapping->begin); ! 1194: s->cluster = (unsigned char*)s->directory.pointer+offset ! 1195: + 0x20*s->current_mapping->info.dir.first_dir_index; ! 1196: assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0); ! 1197: assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size); ! 1198: s->current_cluster = cluster_num; ! 1199: return 0; ! 1200: } ! 1201: ! 1202: if(open_file(s,mapping)) ! 1203: return -2; ! 1204: } else if (s->current_mapping->mode & MODE_DIRECTORY) ! 1205: goto read_cluster_directory; ! 1206: ! 1207: assert(s->current_fd); ! 1208: ! 1209: offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset; ! 1210: if(lseek(s->current_fd, offset, SEEK_SET)!=offset) ! 1211: return -3; ! 1212: s->cluster=s->cluster_buffer; ! 1213: result=read(s->current_fd,s->cluster,s->cluster_size); ! 1214: if(result<0) { ! 1215: s->current_cluster = -1; ! 1216: return -1; ! 1217: } ! 1218: s->current_cluster = cluster_num; ! 1219: } ! 1220: return 0; ! 1221: } ! 1222: ! 1223: #ifdef DEBUG ! 1224: static void hexdump(const void* address, uint32_t len) ! 1225: { ! 1226: const unsigned char* p = address; ! 1227: int i, j; ! 1228: ! 1229: for (i = 0; i < len; i += 16) { ! 1230: for (j = 0; j < 16 && i + j < len; j++) ! 1231: fprintf(stderr, "%02x ", p[i + j]); ! 1232: for (; j < 16; j++) ! 1233: fprintf(stderr, " "); ! 1234: fprintf(stderr, " "); ! 1235: for (j = 0; j < 16 && i + j < len; j++) ! 1236: fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]); ! 1237: fprintf(stderr, "\n"); ! 1238: } ! 1239: } ! 1240: ! 1241: static void print_direntry(const direntry_t* direntry) ! 1242: { ! 1243: int j = 0; ! 1244: char buffer[1024]; ! 1245: ! 1246: fprintf(stderr, "direntry 0x%x: ", (int)direntry); ! 1247: if(!direntry) ! 1248: return; ! 1249: if(is_long_name(direntry)) { ! 1250: unsigned char* c=(unsigned char*)direntry; ! 1251: int i; ! 1252: for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2) ! 1253: #define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;} ! 1254: ADD_CHAR(c[i]); ! 1255: for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2) ! 1256: ADD_CHAR(c[i]); ! 1257: for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2) ! 1258: ADD_CHAR(c[i]); ! 1259: buffer[j] = 0; ! 1260: fprintf(stderr, "%s\n", buffer); ! 1261: } else { ! 1262: int i; ! 1263: for(i=0;i<11;i++) ! 1264: ADD_CHAR(direntry->name[i]); ! 1265: buffer[j] = 0; ! 1266: fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n", ! 1267: buffer, ! 1268: direntry->attributes, ! 1269: begin_of_direntry(direntry),le32_to_cpu(direntry->size)); ! 1270: } ! 1271: } ! 1272: ! 1273: static void print_mapping(const mapping_t* mapping) ! 1274: { ! 1275: fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode); ! 1276: if (mapping->mode & MODE_DIRECTORY) ! 1277: fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index); ! 1278: else ! 1279: fprintf(stderr, "offset = %d\n", mapping->info.file.offset); ! 1280: } ! 1281: #endif ! 1282: ! 1283: static int vvfat_read(BlockDriverState *bs, int64_t sector_num, ! 1284: uint8_t *buf, int nb_sectors) ! 1285: { ! 1286: BDRVVVFATState *s = bs->opaque; ! 1287: int i; ! 1288: ! 1289: for(i=0;i<nb_sectors;i++,sector_num++) { ! 1290: if (sector_num >= s->sector_count) ! 1291: return -1; ! 1292: if (s->qcow) { ! 1293: int n; ! 1294: if (s->qcow->drv->bdrv_is_allocated(s->qcow, ! 1295: sector_num, nb_sectors-i, &n)) { ! 1296: DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n)); ! 1297: if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n)) ! 1298: return -1; ! 1299: i += n - 1; ! 1300: sector_num += n - 1; ! 1301: continue; ! 1302: } ! 1303: DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num)); ! 1304: } ! 1305: if(sector_num<s->faked_sectors) { ! 1306: if(sector_num<s->first_sectors_number) ! 1307: memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200); ! 1308: else if(sector_num-s->first_sectors_number<s->sectors_per_fat) ! 1309: memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200); ! 1310: else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat) ! 1311: memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200); ! 1312: } else { ! 1313: uint32_t sector=sector_num-s->faked_sectors, ! 1314: sector_offset_in_cluster=(sector%s->sectors_per_cluster), ! 1315: cluster_num=sector/s->sectors_per_cluster; ! 1316: if(read_cluster(s, cluster_num) != 0) { ! 1317: /* LATER TODO: strict: return -1; */ ! 1318: memset(buf+i*0x200,0,0x200); ! 1319: continue; ! 1320: } ! 1321: memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200); ! 1322: } ! 1323: } ! 1324: return 0; ! 1325: } ! 1326: ! 1327: /* LATER TODO: statify all functions */ ! 1328: ! 1329: /* ! 1330: * Idea of the write support (use snapshot): ! 1331: * ! 1332: * 1. check if all data is consistent, recording renames, modifications, ! 1333: * new files and directories (in s->commits). ! 1334: * ! 1335: * 2. if the data is not consistent, stop committing ! 1336: * ! 1337: * 3. handle renames, and create new files and directories (do not yet ! 1338: * write their contents) ! 1339: * ! 1340: * 4. walk the directories, fixing the mapping and direntries, and marking ! 1341: * the handled mappings as not deleted ! 1342: * ! 1343: * 5. commit the contents of the files ! 1344: * ! 1345: * 6. handle deleted files and directories ! 1346: * ! 1347: */ ! 1348: ! 1349: typedef struct commit_t { ! 1350: char* path; ! 1351: union { ! 1352: struct { uint32_t cluster; } rename; ! 1353: struct { int dir_index; uint32_t modified_offset; } writeout; ! 1354: struct { uint32_t first_cluster; } new_file; ! 1355: struct { uint32_t cluster; } mkdir; ! 1356: } param; ! 1357: /* DELETEs and RMDIRs are handled differently: see handle_deletes() */ ! 1358: enum { ! 1359: ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR ! 1360: } action; ! 1361: } commit_t; ! 1362: ! 1363: static void clear_commits(BDRVVVFATState* s) ! 1364: { ! 1365: int i; ! 1366: DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next)); ! 1367: for (i = 0; i < s->commits.next; i++) { ! 1368: commit_t* commit = array_get(&(s->commits), i); ! 1369: assert(commit->path || commit->action == ACTION_WRITEOUT); ! 1370: if (commit->action != ACTION_WRITEOUT) { ! 1371: assert(commit->path); ! 1372: free(commit->path); ! 1373: } else ! 1374: assert(commit->path == NULL); ! 1375: } ! 1376: s->commits.next = 0; ! 1377: } ! 1378: ! 1379: static void schedule_rename(BDRVVVFATState* s, ! 1380: uint32_t cluster, char* new_path) ! 1381: { ! 1382: commit_t* commit = array_get_next(&(s->commits)); ! 1383: commit->path = new_path; ! 1384: commit->param.rename.cluster = cluster; ! 1385: commit->action = ACTION_RENAME; ! 1386: } ! 1387: ! 1388: static void schedule_writeout(BDRVVVFATState* s, ! 1389: int dir_index, uint32_t modified_offset) ! 1390: { ! 1391: commit_t* commit = array_get_next(&(s->commits)); ! 1392: commit->path = NULL; ! 1393: commit->param.writeout.dir_index = dir_index; ! 1394: commit->param.writeout.modified_offset = modified_offset; ! 1395: commit->action = ACTION_WRITEOUT; ! 1396: } ! 1397: ! 1398: static void schedule_new_file(BDRVVVFATState* s, ! 1399: char* path, uint32_t first_cluster) ! 1400: { ! 1401: commit_t* commit = array_get_next(&(s->commits)); ! 1402: commit->path = path; ! 1403: commit->param.new_file.first_cluster = first_cluster; ! 1404: commit->action = ACTION_NEW_FILE; ! 1405: } ! 1406: ! 1407: static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path) ! 1408: { ! 1409: commit_t* commit = array_get_next(&(s->commits)); ! 1410: commit->path = path; ! 1411: commit->param.mkdir.cluster = cluster; ! 1412: commit->action = ACTION_MKDIR; ! 1413: } ! 1414: ! 1415: typedef struct { ! 1416: /* ! 1417: * Since the sequence number is at most 0x3f, and the filename ! 1418: * length is at most 13 times the sequence number, the maximal ! 1419: * filename length is 0x3f * 13 bytes. ! 1420: */ ! 1421: unsigned char name[0x3f * 13 + 1]; ! 1422: int checksum, len; ! 1423: int sequence_number; ! 1424: } long_file_name; ! 1425: ! 1426: static void lfn_init(long_file_name* lfn) ! 1427: { ! 1428: lfn->sequence_number = lfn->len = 0; ! 1429: lfn->checksum = 0x100; ! 1430: } ! 1431: ! 1432: /* return 0 if parsed successfully, > 0 if no long name, < 0 if error */ ! 1433: static int parse_long_name(long_file_name* lfn, ! 1434: const direntry_t* direntry) ! 1435: { ! 1436: int i, j, offset; ! 1437: const unsigned char* pointer = (const unsigned char*)direntry; ! 1438: ! 1439: if (!is_long_name(direntry)) ! 1440: return 1; ! 1441: ! 1442: if (pointer[0] & 0x40) { ! 1443: lfn->sequence_number = pointer[0] & 0x3f; ! 1444: lfn->checksum = pointer[13]; ! 1445: lfn->name[0] = 0; ! 1446: lfn->name[lfn->sequence_number * 13] = 0; ! 1447: } else if ((pointer[0] & 0x3f) != --lfn->sequence_number) ! 1448: return -1; ! 1449: else if (pointer[13] != lfn->checksum) ! 1450: return -2; ! 1451: else if (pointer[12] || pointer[26] || pointer[27]) ! 1452: return -3; ! 1453: ! 1454: offset = 13 * (lfn->sequence_number - 1); ! 1455: for (i = 0, j = 1; i < 13; i++, j+=2) { ! 1456: if (j == 11) ! 1457: j = 14; ! 1458: else if (j == 26) ! 1459: j = 28; ! 1460: ! 1461: if (pointer[j+1] == 0) ! 1462: lfn->name[offset + i] = pointer[j]; ! 1463: else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0) ! 1464: return -4; ! 1465: else ! 1466: lfn->name[offset + i] = 0; ! 1467: } ! 1468: ! 1469: if (pointer[0] & 0x40) ! 1470: lfn->len = offset + strlen((char*)lfn->name + offset); ! 1471: ! 1472: return 0; ! 1473: } ! 1474: ! 1475: /* returns 0 if successful, >0 if no short_name, and <0 on error */ ! 1476: static int parse_short_name(BDRVVVFATState* s, ! 1477: long_file_name* lfn, direntry_t* direntry) ! 1478: { ! 1479: int i, j; ! 1480: ! 1481: if (!is_short_name(direntry)) ! 1482: return 1; ! 1483: ! 1484: for (j = 7; j >= 0 && direntry->name[j] == ' '; j--); ! 1485: for (i = 0; i <= j; i++) { ! 1486: if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f) ! 1487: return -1; ! 1488: else if (s->downcase_short_names) ! 1489: lfn->name[i] = qemu_tolower(direntry->name[i]); ! 1490: else ! 1491: lfn->name[i] = direntry->name[i]; ! 1492: } ! 1493: ! 1494: for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--); ! 1495: if (j >= 0) { ! 1496: lfn->name[i++] = '.'; ! 1497: lfn->name[i + j + 1] = '\0'; ! 1498: for (;j >= 0; j--) { ! 1499: if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f) ! 1500: return -2; ! 1501: else if (s->downcase_short_names) ! 1502: lfn->name[i + j] = qemu_tolower(direntry->extension[j]); ! 1503: else ! 1504: lfn->name[i + j] = direntry->extension[j]; ! 1505: } ! 1506: } else ! 1507: lfn->name[i + j + 1] = '\0'; ! 1508: ! 1509: lfn->len = strlen((char*)lfn->name); ! 1510: ! 1511: return 0; ! 1512: } ! 1513: ! 1514: static inline uint32_t modified_fat_get(BDRVVVFATState* s, ! 1515: unsigned int cluster) ! 1516: { ! 1517: if (cluster < s->last_cluster_of_root_directory) { ! 1518: if (cluster + 1 == s->last_cluster_of_root_directory) ! 1519: return s->max_fat_value; ! 1520: else ! 1521: return cluster + 1; ! 1522: } ! 1523: ! 1524: if (s->fat_type==32) { ! 1525: uint32_t* entry=((uint32_t*)s->fat2)+cluster; ! 1526: return le32_to_cpu(*entry); ! 1527: } else if (s->fat_type==16) { ! 1528: uint16_t* entry=((uint16_t*)s->fat2)+cluster; ! 1529: return le16_to_cpu(*entry); ! 1530: } else { ! 1531: const uint8_t* x=s->fat2+cluster*3/2; ! 1532: return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff; ! 1533: } ! 1534: } ! 1535: ! 1536: static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num) ! 1537: { ! 1538: int was_modified = 0; ! 1539: int i, dummy; ! 1540: ! 1541: if (s->qcow == NULL) ! 1542: return 0; ! 1543: ! 1544: for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) ! 1545: was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow, ! 1546: cluster2sector(s, cluster_num) + i, 1, &dummy); ! 1547: ! 1548: return was_modified; ! 1549: } ! 1550: ! 1551: static const char* get_basename(const char* path) ! 1552: { ! 1553: char* basename = strrchr(path, '/'); ! 1554: if (basename == NULL) ! 1555: return path; ! 1556: else ! 1557: return basename + 1; /* strip '/' */ ! 1558: } ! 1559: ! 1560: /* ! 1561: * The array s->used_clusters holds the states of the clusters. If it is ! 1562: * part of a file, it has bit 2 set, in case of a directory, bit 1. If it ! 1563: * was modified, bit 3 is set. ! 1564: * If any cluster is allocated, but not part of a file or directory, this ! 1565: * driver refuses to commit. ! 1566: */ ! 1567: typedef enum { ! 1568: USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4 ! 1569: } used_t; ! 1570: ! 1571: /* ! 1572: * get_cluster_count_for_direntry() not only determines how many clusters ! 1573: * are occupied by direntry, but also if it was renamed or modified. ! 1574: * ! 1575: * A file is thought to be renamed *only* if there already was a file with ! 1576: * exactly the same first cluster, but a different name. ! 1577: * ! 1578: * Further, the files/directories handled by this function are ! 1579: * assumed to be *not* deleted (and *only* those). ! 1580: */ ! 1581: static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, ! 1582: direntry_t* direntry, const char* path) ! 1583: { ! 1584: /* ! 1585: * This is a little bit tricky: ! 1586: * IF the guest OS just inserts a cluster into the file chain, ! 1587: * and leaves the rest alone, (i.e. the original file had clusters ! 1588: * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens: ! 1589: * ! 1590: * - do_commit will write the cluster into the file at the given ! 1591: * offset, but ! 1592: * ! 1593: * - the cluster which is overwritten should be moved to a later ! 1594: * position in the file. ! 1595: * ! 1596: * I am not aware that any OS does something as braindead, but this ! 1597: * situation could happen anyway when not committing for a long time. ! 1598: * Just to be sure that this does not bite us, detect it, and copy the ! 1599: * contents of the clusters to-be-overwritten into the qcow. ! 1600: */ ! 1601: int copy_it = 0; ! 1602: int was_modified = 0; ! 1603: int32_t ret = 0; ! 1604: ! 1605: uint32_t cluster_num = begin_of_direntry(direntry); ! 1606: uint32_t offset = 0; ! 1607: int first_mapping_index = -1; ! 1608: mapping_t* mapping = NULL; ! 1609: const char* basename2 = NULL; ! 1610: ! 1611: vvfat_close_current_file(s); ! 1612: ! 1613: /* the root directory */ ! 1614: if (cluster_num == 0) ! 1615: return 0; ! 1616: ! 1617: /* write support */ ! 1618: if (s->qcow) { ! 1619: basename2 = get_basename(path); ! 1620: ! 1621: mapping = find_mapping_for_cluster(s, cluster_num); ! 1622: ! 1623: if (mapping) { ! 1624: const char* basename; ! 1625: ! 1626: assert(mapping->mode & MODE_DELETED); ! 1627: mapping->mode &= ~MODE_DELETED; ! 1628: ! 1629: basename = get_basename(mapping->path); ! 1630: ! 1631: assert(mapping->mode & MODE_NORMAL); ! 1632: ! 1633: /* rename */ ! 1634: if (strcmp(basename, basename2)) ! 1635: schedule_rename(s, cluster_num, strdup(path)); ! 1636: } else if (is_file(direntry)) ! 1637: /* new file */ ! 1638: schedule_new_file(s, strdup(path), cluster_num); ! 1639: else { ! 1640: assert(0); ! 1641: return 0; ! 1642: } ! 1643: } ! 1644: ! 1645: while(1) { ! 1646: if (s->qcow) { ! 1647: if (!copy_it && cluster_was_modified(s, cluster_num)) { ! 1648: if (mapping == NULL || ! 1649: mapping->begin > cluster_num || ! 1650: mapping->end <= cluster_num) ! 1651: mapping = find_mapping_for_cluster(s, cluster_num); ! 1652: ! 1653: ! 1654: if (mapping && ! 1655: (mapping->mode & MODE_DIRECTORY) == 0) { ! 1656: ! 1657: /* was modified in qcow */ ! 1658: if (offset != mapping->info.file.offset + s->cluster_size ! 1659: * (cluster_num - mapping->begin)) { ! 1660: /* offset of this cluster in file chain has changed */ ! 1661: assert(0); ! 1662: copy_it = 1; ! 1663: } else if (offset == 0) { ! 1664: const char* basename = get_basename(mapping->path); ! 1665: ! 1666: if (strcmp(basename, basename2)) ! 1667: copy_it = 1; ! 1668: first_mapping_index = array_index(&(s->mapping), mapping); ! 1669: } ! 1670: ! 1671: if (mapping->first_mapping_index != first_mapping_index ! 1672: && mapping->info.file.offset > 0) { ! 1673: assert(0); ! 1674: copy_it = 1; ! 1675: } ! 1676: ! 1677: /* need to write out? */ ! 1678: if (!was_modified && is_file(direntry)) { ! 1679: was_modified = 1; ! 1680: schedule_writeout(s, mapping->dir_index, offset); ! 1681: } ! 1682: } ! 1683: } ! 1684: ! 1685: if (copy_it) { ! 1686: int i, dummy; ! 1687: /* ! 1688: * This is horribly inefficient, but that is okay, since ! 1689: * it is rarely executed, if at all. ! 1690: */ ! 1691: int64_t offset = cluster2sector(s, cluster_num); ! 1692: ! 1693: vvfat_close_current_file(s); ! 1694: for (i = 0; i < s->sectors_per_cluster; i++) ! 1695: if (!s->qcow->drv->bdrv_is_allocated(s->qcow, ! 1696: offset + i, 1, &dummy)) { ! 1697: if (vvfat_read(s->bs, ! 1698: offset, s->cluster_buffer, 1)) ! 1699: return -1; ! 1700: if (s->qcow->drv->bdrv_write(s->qcow, ! 1701: offset, s->cluster_buffer, 1)) ! 1702: return -2; ! 1703: } ! 1704: } ! 1705: } ! 1706: ! 1707: ret++; ! 1708: if (s->used_clusters[cluster_num] & USED_ANY) ! 1709: return 0; ! 1710: s->used_clusters[cluster_num] = USED_FILE; ! 1711: ! 1712: cluster_num = modified_fat_get(s, cluster_num); ! 1713: ! 1714: if (fat_eof(s, cluster_num)) ! 1715: return ret; ! 1716: else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16) ! 1717: return -1; ! 1718: ! 1719: offset += s->cluster_size; ! 1720: } ! 1721: } ! 1722: ! 1723: /* ! 1724: * This function looks at the modified data (qcow). ! 1725: * It returns 0 upon inconsistency or error, and the number of clusters ! 1726: * used by the directory, its subdirectories and their files. ! 1727: */ ! 1728: static int check_directory_consistency(BDRVVVFATState *s, ! 1729: int cluster_num, const char* path) ! 1730: { ! 1731: int ret = 0; ! 1732: unsigned char* cluster = qemu_malloc(s->cluster_size); ! 1733: direntry_t* direntries = (direntry_t*)cluster; ! 1734: mapping_t* mapping = find_mapping_for_cluster(s, cluster_num); ! 1735: ! 1736: long_file_name lfn; ! 1737: int path_len = strlen(path); ! 1738: char path2[PATH_MAX]; ! 1739: ! 1740: assert(path_len < PATH_MAX); /* len was tested before! */ ! 1741: pstrcpy(path2, sizeof(path2), path); ! 1742: path2[path_len] = '/'; ! 1743: path2[path_len + 1] = '\0'; ! 1744: ! 1745: if (mapping) { ! 1746: const char* basename = get_basename(mapping->path); ! 1747: const char* basename2 = get_basename(path); ! 1748: ! 1749: assert(mapping->mode & MODE_DIRECTORY); ! 1750: ! 1751: assert(mapping->mode & MODE_DELETED); ! 1752: mapping->mode &= ~MODE_DELETED; ! 1753: ! 1754: if (strcmp(basename, basename2)) ! 1755: schedule_rename(s, cluster_num, strdup(path)); ! 1756: } else ! 1757: /* new directory */ ! 1758: schedule_mkdir(s, cluster_num, strdup(path)); ! 1759: ! 1760: lfn_init(&lfn); ! 1761: do { ! 1762: int i; ! 1763: int subret = 0; ! 1764: ! 1765: ret++; ! 1766: ! 1767: if (s->used_clusters[cluster_num] & USED_ANY) { ! 1768: fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num); ! 1769: return 0; ! 1770: } ! 1771: s->used_clusters[cluster_num] = USED_DIRECTORY; ! 1772: ! 1773: DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num))); ! 1774: subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster, ! 1775: s->sectors_per_cluster); ! 1776: if (subret) { ! 1777: fprintf(stderr, "Error fetching direntries\n"); ! 1778: fail: ! 1779: free(cluster); ! 1780: return 0; ! 1781: } ! 1782: ! 1783: for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) { ! 1784: int cluster_count = 0; ! 1785: ! 1786: DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i)); ! 1787: if (is_volume_label(direntries + i) || is_dot(direntries + i) || ! 1788: is_free(direntries + i)) ! 1789: continue; ! 1790: ! 1791: subret = parse_long_name(&lfn, direntries + i); ! 1792: if (subret < 0) { ! 1793: fprintf(stderr, "Error in long name\n"); ! 1794: goto fail; ! 1795: } ! 1796: if (subret == 0 || is_free(direntries + i)) ! 1797: continue; ! 1798: ! 1799: if (fat_chksum(direntries+i) != lfn.checksum) { ! 1800: subret = parse_short_name(s, &lfn, direntries + i); ! 1801: if (subret < 0) { ! 1802: fprintf(stderr, "Error in short name (%d)\n", subret); ! 1803: goto fail; ! 1804: } ! 1805: if (subret > 0 || !strcmp((char*)lfn.name, ".") ! 1806: || !strcmp((char*)lfn.name, "..")) ! 1807: continue; ! 1808: } ! 1809: lfn.checksum = 0x100; /* cannot use long name twice */ ! 1810: ! 1811: if (path_len + 1 + lfn.len >= PATH_MAX) { ! 1812: fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name); ! 1813: goto fail; ! 1814: } ! 1815: pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1, ! 1816: (char*)lfn.name); ! 1817: ! 1818: if (is_directory(direntries + i)) { ! 1819: if (begin_of_direntry(direntries + i) == 0) { ! 1820: DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i)); ! 1821: goto fail; ! 1822: } ! 1823: cluster_count = check_directory_consistency(s, ! 1824: begin_of_direntry(direntries + i), path2); ! 1825: if (cluster_count == 0) { ! 1826: DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i)); ! 1827: goto fail; ! 1828: } ! 1829: } else if (is_file(direntries + i)) { ! 1830: /* check file size with FAT */ ! 1831: cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2); ! 1832: if (cluster_count != ! 1833: (le32_to_cpu(direntries[i].size) + s->cluster_size ! 1834: - 1) / s->cluster_size) { ! 1835: DLOG(fprintf(stderr, "Cluster count mismatch\n")); ! 1836: goto fail; ! 1837: } ! 1838: } else ! 1839: assert(0); /* cluster_count = 0; */ ! 1840: ! 1841: ret += cluster_count; ! 1842: } ! 1843: ! 1844: cluster_num = modified_fat_get(s, cluster_num); ! 1845: } while(!fat_eof(s, cluster_num)); ! 1846: ! 1847: free(cluster); ! 1848: return ret; ! 1849: } ! 1850: ! 1851: /* returns 1 on success */ ! 1852: static int is_consistent(BDRVVVFATState* s) ! 1853: { ! 1854: int i, check; ! 1855: int used_clusters_count = 0; ! 1856: ! 1857: DLOG(checkpoint()); ! 1858: /* ! 1859: * - get modified FAT ! 1860: * - compare the two FATs (TODO) ! 1861: * - get buffer for marking used clusters ! 1862: * - recurse direntries from root (using bs->bdrv_read to make ! 1863: * sure to get the new data) ! 1864: * - check that the FAT agrees with the size ! 1865: * - count the number of clusters occupied by this directory and ! 1866: * its files ! 1867: * - check that the cumulative used cluster count agrees with the ! 1868: * FAT ! 1869: * - if all is fine, return number of used clusters ! 1870: */ ! 1871: if (s->fat2 == NULL) { ! 1872: int size = 0x200 * s->sectors_per_fat; ! 1873: s->fat2 = qemu_malloc(size); ! 1874: memcpy(s->fat2, s->fat.pointer, size); ! 1875: } ! 1876: check = vvfat_read(s->bs, ! 1877: s->first_sectors_number, s->fat2, s->sectors_per_fat); ! 1878: if (check) { ! 1879: fprintf(stderr, "Could not copy fat\n"); ! 1880: return 0; ! 1881: } ! 1882: assert (s->used_clusters); ! 1883: for (i = 0; i < sector2cluster(s, s->sector_count); i++) ! 1884: s->used_clusters[i] &= ~USED_ANY; ! 1885: ! 1886: clear_commits(s); ! 1887: ! 1888: /* mark every mapped file/directory as deleted. ! 1889: * (check_directory_consistency() will unmark those still present). */ ! 1890: if (s->qcow) ! 1891: for (i = 0; i < s->mapping.next; i++) { ! 1892: mapping_t* mapping = array_get(&(s->mapping), i); ! 1893: if (mapping->first_mapping_index < 0) ! 1894: mapping->mode |= MODE_DELETED; ! 1895: } ! 1896: ! 1897: used_clusters_count = check_directory_consistency(s, 0, s->path); ! 1898: if (used_clusters_count <= 0) { ! 1899: DLOG(fprintf(stderr, "problem in directory\n")); ! 1900: return 0; ! 1901: } ! 1902: ! 1903: check = s->last_cluster_of_root_directory; ! 1904: for (i = check; i < sector2cluster(s, s->sector_count); i++) { ! 1905: if (modified_fat_get(s, i)) { ! 1906: if(!s->used_clusters[i]) { ! 1907: DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i)); ! 1908: return 0; ! 1909: } ! 1910: check++; ! 1911: } ! 1912: ! 1913: if (s->used_clusters[i] == USED_ALLOCATED) { ! 1914: /* allocated, but not used... */ ! 1915: DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i)); ! 1916: return 0; ! 1917: } ! 1918: } ! 1919: ! 1920: if (check != used_clusters_count) ! 1921: return 0; ! 1922: ! 1923: return used_clusters_count; ! 1924: } ! 1925: ! 1926: static inline void adjust_mapping_indices(BDRVVVFATState* s, ! 1927: int offset, int adjust) ! 1928: { ! 1929: int i; ! 1930: ! 1931: for (i = 0; i < s->mapping.next; i++) { ! 1932: mapping_t* mapping = array_get(&(s->mapping), i); ! 1933: ! 1934: #define ADJUST_MAPPING_INDEX(name) \ ! 1935: if (mapping->name >= offset) \ ! 1936: mapping->name += adjust ! 1937: ! 1938: ADJUST_MAPPING_INDEX(first_mapping_index); ! 1939: if (mapping->mode & MODE_DIRECTORY) ! 1940: ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index); ! 1941: } ! 1942: } ! 1943: ! 1944: /* insert or update mapping */ ! 1945: static mapping_t* insert_mapping(BDRVVVFATState* s, ! 1946: uint32_t begin, uint32_t end) ! 1947: { ! 1948: /* ! 1949: * - find mapping where mapping->begin >= begin, ! 1950: * - if mapping->begin > begin: insert ! 1951: * - adjust all references to mappings! ! 1952: * - else: adjust ! 1953: * - replace name ! 1954: */ ! 1955: int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next); ! 1956: mapping_t* mapping = NULL; ! 1957: mapping_t* first_mapping = array_get(&(s->mapping), 0); ! 1958: ! 1959: if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index)) ! 1960: && mapping->begin < begin) { ! 1961: mapping->end = begin; ! 1962: index++; ! 1963: mapping = array_get(&(s->mapping), index); ! 1964: } ! 1965: if (index >= s->mapping.next || mapping->begin > begin) { ! 1966: mapping = array_insert(&(s->mapping), index, 1); ! 1967: mapping->path = NULL; ! 1968: adjust_mapping_indices(s, index, +1); ! 1969: } ! 1970: ! 1971: mapping->begin = begin; ! 1972: mapping->end = end; ! 1973: ! 1974: DLOG(mapping_t* next_mapping; ! 1975: assert(index + 1 >= s->mapping.next || ! 1976: ((next_mapping = array_get(&(s->mapping), index + 1)) && ! 1977: next_mapping->begin >= end))); ! 1978: ! 1979: if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer) ! 1980: s->current_mapping = array_get(&(s->mapping), ! 1981: s->current_mapping - first_mapping); ! 1982: ! 1983: return mapping; ! 1984: } ! 1985: ! 1986: static int remove_mapping(BDRVVVFATState* s, int mapping_index) ! 1987: { ! 1988: mapping_t* mapping = array_get(&(s->mapping), mapping_index); ! 1989: mapping_t* first_mapping = array_get(&(s->mapping), 0); ! 1990: ! 1991: /* free mapping */ ! 1992: if (mapping->first_mapping_index < 0) ! 1993: free(mapping->path); ! 1994: ! 1995: /* remove from s->mapping */ ! 1996: array_remove(&(s->mapping), mapping_index); ! 1997: ! 1998: /* adjust all references to mappings */ ! 1999: adjust_mapping_indices(s, mapping_index, -1); ! 2000: ! 2001: if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer) ! 2002: s->current_mapping = array_get(&(s->mapping), ! 2003: s->current_mapping - first_mapping); ! 2004: ! 2005: return 0; ! 2006: } ! 2007: ! 2008: static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust) ! 2009: { ! 2010: int i; ! 2011: for (i = 0; i < s->mapping.next; i++) { ! 2012: mapping_t* mapping = array_get(&(s->mapping), i); ! 2013: if (mapping->dir_index >= offset) ! 2014: mapping->dir_index += adjust; ! 2015: if ((mapping->mode & MODE_DIRECTORY) && ! 2016: mapping->info.dir.first_dir_index >= offset) ! 2017: mapping->info.dir.first_dir_index += adjust; ! 2018: } ! 2019: } ! 2020: ! 2021: static direntry_t* insert_direntries(BDRVVVFATState* s, ! 2022: int dir_index, int count) ! 2023: { ! 2024: /* ! 2025: * make room in s->directory, ! 2026: * adjust_dirindices ! 2027: */ ! 2028: direntry_t* result = array_insert(&(s->directory), dir_index, count); ! 2029: if (result == NULL) ! 2030: return NULL; ! 2031: adjust_dirindices(s, dir_index, count); ! 2032: return result; ! 2033: } ! 2034: ! 2035: static int remove_direntries(BDRVVVFATState* s, int dir_index, int count) ! 2036: { ! 2037: int ret = array_remove_slice(&(s->directory), dir_index, count); ! 2038: if (ret) ! 2039: return ret; ! 2040: adjust_dirindices(s, dir_index, -count); ! 2041: return 0; ! 2042: } ! 2043: ! 2044: /* ! 2045: * Adapt the mappings of the cluster chain starting at first cluster ! 2046: * (i.e. if a file starts at first_cluster, the chain is followed according ! 2047: * to the modified fat, and the corresponding entries in s->mapping are ! 2048: * adjusted) ! 2049: */ ! 2050: static int commit_mappings(BDRVVVFATState* s, ! 2051: uint32_t first_cluster, int dir_index) ! 2052: { ! 2053: mapping_t* mapping = find_mapping_for_cluster(s, first_cluster); ! 2054: direntry_t* direntry = array_get(&(s->directory), dir_index); ! 2055: uint32_t cluster = first_cluster; ! 2056: ! 2057: vvfat_close_current_file(s); ! 2058: ! 2059: assert(mapping); ! 2060: assert(mapping->begin == first_cluster); ! 2061: mapping->first_mapping_index = -1; ! 2062: mapping->dir_index = dir_index; ! 2063: mapping->mode = (dir_index <= 0 || is_directory(direntry)) ? ! 2064: MODE_DIRECTORY : MODE_NORMAL; ! 2065: ! 2066: while (!fat_eof(s, cluster)) { ! 2067: uint32_t c, c1; ! 2068: ! 2069: for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1; ! 2070: c = c1, c1 = modified_fat_get(s, c1)); ! 2071: ! 2072: c++; ! 2073: if (c > mapping->end) { ! 2074: int index = array_index(&(s->mapping), mapping); ! 2075: int i, max_i = s->mapping.next - index; ! 2076: for (i = 1; i < max_i && mapping[i].begin < c; i++); ! 2077: while (--i > 0) ! 2078: remove_mapping(s, index + 1); ! 2079: } ! 2080: assert(mapping == array_get(&(s->mapping), s->mapping.next - 1) ! 2081: || mapping[1].begin >= c); ! 2082: mapping->end = c; ! 2083: ! 2084: if (!fat_eof(s, c1)) { ! 2085: int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next); ! 2086: mapping_t* next_mapping = i >= s->mapping.next ? NULL : ! 2087: array_get(&(s->mapping), i); ! 2088: ! 2089: if (next_mapping == NULL || next_mapping->begin > c1) { ! 2090: int i1 = array_index(&(s->mapping), mapping); ! 2091: ! 2092: next_mapping = insert_mapping(s, c1, c1+1); ! 2093: ! 2094: if (c1 < c) ! 2095: i1++; ! 2096: mapping = array_get(&(s->mapping), i1); ! 2097: } ! 2098: ! 2099: next_mapping->dir_index = mapping->dir_index; ! 2100: next_mapping->first_mapping_index = ! 2101: mapping->first_mapping_index < 0 ? ! 2102: array_index(&(s->mapping), mapping) : ! 2103: mapping->first_mapping_index; ! 2104: next_mapping->path = mapping->path; ! 2105: next_mapping->mode = mapping->mode; ! 2106: next_mapping->read_only = mapping->read_only; ! 2107: if (mapping->mode & MODE_DIRECTORY) { ! 2108: next_mapping->info.dir.parent_mapping_index = ! 2109: mapping->info.dir.parent_mapping_index; ! 2110: next_mapping->info.dir.first_dir_index = ! 2111: mapping->info.dir.first_dir_index + ! 2112: 0x10 * s->sectors_per_cluster * ! 2113: (mapping->end - mapping->begin); ! 2114: } else ! 2115: next_mapping->info.file.offset = mapping->info.file.offset + ! 2116: mapping->end - mapping->begin; ! 2117: ! 2118: mapping = next_mapping; ! 2119: } ! 2120: ! 2121: cluster = c1; ! 2122: } ! 2123: ! 2124: return 0; ! 2125: } ! 2126: ! 2127: static int commit_direntries(BDRVVVFATState* s, ! 2128: int dir_index, int parent_mapping_index) ! 2129: { ! 2130: direntry_t* direntry = array_get(&(s->directory), dir_index); ! 2131: uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry); ! 2132: mapping_t* mapping = find_mapping_for_cluster(s, first_cluster); ! 2133: ! 2134: int factor = 0x10 * s->sectors_per_cluster; ! 2135: int old_cluster_count, new_cluster_count; ! 2136: int current_dir_index = mapping->info.dir.first_dir_index; ! 2137: int first_dir_index = current_dir_index; ! 2138: int ret, i; ! 2139: uint32_t c; ! 2140: ! 2141: DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index)); ! 2142: ! 2143: assert(direntry); ! 2144: assert(mapping); ! 2145: assert(mapping->begin == first_cluster); ! 2146: assert(mapping->info.dir.first_dir_index < s->directory.next); ! 2147: assert(mapping->mode & MODE_DIRECTORY); ! 2148: assert(dir_index == 0 || is_directory(direntry)); ! 2149: ! 2150: mapping->info.dir.parent_mapping_index = parent_mapping_index; ! 2151: ! 2152: if (first_cluster == 0) { ! 2153: old_cluster_count = new_cluster_count = ! 2154: s->last_cluster_of_root_directory; ! 2155: } else { ! 2156: for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c); ! 2157: c = fat_get(s, c)) ! 2158: old_cluster_count++; ! 2159: ! 2160: for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c); ! 2161: c = modified_fat_get(s, c)) ! 2162: new_cluster_count++; ! 2163: } ! 2164: ! 2165: if (new_cluster_count > old_cluster_count) { ! 2166: if (insert_direntries(s, ! 2167: current_dir_index + factor * old_cluster_count, ! 2168: factor * (new_cluster_count - old_cluster_count)) == NULL) ! 2169: return -1; ! 2170: } else if (new_cluster_count < old_cluster_count) ! 2171: remove_direntries(s, ! 2172: current_dir_index + factor * new_cluster_count, ! 2173: factor * (old_cluster_count - new_cluster_count)); ! 2174: ! 2175: for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) { ! 2176: void* direntry = array_get(&(s->directory), current_dir_index); ! 2177: int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry, ! 2178: s->sectors_per_cluster); ! 2179: if (ret) ! 2180: return ret; ! 2181: assert(!strncmp(s->directory.pointer, "QEMU", 4)); ! 2182: current_dir_index += factor; ! 2183: } ! 2184: ! 2185: ret = commit_mappings(s, first_cluster, dir_index); ! 2186: if (ret) ! 2187: return ret; ! 2188: ! 2189: /* recurse */ ! 2190: for (i = 0; i < factor * new_cluster_count; i++) { ! 2191: direntry = array_get(&(s->directory), first_dir_index + i); ! 2192: if (is_directory(direntry) && !is_dot(direntry)) { ! 2193: mapping = find_mapping_for_cluster(s, first_cluster); ! 2194: assert(mapping->mode & MODE_DIRECTORY); ! 2195: ret = commit_direntries(s, first_dir_index + i, ! 2196: array_index(&(s->mapping), mapping)); ! 2197: if (ret) ! 2198: return ret; ! 2199: } ! 2200: } ! 2201: ! 2202: return 0; ! 2203: } ! 2204: ! 2205: /* commit one file (adjust contents, adjust mapping), ! 2206: return first_mapping_index */ ! 2207: static int commit_one_file(BDRVVVFATState* s, ! 2208: int dir_index, uint32_t offset) ! 2209: { ! 2210: direntry_t* direntry = array_get(&(s->directory), dir_index); ! 2211: uint32_t c = begin_of_direntry(direntry); ! 2212: uint32_t first_cluster = c; ! 2213: mapping_t* mapping = find_mapping_for_cluster(s, c); ! 2214: uint32_t size = filesize_of_direntry(direntry); ! 2215: char* cluster = qemu_malloc(s->cluster_size); ! 2216: uint32_t i; ! 2217: int fd = 0; ! 2218: ! 2219: assert(offset < size); ! 2220: assert((offset % s->cluster_size) == 0); ! 2221: ! 2222: for (i = s->cluster_size; i < offset; i += s->cluster_size) ! 2223: c = modified_fat_get(s, c); ! 2224: ! 2225: fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666); ! 2226: if (fd < 0) { ! 2227: fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path, ! 2228: strerror(errno), errno); ! 2229: return fd; ! 2230: } ! 2231: if (offset > 0) ! 2232: if (lseek(fd, offset, SEEK_SET) != offset) ! 2233: return -3; ! 2234: ! 2235: while (offset < size) { ! 2236: uint32_t c1; ! 2237: int rest_size = (size - offset > s->cluster_size ? ! 2238: s->cluster_size : size - offset); ! 2239: int ret; ! 2240: ! 2241: c1 = modified_fat_get(s, c); ! 2242: ! 2243: assert((size - offset == 0 && fat_eof(s, c)) || ! 2244: (size > offset && c >=2 && !fat_eof(s, c))); ! 2245: ! 2246: ret = vvfat_read(s->bs, cluster2sector(s, c), ! 2247: (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200); ! 2248: ! 2249: if (ret < 0) ! 2250: return ret; ! 2251: ! 2252: if (write(fd, cluster, rest_size) < 0) ! 2253: return -2; ! 2254: ! 2255: offset += rest_size; ! 2256: c = c1; ! 2257: } ! 2258: ! 2259: ftruncate(fd, size); ! 2260: close(fd); ! 2261: ! 2262: return commit_mappings(s, first_cluster, dir_index); ! 2263: } ! 2264: ! 2265: #ifdef DEBUG ! 2266: /* test, if all mappings point to valid direntries */ ! 2267: static void check1(BDRVVVFATState* s) ! 2268: { ! 2269: int i; ! 2270: for (i = 0; i < s->mapping.next; i++) { ! 2271: mapping_t* mapping = array_get(&(s->mapping), i); ! 2272: if (mapping->mode & MODE_DELETED) { ! 2273: fprintf(stderr, "deleted\n"); ! 2274: continue; ! 2275: } ! 2276: assert(mapping->dir_index >= 0); ! 2277: assert(mapping->dir_index < s->directory.next); ! 2278: direntry_t* direntry = array_get(&(s->directory), mapping->dir_index); ! 2279: assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0); ! 2280: if (mapping->mode & MODE_DIRECTORY) { ! 2281: assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next); ! 2282: assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0); ! 2283: } ! 2284: } ! 2285: } ! 2286: ! 2287: /* test, if all direntries have mappings */ ! 2288: static void check2(BDRVVVFATState* s) ! 2289: { ! 2290: int i; ! 2291: int first_mapping = -1; ! 2292: ! 2293: for (i = 0; i < s->directory.next; i++) { ! 2294: direntry_t* direntry = array_get(&(s->directory), i); ! 2295: ! 2296: if (is_short_name(direntry) && begin_of_direntry(direntry)) { ! 2297: mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry)); ! 2298: assert(mapping); ! 2299: assert(mapping->dir_index == i || is_dot(direntry)); ! 2300: assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry)); ! 2301: } ! 2302: ! 2303: if ((i % (0x10 * s->sectors_per_cluster)) == 0) { ! 2304: /* cluster start */ ! 2305: int j, count = 0; ! 2306: ! 2307: for (j = 0; j < s->mapping.next; j++) { ! 2308: mapping_t* mapping = array_get(&(s->mapping), j); ! 2309: if (mapping->mode & MODE_DELETED) ! 2310: continue; ! 2311: if (mapping->mode & MODE_DIRECTORY) { ! 2312: if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) { ! 2313: assert(++count == 1); ! 2314: if (mapping->first_mapping_index == -1) ! 2315: first_mapping = array_index(&(s->mapping), mapping); ! 2316: else ! 2317: assert(first_mapping == mapping->first_mapping_index); ! 2318: if (mapping->info.dir.parent_mapping_index < 0) ! 2319: assert(j == 0); ! 2320: else { ! 2321: mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index); ! 2322: assert(parent->mode & MODE_DIRECTORY); ! 2323: assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index); ! 2324: } ! 2325: } ! 2326: } ! 2327: } ! 2328: if (count == 0) ! 2329: first_mapping = -1; ! 2330: } ! 2331: } ! 2332: } ! 2333: #endif ! 2334: ! 2335: static int handle_renames_and_mkdirs(BDRVVVFATState* s) ! 2336: { ! 2337: int i; ! 2338: ! 2339: #ifdef DEBUG ! 2340: fprintf(stderr, "handle_renames\n"); ! 2341: for (i = 0; i < s->commits.next; i++) { ! 2342: commit_t* commit = array_get(&(s->commits), i); ! 2343: fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action); ! 2344: } ! 2345: #endif ! 2346: ! 2347: for (i = 0; i < s->commits.next;) { ! 2348: commit_t* commit = array_get(&(s->commits), i); ! 2349: if (commit->action == ACTION_RENAME) { ! 2350: mapping_t* mapping = find_mapping_for_cluster(s, ! 2351: commit->param.rename.cluster); ! 2352: char* old_path = mapping->path; ! 2353: ! 2354: assert(commit->path); ! 2355: mapping->path = commit->path; ! 2356: if (rename(old_path, mapping->path)) ! 2357: return -2; ! 2358: ! 2359: if (mapping->mode & MODE_DIRECTORY) { ! 2360: int l1 = strlen(mapping->path); ! 2361: int l2 = strlen(old_path); ! 2362: int diff = l1 - l2; ! 2363: direntry_t* direntry = array_get(&(s->directory), ! 2364: mapping->info.dir.first_dir_index); ! 2365: uint32_t c = mapping->begin; ! 2366: int i = 0; ! 2367: ! 2368: /* recurse */ ! 2369: while (!fat_eof(s, c)) { ! 2370: do { ! 2371: direntry_t* d = direntry + i; ! 2372: ! 2373: if (is_file(d) || (is_directory(d) && !is_dot(d))) { ! 2374: mapping_t* m = find_mapping_for_cluster(s, ! 2375: begin_of_direntry(d)); ! 2376: int l = strlen(m->path); ! 2377: char* new_path = qemu_malloc(l + diff + 1); ! 2378: ! 2379: assert(!strncmp(m->path, mapping->path, l2)); ! 2380: ! 2381: pstrcpy(new_path, l + diff + 1, mapping->path); ! 2382: pstrcpy(new_path + l1, l + diff + 1 - l1, ! 2383: m->path + l2); ! 2384: ! 2385: schedule_rename(s, m->begin, new_path); ! 2386: } ! 2387: i++; ! 2388: } while((i % (0x10 * s->sectors_per_cluster)) != 0); ! 2389: c = fat_get(s, c); ! 2390: } ! 2391: } ! 2392: ! 2393: free(old_path); ! 2394: array_remove(&(s->commits), i); ! 2395: continue; ! 2396: } else if (commit->action == ACTION_MKDIR) { ! 2397: mapping_t* mapping; ! 2398: int j, parent_path_len; ! 2399: ! 2400: #ifdef __MINGW32__ ! 2401: if (mkdir(commit->path)) ! 2402: return -5; ! 2403: #else ! 2404: if (mkdir(commit->path, 0755)) ! 2405: return -5; ! 2406: #endif ! 2407: ! 2408: mapping = insert_mapping(s, commit->param.mkdir.cluster, ! 2409: commit->param.mkdir.cluster + 1); ! 2410: if (mapping == NULL) ! 2411: return -6; ! 2412: ! 2413: mapping->mode = MODE_DIRECTORY; ! 2414: mapping->read_only = 0; ! 2415: mapping->path = commit->path; ! 2416: j = s->directory.next; ! 2417: assert(j); ! 2418: insert_direntries(s, s->directory.next, ! 2419: 0x10 * s->sectors_per_cluster); ! 2420: mapping->info.dir.first_dir_index = j; ! 2421: ! 2422: parent_path_len = strlen(commit->path) ! 2423: - strlen(get_basename(commit->path)) - 1; ! 2424: for (j = 0; j < s->mapping.next; j++) { ! 2425: mapping_t* m = array_get(&(s->mapping), j); ! 2426: if (m->first_mapping_index < 0 && m != mapping && ! 2427: !strncmp(m->path, mapping->path, parent_path_len) && ! 2428: strlen(m->path) == parent_path_len) ! 2429: break; ! 2430: } ! 2431: assert(j < s->mapping.next); ! 2432: mapping->info.dir.parent_mapping_index = j; ! 2433: ! 2434: array_remove(&(s->commits), i); ! 2435: continue; ! 2436: } ! 2437: ! 2438: i++; ! 2439: } ! 2440: return 0; ! 2441: } ! 2442: ! 2443: /* ! 2444: * TODO: make sure that the short name is not matching *another* file ! 2445: */ ! 2446: static int handle_commits(BDRVVVFATState* s) ! 2447: { ! 2448: int i, fail = 0; ! 2449: ! 2450: vvfat_close_current_file(s); ! 2451: ! 2452: for (i = 0; !fail && i < s->commits.next; i++) { ! 2453: commit_t* commit = array_get(&(s->commits), i); ! 2454: switch(commit->action) { ! 2455: case ACTION_RENAME: case ACTION_MKDIR: ! 2456: assert(0); ! 2457: fail = -2; ! 2458: break; ! 2459: case ACTION_WRITEOUT: { ! 2460: direntry_t* entry = array_get(&(s->directory), ! 2461: commit->param.writeout.dir_index); ! 2462: uint32_t begin = begin_of_direntry(entry); ! 2463: mapping_t* mapping = find_mapping_for_cluster(s, begin); ! 2464: ! 2465: assert(mapping); ! 2466: assert(mapping->begin == begin); ! 2467: assert(commit->path == NULL); ! 2468: ! 2469: if (commit_one_file(s, commit->param.writeout.dir_index, ! 2470: commit->param.writeout.modified_offset)) ! 2471: fail = -3; ! 2472: ! 2473: break; ! 2474: } ! 2475: case ACTION_NEW_FILE: { ! 2476: int begin = commit->param.new_file.first_cluster; ! 2477: mapping_t* mapping = find_mapping_for_cluster(s, begin); ! 2478: direntry_t* entry; ! 2479: int i; ! 2480: ! 2481: /* find direntry */ ! 2482: for (i = 0; i < s->directory.next; i++) { ! 2483: entry = array_get(&(s->directory), i); ! 2484: if (is_file(entry) && begin_of_direntry(entry) == begin) ! 2485: break; ! 2486: } ! 2487: ! 2488: if (i >= s->directory.next) { ! 2489: fail = -6; ! 2490: continue; ! 2491: } ! 2492: ! 2493: /* make sure there exists an initial mapping */ ! 2494: if (mapping && mapping->begin != begin) { ! 2495: mapping->end = begin; ! 2496: mapping = NULL; ! 2497: } ! 2498: if (mapping == NULL) { ! 2499: mapping = insert_mapping(s, begin, begin+1); ! 2500: } ! 2501: /* most members will be fixed in commit_mappings() */ ! 2502: assert(commit->path); ! 2503: mapping->path = commit->path; ! 2504: mapping->read_only = 0; ! 2505: mapping->mode = MODE_NORMAL; ! 2506: mapping->info.file.offset = 0; ! 2507: ! 2508: if (commit_one_file(s, i, 0)) ! 2509: fail = -7; ! 2510: ! 2511: break; ! 2512: } ! 2513: default: ! 2514: assert(0); ! 2515: } ! 2516: } ! 2517: if (i > 0 && array_remove_slice(&(s->commits), 0, i)) ! 2518: return -1; ! 2519: return fail; ! 2520: } ! 2521: ! 2522: static int handle_deletes(BDRVVVFATState* s) ! 2523: { ! 2524: int i, deferred = 1, deleted = 1; ! 2525: ! 2526: /* delete files corresponding to mappings marked as deleted */ ! 2527: /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */ ! 2528: while (deferred && deleted) { ! 2529: deferred = 0; ! 2530: deleted = 0; ! 2531: ! 2532: for (i = 1; i < s->mapping.next; i++) { ! 2533: mapping_t* mapping = array_get(&(s->mapping), i); ! 2534: if (mapping->mode & MODE_DELETED) { ! 2535: direntry_t* entry = array_get(&(s->directory), ! 2536: mapping->dir_index); ! 2537: ! 2538: if (is_free(entry)) { ! 2539: /* remove file/directory */ ! 2540: if (mapping->mode & MODE_DIRECTORY) { ! 2541: int j, next_dir_index = s->directory.next, ! 2542: first_dir_index = mapping->info.dir.first_dir_index; ! 2543: ! 2544: if (rmdir(mapping->path) < 0) { ! 2545: if (errno == ENOTEMPTY) { ! 2546: deferred++; ! 2547: continue; ! 2548: } else ! 2549: return -5; ! 2550: } ! 2551: ! 2552: for (j = 1; j < s->mapping.next; j++) { ! 2553: mapping_t* m = array_get(&(s->mapping), j); ! 2554: if (m->mode & MODE_DIRECTORY && ! 2555: m->info.dir.first_dir_index > ! 2556: first_dir_index && ! 2557: m->info.dir.first_dir_index < ! 2558: next_dir_index) ! 2559: next_dir_index = ! 2560: m->info.dir.first_dir_index; ! 2561: } ! 2562: remove_direntries(s, first_dir_index, ! 2563: next_dir_index - first_dir_index); ! 2564: ! 2565: deleted++; ! 2566: } ! 2567: } else { ! 2568: if (unlink(mapping->path)) ! 2569: return -4; ! 2570: deleted++; ! 2571: } ! 2572: DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry)); ! 2573: remove_mapping(s, i); ! 2574: } ! 2575: } ! 2576: } ! 2577: ! 2578: return 0; ! 2579: } ! 2580: ! 2581: /* ! 2582: * synchronize mapping with new state: ! 2583: * ! 2584: * - copy FAT (with bdrv_read) ! 2585: * - mark all filenames corresponding to mappings as deleted ! 2586: * - recurse direntries from root (using bs->bdrv_read) ! 2587: * - delete files corresponding to mappings marked as deleted ! 2588: */ ! 2589: static int do_commit(BDRVVVFATState* s) ! 2590: { ! 2591: int ret = 0; ! 2592: ! 2593: /* the real meat are the commits. Nothing to do? Move along! */ ! 2594: if (s->commits.next == 0) ! 2595: return 0; ! 2596: ! 2597: vvfat_close_current_file(s); ! 2598: ! 2599: ret = handle_renames_and_mkdirs(s); ! 2600: if (ret) { ! 2601: fprintf(stderr, "Error handling renames (%d)\n", ret); ! 2602: assert(0); ! 2603: return ret; ! 2604: } ! 2605: ! 2606: /* copy FAT (with bdrv_read) */ ! 2607: memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat); ! 2608: ! 2609: /* recurse direntries from root (using bs->bdrv_read) */ ! 2610: ret = commit_direntries(s, 0, -1); ! 2611: if (ret) { ! 2612: fprintf(stderr, "Fatal: error while committing (%d)\n", ret); ! 2613: assert(0); ! 2614: return ret; ! 2615: } ! 2616: ! 2617: ret = handle_commits(s); ! 2618: if (ret) { ! 2619: fprintf(stderr, "Error handling commits (%d)\n", ret); ! 2620: assert(0); ! 2621: return ret; ! 2622: } ! 2623: ! 2624: ret = handle_deletes(s); ! 2625: if (ret) { ! 2626: fprintf(stderr, "Error deleting\n"); ! 2627: assert(0); ! 2628: return ret; ! 2629: } ! 2630: ! 2631: s->qcow->drv->bdrv_make_empty(s->qcow); ! 2632: ! 2633: memset(s->used_clusters, 0, sector2cluster(s, s->sector_count)); ! 2634: ! 2635: DLOG(checkpoint()); ! 2636: return 0; ! 2637: } ! 2638: ! 2639: static int try_commit(BDRVVVFATState* s) ! 2640: { ! 2641: vvfat_close_current_file(s); ! 2642: DLOG(checkpoint()); ! 2643: if(!is_consistent(s)) ! 2644: return -1; ! 2645: return do_commit(s); ! 2646: } ! 2647: ! 2648: static int vvfat_write(BlockDriverState *bs, int64_t sector_num, ! 2649: const uint8_t *buf, int nb_sectors) ! 2650: { ! 2651: BDRVVVFATState *s = bs->opaque; ! 2652: int i, ret; ! 2653: ! 2654: DLOG(checkpoint()); ! 2655: ! 2656: vvfat_close_current_file(s); ! 2657: ! 2658: /* ! 2659: * Some sanity checks: ! 2660: * - do not allow writing to the boot sector ! 2661: * - do not allow to write non-ASCII filenames ! 2662: */ ! 2663: ! 2664: if (sector_num < s->first_sectors_number) ! 2665: return -1; ! 2666: ! 2667: for (i = sector2cluster(s, sector_num); ! 2668: i <= sector2cluster(s, sector_num + nb_sectors - 1);) { ! 2669: mapping_t* mapping = find_mapping_for_cluster(s, i); ! 2670: if (mapping) { ! 2671: if (mapping->read_only) { ! 2672: fprintf(stderr, "Tried to write to write-protected file %s\n", ! 2673: mapping->path); ! 2674: return -1; ! 2675: } ! 2676: ! 2677: if (mapping->mode & MODE_DIRECTORY) { ! 2678: int begin = cluster2sector(s, i); ! 2679: int end = begin + s->sectors_per_cluster, k; ! 2680: int dir_index; ! 2681: const direntry_t* direntries; ! 2682: long_file_name lfn; ! 2683: ! 2684: lfn_init(&lfn); ! 2685: ! 2686: if (begin < sector_num) ! 2687: begin = sector_num; ! 2688: if (end > sector_num + nb_sectors) ! 2689: end = sector_num + nb_sectors; ! 2690: dir_index = mapping->dir_index + ! 2691: 0x10 * (begin - mapping->begin * s->sectors_per_cluster); ! 2692: direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num)); ! 2693: ! 2694: for (k = 0; k < (end - begin) * 0x10; k++) { ! 2695: /* do not allow non-ASCII filenames */ ! 2696: if (parse_long_name(&lfn, direntries + k) < 0) { ! 2697: fprintf(stderr, "Warning: non-ASCII filename\n"); ! 2698: return -1; ! 2699: } ! 2700: /* no access to the direntry of a read-only file */ ! 2701: else if (is_short_name(direntries+k) && ! 2702: (direntries[k].attributes & 1)) { ! 2703: if (memcmp(direntries + k, ! 2704: array_get(&(s->directory), dir_index + k), ! 2705: sizeof(direntry_t))) { ! 2706: fprintf(stderr, "Warning: tried to write to write-protected file\n"); ! 2707: return -1; ! 2708: } ! 2709: } ! 2710: } ! 2711: } ! 2712: i = mapping->end; ! 2713: } else ! 2714: i++; ! 2715: } ! 2716: ! 2717: /* ! 2718: * Use qcow backend. Commit later. ! 2719: */ ! 2720: DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors)); ! 2721: ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors); ! 2722: if (ret < 0) { ! 2723: fprintf(stderr, "Error writing to qcow backend\n"); ! 2724: return ret; ! 2725: } ! 2726: ! 2727: for (i = sector2cluster(s, sector_num); ! 2728: i <= sector2cluster(s, sector_num + nb_sectors - 1); i++) ! 2729: if (i >= 0) ! 2730: s->used_clusters[i] |= USED_ALLOCATED; ! 2731: ! 2732: DLOG(checkpoint()); ! 2733: /* TODO: add timeout */ ! 2734: try_commit(s); ! 2735: ! 2736: DLOG(checkpoint()); ! 2737: return 0; ! 2738: } ! 2739: ! 2740: static int vvfat_is_allocated(BlockDriverState *bs, ! 2741: int64_t sector_num, int nb_sectors, int* n) ! 2742: { ! 2743: BDRVVVFATState* s = bs->opaque; ! 2744: *n = s->sector_count - sector_num; ! 2745: if (*n > nb_sectors) ! 2746: *n = nb_sectors; ! 2747: else if (*n < 0) ! 2748: return 0; ! 2749: return 1; ! 2750: } ! 2751: ! 2752: static int write_target_commit(BlockDriverState *bs, int64_t sector_num, ! 2753: const uint8_t* buffer, int nb_sectors) { ! 2754: BDRVVVFATState* s = bs->opaque; ! 2755: return try_commit(s); ! 2756: } ! 2757: ! 2758: static void write_target_close(BlockDriverState *bs) { ! 2759: BDRVVVFATState* s = bs->opaque; ! 2760: bdrv_delete(s->qcow); ! 2761: free(s->qcow_filename); ! 2762: } ! 2763: ! 2764: static BlockDriver vvfat_write_target = { ! 2765: .format_name = "vvfat_write_target", ! 2766: .bdrv_write = write_target_commit, ! 2767: .bdrv_close = write_target_close, ! 2768: }; ! 2769: ! 2770: static int enable_write_target(BDRVVVFATState *s) ! 2771: { ! 2772: BlockDriver *bdrv_qcow; ! 2773: QEMUOptionParameter *options; ! 2774: int size = sector2cluster(s, s->sector_count); ! 2775: s->used_clusters = calloc(size, 1); ! 2776: ! 2777: array_init(&(s->commits), sizeof(commit_t)); ! 2778: ! 2779: s->qcow_filename = qemu_malloc(1024); ! 2780: get_tmp_filename(s->qcow_filename, 1024); ! 2781: ! 2782: bdrv_qcow = bdrv_find_format("qcow"); ! 2783: options = parse_option_parameters("", bdrv_qcow->create_options, NULL); ! 2784: set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512); ! 2785: set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:"); ! 2786: ! 2787: if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0) ! 2788: return -1; ! 2789: s->qcow = bdrv_new(""); ! 2790: if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0) ! 2791: return -1; ! 2792: ! 2793: #ifndef _WIN32 ! 2794: unlink(s->qcow_filename); ! 2795: #endif ! 2796: ! 2797: s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1); ! 2798: s->bs->backing_hd->drv = &vvfat_write_target; ! 2799: s->bs->backing_hd->opaque = s; ! 2800: ! 2801: return 0; ! 2802: } ! 2803: ! 2804: static void vvfat_close(BlockDriverState *bs) ! 2805: { ! 2806: BDRVVVFATState *s = bs->opaque; ! 2807: ! 2808: vvfat_close_current_file(s); ! 2809: array_free(&(s->fat)); ! 2810: array_free(&(s->directory)); ! 2811: array_free(&(s->mapping)); ! 2812: if(s->cluster_buffer) ! 2813: free(s->cluster_buffer); ! 2814: } ! 2815: ! 2816: static BlockDriver bdrv_vvfat = { ! 2817: .format_name = "vvfat", ! 2818: .instance_size = sizeof(BDRVVVFATState), ! 2819: .bdrv_open = vvfat_open, ! 2820: .bdrv_read = vvfat_read, ! 2821: .bdrv_write = vvfat_write, ! 2822: .bdrv_close = vvfat_close, ! 2823: .bdrv_is_allocated = vvfat_is_allocated, ! 2824: .protocol_name = "fat", ! 2825: }; ! 2826: ! 2827: static void bdrv_vvfat_init(void) ! 2828: { ! 2829: bdrv_register(&bdrv_vvfat); ! 2830: } ! 2831: ! 2832: block_init(bdrv_vvfat_init); ! 2833: ! 2834: #ifdef DEBUG ! 2835: static void checkpoint(void) { ! 2836: assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2); ! 2837: check1(vvv); ! 2838: check2(vvv); ! 2839: assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY)); ! 2840: #if 0 ! 2841: if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf) ! 2842: fprintf(stderr, "Nonono!\n"); ! 2843: mapping_t* mapping; ! 2844: direntry_t* direntry; ! 2845: assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next); ! 2846: assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next); ! 2847: if (vvv->mapping.next<47) ! 2848: return; ! 2849: assert((mapping = array_get(&(vvv->mapping), 47))); ! 2850: assert(mapping->dir_index < vvv->directory.next); ! 2851: direntry = array_get(&(vvv->directory), mapping->dir_index); ! 2852: assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0); ! 2853: #endif ! 2854: return; ! 2855: /* avoid compiler warnings: */ ! 2856: hexdump(NULL, 100); ! 2857: remove_mapping(vvv, NULL); ! 2858: print_mapping(NULL); ! 2859: print_direntry(NULL); ! 2860: } ! 2861: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.