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 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 7 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 8 * of this software and associated documentation files (the "Software"), to deal
... ... @@ -27,19 +28,47 @@
27 28 #include "vl.h"
28 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 73 /* dynamic array functions */
45 74 typedef struct array_t {
... ... @@ -62,23 +91,37 @@ static inline void array_free(array_t* array)
62 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 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 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 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 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 184 return -1;
139   - array->next--;
  185 + array->next -= count;
140 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 204 /* These structures are used to fake a disk and the VFAT filesystem.
144 205 * For this reason we need to use __attribute__((packed)). */
145 206  
... ... @@ -151,7 +212,7 @@ typedef struct bootsector_t {
151 212 uint16_t reserved_sectors;
152 213 uint8_t number_of_fats;
153 214 uint16_t root_entries;
154   - uint16_t zero;
  215 + uint16_t total_sectors16;
155 216 uint8_t media_type;
156 217 uint16_t sectors_per_fat;
157 218 uint16_t sectors_per_track;
... ... @@ -186,7 +247,7 @@ typedef struct partition_t {
186 247 uint8_t start_head;
187 248 uint8_t start_sector;
188 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 251 uint8_t end_head;
191 252 uint8_t end_sector;
192 253 uint8_t end_cylinder;
... ... @@ -218,32 +279,44 @@ typedef struct direntry_t {
218 279 /* this structure are used to transparently access the files */
219 280  
220 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 284 /* as s->directory is growable, no pointer may be used here */
227 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 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 316 /* here begins the real VVFAT driver */
245 317  
246 318 typedef struct BDRVVVFATState {
  319 + BlockDriverState* bs; /* pointer to parent */
247 320 unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
248 321 unsigned char first_sectors[0x40*0x200];
249 322  
... ... @@ -254,31 +327,33 @@ typedef struct BDRVVVFATState {
254 327 unsigned int sectors_per_cluster;
255 328 unsigned int sectors_per_fat;
256 329 unsigned int sectors_of_root_directory;
257   - unsigned int sectors_for_directory;
  330 + uint32_t last_cluster_of_root_directory;
258 331 unsigned int faked_sectors; /* how many sectors are faked before file data */
259 332 uint32_t sector_count; /* total number of sectors of the partition */
260 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 334 uint32_t max_fat_value;
263 335  
264 336 int current_fd;
265   - char current_fd_is_writable; /* =0 if read only, !=0 if read/writable */
266 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 340 unsigned int current_cluster;
269 341  
270 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 351 } BDRVVVFATState;
276 352  
277 353  
278 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 357 return 100;
283 358 return 0;
284 359 }
... ... @@ -295,16 +370,19 @@ static void init_mbr(BDRVVVFATState* s)
295 370 partition->start_head=1;
296 371 partition->start_sector=1;
297 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 376 partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */;
301 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 379 partition->end_sector_long=cpu_to_le32(s->sector_count);
304 380  
305 381 real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
306 382 }
307 383  
  384 +/* direntry functions */
  385 +
308 386 /* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
309 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 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 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 482 uint8_t chksum=0;
352 483 int i;
... ... @@ -375,29 +506,39 @@ static uint16_t fat_datetime(time_t time,int return_time) {
375 506  
376 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 512 } else if(s->fat_type==16) {
381 513 uint16_t* entry=array_get(&(s->fat),cluster);
382 514 *entry=cpu_to_le16(value&0xffff);
383 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 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 536 } else if(s->fat_type==16) {
396 537 uint16_t* entry=array_get(&(s->fat),cluster);
397 538 return le16_to_cpu(*entry);
398 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 551  
411 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 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 565 switch(s->fat_type) {
425 566 case 12: s->max_fat_value=0xfff; break;
426 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 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 580 direntry_t* entry=0;
477 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 587 return entry;
484 588 }
485 589  
486   - for(i=1;i<8 && filename[i] && filename[i]!='.';i++);
487   -
488 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 599 entry=array_get_next(&(s->directory));
491 600 memset(entry->name,0x20,11);
492 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 607 /* upcase & remove unwanted characters */
502 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 610 if(entry->name[i]<=' ' || entry->name[i]>0x7f
505   - || strchr("*?<>|\":/\\[];,+='",entry->name[i]))
  611 + || strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
506 612 entry->name[i]='_';
507 613 else if(entry->name[i]>='a' && entry->name[i]<='z')
508 614 entry->name[i]+='A'-'a';
... ... @@ -514,7 +620,7 @@ static inline direntry_t* create_short_filename(BDRVVVFATState* s,unsigned int d
514 620 int j;
515 621  
516 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 624 break; /* found dupe */
519 625 if(entry1==entry) /* no dupe found */
520 626 break;
... ... @@ -543,8 +649,7 @@ static inline direntry_t* create_short_filename(BDRVVVFATState* s,unsigned int d
543 649  
544 650 /* calculate anew, because realloc could have taken place */
545 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 653 entry_long->reserved[1]=chksum;
549 654 entry_long++;
550 655 }
... ... @@ -553,33 +658,48 @@ static inline direntry_t* create_short_filename(BDRVVVFATState* s,unsigned int d
553 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 675 DIR* dir=opendir(dirname);
561 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 677 int i;
568 678  
569   - if(!dir)
  679 + assert(mapping->mode & MODE_DIRECTORY);
  680 +
  681 + if(!dir) {
  682 + mapping->end = mapping->begin;
570 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 690 while((entry=readdir(dir))) {
573 691 unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
574 692 char* buffer;
575 693 direntry_t* direntry;
  694 + struct stat st;
576 695 int is_dot=!strcmp(entry->d_name,".");
577 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 699 continue;
581 700  
582 701 buffer=(char*)malloc(length);
  702 + assert(buffer);
583 703 snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
584 704  
585 705 if(stat(buffer,&st)<0) {
... ... @@ -588,8 +708,8 @@ static int read_directory(BDRVVVFATState* s,const char* dirname,
588 708 }
589 709  
590 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 713 direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
594 714 direntry->reserved[0]=direntry->reserved[1]=0;
595 715 direntry->ctime=fat_datetime(st.st_ctime,1);
... ... @@ -599,25 +719,40 @@ static int read_directory(BDRVVVFATState* s,const char* dirname,
599 719 direntry->mtime=fat_datetime(st.st_mtime,1);
600 720 direntry->mdate=fat_datetime(st.st_mtime,0);
601 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 723 else if(is_dot)
604   - direntry->begin=cpu_to_le16(first_cluster);
  724 + set_begin_of_direntry(direntry, first_cluster);
605 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 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 737 s->current_mapping->begin=0;
616 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 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 758 closedir(dir);
... ... @@ -628,89 +763,78 @@ static int read_directory(BDRVVVFATState* s,const char* dirname,
628 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 817 unsigned int i;
701 818 unsigned int cluster;
702 819  
703 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 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 836 array_init(&(s->mapping),sizeof(mapping_t));
712 837 array_init(&(s->directory),sizeof(direntry_t));
713   - array_init(&(s->commit),sizeof(commit_t));
714 838  
715 839 /* add volume label */
716 840 {
... ... @@ -719,69 +843,86 @@ static int init_directory(BDRVVVFATState* s,const char* dirname)
719 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 846 /* Now build FAT, and write back information into directory */
732 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 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 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 926 bootsector->jump[0]=0xeb;
786 927 bootsector->jump[1]=0x3e;
787 928 bootsector->jump[2]=0x90;
... ... @@ -791,17 +932,17 @@ static int init_directory(BDRVVVFATState* s,const char* dirname)
791 932 bootsector->reserved_sectors=cpu_to_le16(1);
792 933 bootsector->number_of_fats=0x2; /* number of FATs */
793 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 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 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 946 bootsector->u.fat16.current_head=0;
806 947 bootsector->u.fat16.signature=0x29;
807 948 bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
... ... @@ -813,52 +954,106 @@ static int init_directory(BDRVVVFATState* s,const char* dirname)
813 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 962 static int vvfat_open(BlockDriverState *bs, const char* dirname)
817 963 {
818 964 BDRVVVFATState *s = bs->opaque;
  965 + int floppy = 0;
819 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 977 s->fat_type=16;
  978 + /* LATER TODO: if FAT32, adjust */
823 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 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 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 1033 return -1;
842 1034  
843 1035 if(s->first_sectors_number==0x40)
844 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 1044 return 0;
854 1045 }
855 1046  
856 1047 static inline void vvfat_close_current_file(BDRVVVFATState *s)
857 1048 {
858 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 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 1062 static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
868 1063 {
869 1064 int index3=index1+1;
870   - //fprintf(stderr,"find_aux: cluster_num=%d, index1=%d,index2=%d\n",cluster_num,index1,index2);
871 1065 while(1) {
872 1066 mapping_t* mapping;
873 1067 index3=(index1+index2)/2;
874 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 1071 assert(index2!=index3 || index2==0);
878 1072 if(index2==index3)
879   - return index2;
  1073 + return index1;
880 1074 index2=index3;
881 1075 } else {
882 1076 if(index1==index3)
883   - return index2;
  1077 + return mapping->end<=cluster_num ? index2 : index1;
884 1078 index1=index3;
885 1079 }
886 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 1095 mapping=array_get(&(s->mapping),index);
897 1096 if(mapping->begin>cluster_num)
898 1097 return 0;
  1098 + assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
899 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 1123 if(!mapping)
905 1124 return -1;
906   - assert(flags==O_RDONLY || flags==O_RDWR);
907 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 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 1129 if(fd<0)
913 1130 return -1;
914 1131 vvfat_close_current_file(s);
915 1132 s->current_fd = fd;
916   - s->current_fd_is_writable = (flags==O_RDWR?-1:0);
917 1133 s->current_mapping = mapping;
918 1134 }
919 1135 return 0;
... ... @@ -924,18 +1140,39 @@ static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
924 1140 if(s->current_cluster != cluster_num) {
925 1141 int result=0;
926 1142 off_t offset;
  1143 + assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
927 1144 if(!s->current_mapping
928 1145 || s->current_mapping->begin>cluster_num
929 1146 || s->current_mapping->end<=cluster_num) {
930 1147 /* binary search of mappings for file */
931 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 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 1173 if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
938 1174 return -3;
  1175 + s->cluster=s->cluster_buffer;
939 1176 result=read(s->current_fd,s->cluster,s->cluster_size);
940 1177 if(result<0) {
941 1178 s->current_cluster = -1;
... ... @@ -946,774 +1183,1565 @@ static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
946 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 1210 if(!direntry)
986 1211 return;
987   - if(direntry->attributes==0xf) {
  1212 + if(is_long_name(direntry)) {
988 1213 unsigned char* c=(unsigned char*)direntry;
989 1214 int i;
990 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 1218 for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
993   - fputc(c[i],stderr);
  1219 + ADD_CHAR(c[i]);
994 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 1224 } else {
998 1225 int i;
999 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 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 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 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 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 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 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 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 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 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 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 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 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 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 2745 return 0;
1718 2746 }
1719 2747  
... ... @@ -1725,8 +2753,8 @@ static void vvfat_close(BlockDriverState *bs)
1725 2753 array_free(&(s->fat));
1726 2754 array_free(&(s->directory));
1727 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 2760 BlockDriver bdrv_vvfat = {
... ... @@ -1737,6 +2765,36 @@ BlockDriver bdrv_vvfat = {
1737 2765 vvfat_read,
1738 2766 vvfat_write,
1739 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  
... ...