Commit a046433a161a1f554be55df8421d92cbdc52779f

Authored by bellard
1 parent 95389c86

Major overhaul of the virtual FAT driver for read/write support (Johannes Schindelin)


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