Reorganize struct Qcow2Cache for better struct packing
[qemu/agraf.git] / block / vvfat.c
blobfe568fe2c7548556a202cb33b7e0c1c4243396fa
1 /* vim:set shiftwidth=4 ts=8: */
2 /*
3 * QEMU Block driver for virtual VFAT (shadows a local directory)
5 * Copyright (c) 2004,2005 Johannes E. Schindelin
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
25 #include <sys/stat.h>
26 #include <dirent.h>
27 #include "qemu-common.h"
28 #include "block_int.h"
29 #include "module.h"
31 #ifndef S_IWGRP
32 #define S_IWGRP 0
33 #endif
34 #ifndef S_IWOTH
35 #define S_IWOTH 0
36 #endif
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 */
46 /* #define DEBUG */
48 #ifdef DEBUG
50 #define DLOG(a) a
52 #undef stderr
53 #define stderr STDERR
54 FILE* stderr = NULL;
56 static void checkpoint(void);
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);
63 #undef assert
64 #define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0)
65 #endif
67 #else
69 #define DLOG(a)
71 #endif
73 /* dynamic array functions */
74 typedef struct array_t {
75 char* pointer;
76 unsigned int size,next,item_size;
77 } array_t;
79 static inline void array_init(array_t* array,unsigned int item_size)
81 array->pointer = NULL;
82 array->size=0;
83 array->next=0;
84 array->item_size=item_size;
87 static inline void array_free(array_t* array)
89 if(array->pointer)
90 free(array->pointer);
91 array->size=array->next=0;
94 /* does not automatically grow */
95 static inline void* array_get(array_t* array,unsigned int index) {
96 assert(index < array->next);
97 return array->pointer + index * array->item_size;
100 static inline int array_ensure_allocated(array_t* array, int index)
102 if((index + 1) * array->item_size > array->size) {
103 int new_size = (index + 32) * array->item_size;
104 array->pointer = qemu_realloc(array->pointer, new_size);
105 if (!array->pointer)
106 return -1;
107 array->size = new_size;
108 array->next = index + 1;
111 return 0;
114 static inline void* array_get_next(array_t* array) {
115 unsigned int next = array->next;
116 void* result;
118 if (array_ensure_allocated(array, next) < 0)
119 return NULL;
121 array->next = next + 1;
122 result = array_get(array, next);
124 return result;
127 static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
128 if((array->next+count)*array->item_size>array->size) {
129 int increment=count*array->item_size;
130 array->pointer=qemu_realloc(array->pointer,array->size+increment);
131 if(!array->pointer)
132 return NULL;
133 array->size+=increment;
135 memmove(array->pointer+(index+count)*array->item_size,
136 array->pointer+index*array->item_size,
137 (array->next-index)*array->item_size);
138 array->next+=count;
139 return array->pointer+index*array->item_size;
142 /* this performs a "roll", so that the element which was at index_from becomes
143 * index_to, but the order of all other elements is preserved. */
144 static inline int array_roll(array_t* array,int index_to,int index_from,int count)
146 char* buf;
147 char* from;
148 char* to;
149 int is;
151 if(!array ||
152 index_to<0 || index_to>=array->next ||
153 index_from<0 || index_from>=array->next)
154 return -1;
156 if(index_to==index_from)
157 return 0;
159 is=array->item_size;
160 from=array->pointer+index_from*is;
161 to=array->pointer+index_to*is;
162 buf=qemu_malloc(is*count);
163 memcpy(buf,from,is*count);
165 if(index_to<index_from)
166 memmove(to+is*count,to,from-to);
167 else
168 memmove(from,from+is*count,to-from);
170 memcpy(to,buf,is*count);
172 free(buf);
174 return 0;
177 static inline int array_remove_slice(array_t* array,int index, int count)
179 assert(index >=0);
180 assert(count > 0);
181 assert(index + count <= array->next);
182 if(array_roll(array,array->next-1,index,count))
183 return -1;
184 array->next -= count;
185 return 0;
188 static int array_remove(array_t* array,int index)
190 return array_remove_slice(array, index, 1);
193 /* return the index for a given member */
194 static int array_index(array_t* array, void* pointer)
196 size_t offset = (char*)pointer - array->pointer;
197 assert((offset % array->item_size) == 0);
198 assert(offset/array->item_size < array->next);
199 return offset/array->item_size;
202 /* These structures are used to fake a disk and the VFAT filesystem.
203 * For this reason we need to use __attribute__((packed)). */
205 typedef struct bootsector_t {
206 uint8_t jump[3];
207 uint8_t name[8];
208 uint16_t sector_size;
209 uint8_t sectors_per_cluster;
210 uint16_t reserved_sectors;
211 uint8_t number_of_fats;
212 uint16_t root_entries;
213 uint16_t total_sectors16;
214 uint8_t media_type;
215 uint16_t sectors_per_fat;
216 uint16_t sectors_per_track;
217 uint16_t number_of_heads;
218 uint32_t hidden_sectors;
219 uint32_t total_sectors;
220 union {
221 struct {
222 uint8_t drive_number;
223 uint8_t current_head;
224 uint8_t signature;
225 uint32_t id;
226 uint8_t volume_label[11];
227 } __attribute__((packed)) fat16;
228 struct {
229 uint32_t sectors_per_fat;
230 uint16_t flags;
231 uint8_t major,minor;
232 uint32_t first_cluster_of_root_directory;
233 uint16_t info_sector;
234 uint16_t backup_boot_sector;
235 uint16_t ignored;
236 } __attribute__((packed)) fat32;
237 } u;
238 uint8_t fat_type[8];
239 uint8_t ignored[0x1c0];
240 uint8_t magic[2];
241 } __attribute__((packed)) bootsector_t;
243 typedef struct {
244 uint8_t head;
245 uint8_t sector;
246 uint8_t cylinder;
247 } mbr_chs_t;
249 typedef struct partition_t {
250 uint8_t attributes; /* 0x80 = bootable */
251 mbr_chs_t start_CHS;
252 uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
253 mbr_chs_t end_CHS;
254 uint32_t start_sector_long;
255 uint32_t length_sector_long;
256 } __attribute__((packed)) partition_t;
258 typedef struct mbr_t {
259 uint8_t ignored[0x1b8];
260 uint32_t nt_id;
261 uint8_t ignored2[2];
262 partition_t partition[4];
263 uint8_t magic[2];
264 } __attribute__((packed)) mbr_t;
266 typedef struct direntry_t {
267 uint8_t name[8];
268 uint8_t extension[3];
269 uint8_t attributes;
270 uint8_t reserved[2];
271 uint16_t ctime;
272 uint16_t cdate;
273 uint16_t adate;
274 uint16_t begin_hi;
275 uint16_t mtime;
276 uint16_t mdate;
277 uint16_t begin;
278 uint32_t size;
279 } __attribute__((packed)) direntry_t;
281 /* this structure are used to transparently access the files */
283 typedef struct mapping_t {
284 /* begin is the first cluster, end is the last+1 */
285 uint32_t begin,end;
286 /* as s->directory is growable, no pointer may be used here */
287 unsigned int dir_index;
288 /* the clusters of a file may be in any order; this points to the first */
289 int first_mapping_index;
290 union {
291 /* offset is
292 * - the offset in the file (in clusters) for a file, or
293 * - the next cluster of the directory for a directory, and
294 * - the address of the buffer for a faked entry
296 struct {
297 uint32_t offset;
298 } file;
299 struct {
300 int parent_mapping_index;
301 int first_dir_index;
302 } dir;
303 } info;
304 /* path contains the full path, i.e. it always starts with s->path */
305 char* path;
307 enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2,
308 MODE_DIRECTORY = 4, MODE_FAKED = 8,
309 MODE_DELETED = 16, MODE_RENAMED = 32 } mode;
310 int read_only;
311 } mapping_t;
313 #ifdef DEBUG
314 static void print_direntry(const struct direntry_t*);
315 static void print_mapping(const struct mapping_t* mapping);
316 #endif
318 /* here begins the real VVFAT driver */
320 typedef struct BDRVVVFATState {
321 BlockDriverState* bs; /* pointer to parent */
322 unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
323 unsigned char first_sectors[0x40*0x200];
325 int fat_type; /* 16 or 32 */
326 array_t fat,directory,mapping;
328 unsigned int cluster_size;
329 unsigned int sectors_per_cluster;
330 unsigned int sectors_per_fat;
331 unsigned int sectors_of_root_directory;
332 uint32_t last_cluster_of_root_directory;
333 unsigned int faked_sectors; /* how many sectors are faked before file data */
334 uint32_t sector_count; /* total number of sectors of the partition */
335 uint32_t cluster_count; /* total number of clusters of this partition */
336 uint32_t max_fat_value;
338 int current_fd;
339 mapping_t* current_mapping;
340 unsigned char* cluster; /* points to current cluster */
341 unsigned char* cluster_buffer; /* points to a buffer to hold temp data */
342 unsigned int current_cluster;
344 /* write support */
345 BlockDriverState* write_target;
346 char* qcow_filename;
347 BlockDriverState* qcow;
348 void* fat2;
349 char* used_clusters;
350 array_t commits;
351 const char* path;
352 int downcase_short_names;
353 } BDRVVVFATState;
355 /* take the sector position spos and convert it to Cylinder/Head/Sector position
356 * if the position is outside the specified geometry, fill maximum value for CHS
357 * and return 1 to signal overflow.
359 static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
360 int head,sector;
361 sector = spos % (bs->secs); spos/= bs->secs;
362 head = spos % (bs->heads); spos/= bs->heads;
363 if(spos >= bs->cyls){
364 /* Overflow,
365 it happens if 32bit sector positions are used, while CHS is only 24bit.
366 Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
367 chs->head = 0xFF;
368 chs->sector = 0xFF;
369 chs->cylinder = 0xFF;
370 return 1;
372 chs->head = (uint8_t)head;
373 chs->sector = (uint8_t)( (sector+1) | ((spos>>8)<<6) );
374 chs->cylinder = (uint8_t)spos;
375 return 0;
378 static void init_mbr(BDRVVVFATState* s)
380 /* TODO: if the files mbr.img and bootsect.img exist, use them */
381 mbr_t* real_mbr=(mbr_t*)s->first_sectors;
382 partition_t* partition = &(real_mbr->partition[0]);
383 int lba;
385 memset(s->first_sectors,0,512);
387 /* Win NT Disk Signature */
388 real_mbr->nt_id= cpu_to_le32(0xbe1afdfa);
390 partition->attributes=0x80; /* bootable */
392 /* LBA is used when partition is outside the CHS geometry */
393 lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1);
394 lba|= sector2CHS(s->bs, &partition->end_CHS, s->sector_count);
396 /*LBA partitions are identified only by start/length_sector_long not by CHS*/
397 partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1);
398 partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1);
400 /* FAT12/FAT16/FAT32 */
401 /* DOS uses different types when partition is LBA,
402 probably to prevent older versions from using CHS on them */
403 partition->fs_type= s->fat_type==12 ? 0x1:
404 s->fat_type==16 ? (lba?0xe:0x06):
405 /*fat_tyoe==32*/ (lba?0xc:0x0b);
407 real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
410 /* direntry functions */
412 /* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
413 static inline int short2long_name(char* dest,const char* src)
415 int i;
416 int len;
417 for(i=0;i<129 && src[i];i++) {
418 dest[2*i]=src[i];
419 dest[2*i+1]=0;
421 len=2*i;
422 dest[2*i]=dest[2*i+1]=0;
423 for(i=2*i+2;(i%26);i++)
424 dest[i]=0xff;
425 return len;
428 static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
430 char buffer[258];
431 int length=short2long_name(buffer,filename),
432 number_of_entries=(length+25)/26,i;
433 direntry_t* entry;
435 for(i=0;i<number_of_entries;i++) {
436 entry=array_get_next(&(s->directory));
437 entry->attributes=0xf;
438 entry->reserved[0]=0;
439 entry->begin=0;
440 entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
442 for(i=0;i<26*number_of_entries;i++) {
443 int offset=(i%26);
444 if(offset<10) offset=1+offset;
445 else if(offset<22) offset=14+offset-10;
446 else offset=28+offset-22;
447 entry=array_get(&(s->directory),s->directory.next-1-(i/26));
448 entry->name[offset]=buffer[i];
450 return array_get(&(s->directory),s->directory.next-number_of_entries);
453 static char is_free(const direntry_t* direntry)
455 return direntry->name[0]==0xe5 || direntry->name[0]==0x00;
458 static char is_volume_label(const direntry_t* direntry)
460 return direntry->attributes == 0x28;
463 static char is_long_name(const direntry_t* direntry)
465 return direntry->attributes == 0xf;
468 static char is_short_name(const direntry_t* direntry)
470 return !is_volume_label(direntry) && !is_long_name(direntry)
471 && !is_free(direntry);
474 static char is_directory(const direntry_t* direntry)
476 return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
479 static inline char is_dot(const direntry_t* direntry)
481 return is_short_name(direntry) && direntry->name[0] == '.';
484 static char is_file(const direntry_t* direntry)
486 return is_short_name(direntry) && !is_directory(direntry);
489 static inline uint32_t begin_of_direntry(const direntry_t* direntry)
491 return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
494 static inline uint32_t filesize_of_direntry(const direntry_t* direntry)
496 return le32_to_cpu(direntry->size);
499 static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
501 direntry->begin = cpu_to_le16(begin & 0xffff);
502 direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
505 /* fat functions */
507 static inline uint8_t fat_chksum(const direntry_t* entry)
509 uint8_t chksum=0;
510 int i;
512 for(i=0;i<11;i++) {
513 unsigned char c;
515 c = (i < 8) ? entry->name[i] : entry->extension[i-8];
516 chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c;
519 return chksum;
522 /* if return_time==0, this returns the fat_date, else the fat_time */
523 static uint16_t fat_datetime(time_t time,int return_time) {
524 struct tm* t;
525 #ifdef _WIN32
526 t=localtime(&time); /* this is not thread safe */
527 #else
528 struct tm t1;
529 t = &t1;
530 localtime_r(&time,t);
531 #endif
532 if(return_time)
533 return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
534 return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
537 static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
539 if(s->fat_type==32) {
540 uint32_t* entry=array_get(&(s->fat),cluster);
541 *entry=cpu_to_le32(value);
542 } else if(s->fat_type==16) {
543 uint16_t* entry=array_get(&(s->fat),cluster);
544 *entry=cpu_to_le16(value&0xffff);
545 } else {
546 int offset = (cluster*3/2);
547 unsigned char* p = array_get(&(s->fat), offset);
548 switch (cluster&1) {
549 case 0:
550 p[0] = value&0xff;
551 p[1] = (p[1]&0xf0) | ((value>>8)&0xf);
552 break;
553 case 1:
554 p[0] = (p[0]&0xf) | ((value&0xf)<<4);
555 p[1] = (value>>4);
556 break;
561 static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
563 if(s->fat_type==32) {
564 uint32_t* entry=array_get(&(s->fat),cluster);
565 return le32_to_cpu(*entry);
566 } else if(s->fat_type==16) {
567 uint16_t* entry=array_get(&(s->fat),cluster);
568 return le16_to_cpu(*entry);
569 } else {
570 const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2;
571 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
575 static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
577 if(fat_entry>s->max_fat_value-8)
578 return -1;
579 return 0;
582 static inline void init_fat(BDRVVVFATState* s)
584 if (s->fat_type == 12) {
585 array_init(&(s->fat),1);
586 array_ensure_allocated(&(s->fat),
587 s->sectors_per_fat * 0x200 * 3 / 2 - 1);
588 } else {
589 array_init(&(s->fat),(s->fat_type==32?4:2));
590 array_ensure_allocated(&(s->fat),
591 s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
593 memset(s->fat.pointer,0,s->fat.size);
595 switch(s->fat_type) {
596 case 12: s->max_fat_value=0xfff; break;
597 case 16: s->max_fat_value=0xffff; break;
598 case 32: s->max_fat_value=0x0fffffff; break;
599 default: s->max_fat_value=0; /* error... */
604 /* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */
605 /* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */
606 static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
607 unsigned int directory_start, const char* filename, int is_dot)
609 int i,j,long_index=s->directory.next;
610 direntry_t* entry = NULL;
611 direntry_t* entry_long = NULL;
613 if(is_dot) {
614 entry=array_get_next(&(s->directory));
615 memset(entry->name,0x20,11);
616 memcpy(entry->name,filename,strlen(filename));
617 return entry;
620 entry_long=create_long_filename(s,filename);
622 i = strlen(filename);
623 for(j = i - 1; j>0 && filename[j]!='.';j--);
624 if (j > 0)
625 i = (j > 8 ? 8 : j);
626 else if (i > 8)
627 i = 8;
629 entry=array_get_next(&(s->directory));
630 memset(entry->name,0x20,11);
631 memcpy(entry->name, filename, i);
633 if(j > 0)
634 for (i = 0; i < 3 && filename[j+1+i]; i++)
635 entry->extension[i] = filename[j+1+i];
637 /* upcase & remove unwanted characters */
638 for(i=10;i>=0;i--) {
639 if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--);
640 if(entry->name[i]<=' ' || entry->name[i]>0x7f
641 || strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
642 entry->name[i]='_';
643 else if(entry->name[i]>='a' && entry->name[i]<='z')
644 entry->name[i]+='A'-'a';
647 /* mangle duplicates */
648 while(1) {
649 direntry_t* entry1=array_get(&(s->directory),directory_start);
650 int j;
652 for(;entry1<entry;entry1++)
653 if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11))
654 break; /* found dupe */
655 if(entry1==entry) /* no dupe found */
656 break;
658 /* use all 8 characters of name */
659 if(entry->name[7]==' ') {
660 int j;
661 for(j=6;j>0 && entry->name[j]==' ';j--)
662 entry->name[j]='~';
665 /* increment number */
666 for(j=7;j>0 && entry->name[j]=='9';j--)
667 entry->name[j]='0';
668 if(j>0) {
669 if(entry->name[j]<'0' || entry->name[j]>'9')
670 entry->name[j]='0';
671 else
672 entry->name[j]++;
676 /* calculate checksum; propagate to long name */
677 if(entry_long) {
678 uint8_t chksum=fat_chksum(entry);
680 /* calculate anew, because realloc could have taken place */
681 entry_long=array_get(&(s->directory),long_index);
682 while(entry_long<entry && is_long_name(entry_long)) {
683 entry_long->reserved[1]=chksum;
684 entry_long++;
688 return entry;
692 * Read a directory. (the index of the corresponding mapping must be passed).
694 static int read_directory(BDRVVVFATState* s, int mapping_index)
696 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
697 direntry_t* direntry;
698 const char* dirname = mapping->path;
699 int first_cluster = mapping->begin;
700 int parent_index = mapping->info.dir.parent_mapping_index;
701 mapping_t* parent_mapping = (mapping_t*)
702 (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : NULL);
703 int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
705 DIR* dir=opendir(dirname);
706 struct dirent* entry;
707 int i;
709 assert(mapping->mode & MODE_DIRECTORY);
711 if(!dir) {
712 mapping->end = mapping->begin;
713 return -1;
716 i = mapping->info.dir.first_dir_index =
717 first_cluster == 0 ? 0 : s->directory.next;
719 /* actually read the directory, and allocate the mappings */
720 while((entry=readdir(dir))) {
721 unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
722 char* buffer;
723 direntry_t* direntry;
724 struct stat st;
725 int is_dot=!strcmp(entry->d_name,".");
726 int is_dotdot=!strcmp(entry->d_name,"..");
728 if(first_cluster == 0 && (is_dotdot || is_dot))
729 continue;
731 buffer=(char*)qemu_malloc(length);
732 snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
734 if(stat(buffer,&st)<0) {
735 free(buffer);
736 continue;
739 /* create directory entry for this file */
740 direntry=create_short_and_long_name(s, i, entry->d_name,
741 is_dot || is_dotdot);
742 direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
743 direntry->reserved[0]=direntry->reserved[1]=0;
744 direntry->ctime=fat_datetime(st.st_ctime,1);
745 direntry->cdate=fat_datetime(st.st_ctime,0);
746 direntry->adate=fat_datetime(st.st_atime,0);
747 direntry->begin_hi=0;
748 direntry->mtime=fat_datetime(st.st_mtime,1);
749 direntry->mdate=fat_datetime(st.st_mtime,0);
750 if(is_dotdot)
751 set_begin_of_direntry(direntry, first_cluster_of_parent);
752 else if(is_dot)
753 set_begin_of_direntry(direntry, first_cluster);
754 else
755 direntry->begin=0; /* do that later */
756 if (st.st_size > 0x7fffffff) {
757 fprintf(stderr, "File %s is larger than 2GB\n", buffer);
758 free(buffer);
759 closedir(dir);
760 return -2;
762 direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
764 /* create mapping for this file */
765 if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
766 s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
767 s->current_mapping->begin=0;
768 s->current_mapping->end=st.st_size;
770 * we get the direntry of the most recent direntry, which
771 * contains the short name and all the relevant information.
773 s->current_mapping->dir_index=s->directory.next-1;
774 s->current_mapping->first_mapping_index = -1;
775 if (S_ISDIR(st.st_mode)) {
776 s->current_mapping->mode = MODE_DIRECTORY;
777 s->current_mapping->info.dir.parent_mapping_index =
778 mapping_index;
779 } else {
780 s->current_mapping->mode = MODE_UNDEFINED;
781 s->current_mapping->info.file.offset = 0;
783 s->current_mapping->path=buffer;
784 s->current_mapping->read_only =
785 (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
788 closedir(dir);
790 /* fill with zeroes up to the end of the cluster */
791 while(s->directory.next%(0x10*s->sectors_per_cluster)) {
792 direntry_t* direntry=array_get_next(&(s->directory));
793 memset(direntry,0,sizeof(direntry_t));
796 /* TODO: if there are more entries, bootsector has to be adjusted! */
797 #define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster)
798 if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) {
799 /* root directory */
800 int cur = s->directory.next;
801 array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
802 memset(array_get(&(s->directory), cur), 0,
803 (ROOT_ENTRIES - cur) * sizeof(direntry_t));
806 /* reget the mapping, since s->mapping was possibly realloc()ed */
807 mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
808 first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
809 * 0x20 / s->cluster_size;
810 mapping->end = first_cluster;
812 direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
813 set_begin_of_direntry(direntry, mapping->begin);
815 return 0;
818 static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
820 return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
823 static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
825 return s->faked_sectors + s->sectors_per_cluster * cluster_num;
828 static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num)
830 return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster;
833 #ifdef DBG
834 static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping)
836 if(mapping->mode==MODE_UNDEFINED)
837 return 0;
838 return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index);
840 #endif
842 static int init_directories(BDRVVVFATState* s,
843 const char* dirname)
845 bootsector_t* bootsector;
846 mapping_t* mapping;
847 unsigned int i;
848 unsigned int cluster;
850 memset(&(s->first_sectors[0]),0,0x40*0x200);
852 s->cluster_size=s->sectors_per_cluster*0x200;
853 s->cluster_buffer=qemu_malloc(s->cluster_size);
856 * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
857 * where sc is sector_count,
858 * spf is sectors_per_fat,
859 * spc is sectors_per_clusters, and
860 * fat_type = 12, 16 or 32.
862 i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
863 s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
865 array_init(&(s->mapping),sizeof(mapping_t));
866 array_init(&(s->directory),sizeof(direntry_t));
868 /* add volume label */
870 direntry_t* entry=array_get_next(&(s->directory));
871 entry->attributes=0x28; /* archive | volume label */
872 memcpy(entry->name,"QEMU VVF",8);
873 memcpy(entry->extension,"AT ",3);
876 /* Now build FAT, and write back information into directory */
877 init_fat(s);
879 s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
880 s->cluster_count=sector2cluster(s, s->sector_count);
882 mapping = array_get_next(&(s->mapping));
883 mapping->begin = 0;
884 mapping->dir_index = 0;
885 mapping->info.dir.parent_mapping_index = -1;
886 mapping->first_mapping_index = -1;
887 mapping->path = qemu_strdup(dirname);
888 i = strlen(mapping->path);
889 if (i > 0 && mapping->path[i - 1] == '/')
890 mapping->path[i - 1] = '\0';
891 mapping->mode = MODE_DIRECTORY;
892 mapping->read_only = 0;
893 s->path = mapping->path;
895 for (i = 0, cluster = 0; i < s->mapping.next; i++) {
896 /* MS-DOS expects the FAT to be 0 for the root directory
897 * (except for the media byte). */
898 /* LATER TODO: still true for FAT32? */
899 int fix_fat = (i != 0);
900 mapping = array_get(&(s->mapping), i);
902 if (mapping->mode & MODE_DIRECTORY) {
903 mapping->begin = cluster;
904 if(read_directory(s, i)) {
905 fprintf(stderr, "Could not read directory %s\n",
906 mapping->path);
907 return -1;
909 mapping = array_get(&(s->mapping), i);
910 } else {
911 assert(mapping->mode == MODE_UNDEFINED);
912 mapping->mode=MODE_NORMAL;
913 mapping->begin = cluster;
914 if (mapping->end > 0) {
915 direntry_t* direntry = array_get(&(s->directory),
916 mapping->dir_index);
918 mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
919 set_begin_of_direntry(direntry, mapping->begin);
920 } else {
921 mapping->end = cluster + 1;
922 fix_fat = 0;
926 assert(mapping->begin < mapping->end);
928 /* next free cluster */
929 cluster = mapping->end;
931 if(cluster > s->cluster_count) {
932 fprintf(stderr,"Directory does not fit in FAT%d (capacity %s)\n",
933 s->fat_type,
934 s->fat_type == 12 ? s->sector_count == 2880 ? "1.44 MB"
935 : "2.88 MB"
936 : "504MB");
937 return -EINVAL;
940 /* fix fat for entry */
941 if (fix_fat) {
942 int j;
943 for(j = mapping->begin; j < mapping->end - 1; j++)
944 fat_set(s, j, j+1);
945 fat_set(s, mapping->end - 1, s->max_fat_value);
949 mapping = array_get(&(s->mapping), 0);
950 s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
951 s->last_cluster_of_root_directory = mapping->end;
953 /* the FAT signature */
954 fat_set(s,0,s->max_fat_value);
955 fat_set(s,1,s->max_fat_value);
957 s->current_mapping = NULL;
959 bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
960 bootsector->jump[0]=0xeb;
961 bootsector->jump[1]=0x3e;
962 bootsector->jump[2]=0x90;
963 memcpy(bootsector->name,"QEMU ",8);
964 bootsector->sector_size=cpu_to_le16(0x200);
965 bootsector->sectors_per_cluster=s->sectors_per_cluster;
966 bootsector->reserved_sectors=cpu_to_le16(1);
967 bootsector->number_of_fats=0x2; /* number of FATs */
968 bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
969 bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
970 bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
971 s->fat.pointer[0] = bootsector->media_type;
972 bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
973 bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
974 bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
975 bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
976 bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
978 /* LATER TODO: if FAT32, this is wrong */
979 bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
980 bootsector->u.fat16.current_head=0;
981 bootsector->u.fat16.signature=0x29;
982 bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
984 memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
985 memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8);
986 bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
988 return 0;
991 #ifdef DEBUG
992 static BDRVVVFATState *vvv = NULL;
993 #endif
995 static int enable_write_target(BDRVVVFATState *s);
996 static int is_consistent(BDRVVVFATState *s);
998 static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
1000 BDRVVVFATState *s = bs->opaque;
1001 int floppy = 0;
1002 int i;
1004 #ifdef DEBUG
1005 vvv = s;
1006 #endif
1008 DLOG(if (stderr == NULL) {
1009 stderr = fopen("vvfat.log", "a");
1010 setbuf(stderr, NULL);
1013 s->bs = bs;
1015 s->fat_type=16;
1016 /* LATER TODO: if FAT32, adjust */
1017 s->sectors_per_cluster=0x10;
1018 /* 504MB disk*/
1019 bs->cyls=1024; bs->heads=16; bs->secs=63;
1021 s->current_cluster=0xffffffff;
1023 s->first_sectors_number=0x40;
1024 /* read only is the default for safety */
1025 bs->read_only = 1;
1026 s->qcow = s->write_target = NULL;
1027 s->qcow_filename = NULL;
1028 s->fat2 = NULL;
1029 s->downcase_short_names = 1;
1031 if (!strstart(dirname, "fat:", NULL))
1032 return -1;
1034 if (strstr(dirname, ":floppy:")) {
1035 floppy = 1;
1036 s->fat_type = 12;
1037 s->first_sectors_number = 1;
1038 s->sectors_per_cluster=2;
1039 bs->cyls = 80; bs->heads = 2; bs->secs = 36;
1042 s->sector_count=bs->cyls*bs->heads*bs->secs;
1044 if (strstr(dirname, ":32:")) {
1045 fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
1046 s->fat_type = 32;
1047 } else if (strstr(dirname, ":16:")) {
1048 s->fat_type = 16;
1049 } else if (strstr(dirname, ":12:")) {
1050 s->fat_type = 12;
1051 s->sector_count=2880;
1054 if (strstr(dirname, ":rw:")) {
1055 if (enable_write_target(s))
1056 return -1;
1057 bs->read_only = 0;
1060 i = strrchr(dirname, ':') - dirname;
1061 assert(i >= 3);
1062 if (dirname[i-2] == ':' && qemu_isalpha(dirname[i-1]))
1063 /* workaround for DOS drive names */
1064 dirname += i-1;
1065 else
1066 dirname += i+1;
1068 bs->total_sectors=bs->cyls*bs->heads*bs->secs;
1070 if(init_directories(s, dirname))
1071 return -1;
1073 s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
1075 if(s->first_sectors_number==0x40)
1076 init_mbr(s);
1078 /* for some reason or other, MS-DOS does not like to know about CHS... */
1079 if (floppy)
1080 bs->heads = bs->cyls = bs->secs = 0;
1082 // assert(is_consistent(s));
1083 return 0;
1086 static inline void vvfat_close_current_file(BDRVVVFATState *s)
1088 if(s->current_mapping) {
1089 s->current_mapping = NULL;
1090 if (s->current_fd) {
1091 close(s->current_fd);
1092 s->current_fd = 0;
1095 s->current_cluster = -1;
1098 /* mappings between index1 and index2-1 are supposed to be ordered
1099 * return value is the index of the last mapping for which end>cluster_num
1101 static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
1103 while(1) {
1104 int index3;
1105 mapping_t* mapping;
1106 index3=(index1+index2)/2;
1107 mapping=array_get(&(s->mapping),index3);
1108 assert(mapping->begin < mapping->end);
1109 if(mapping->begin>=cluster_num) {
1110 assert(index2!=index3 || index2==0);
1111 if(index2==index3)
1112 return index1;
1113 index2=index3;
1114 } else {
1115 if(index1==index3)
1116 return mapping->end<=cluster_num ? index2 : index1;
1117 index1=index3;
1119 assert(index1<=index2);
1120 DLOG(mapping=array_get(&(s->mapping),index1);
1121 assert(mapping->begin<=cluster_num);
1122 assert(index2 >= s->mapping.next ||
1123 ((mapping = array_get(&(s->mapping),index2)) &&
1124 mapping->end>cluster_num)));
1128 static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
1130 int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
1131 mapping_t* mapping;
1132 if(index>=s->mapping.next)
1133 return NULL;
1134 mapping=array_get(&(s->mapping),index);
1135 if(mapping->begin>cluster_num)
1136 return NULL;
1137 assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
1138 return mapping;
1142 * This function simply compares path == mapping->path. Since the mappings
1143 * are sorted by cluster, this is expensive: O(n).
1145 static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s,
1146 const char* path)
1148 int i;
1150 for (i = 0; i < s->mapping.next; i++) {
1151 mapping_t* mapping = array_get(&(s->mapping), i);
1152 if (mapping->first_mapping_index < 0 &&
1153 !strcmp(path, mapping->path))
1154 return mapping;
1157 return NULL;
1160 static int open_file(BDRVVVFATState* s,mapping_t* mapping)
1162 if(!mapping)
1163 return -1;
1164 if(!s->current_mapping ||
1165 strcmp(s->current_mapping->path,mapping->path)) {
1166 /* open file */
1167 int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
1168 if(fd<0)
1169 return -1;
1170 vvfat_close_current_file(s);
1171 s->current_fd = fd;
1172 s->current_mapping = mapping;
1174 return 0;
1177 static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
1179 if(s->current_cluster != cluster_num) {
1180 int result=0;
1181 off_t offset;
1182 assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
1183 if(!s->current_mapping
1184 || s->current_mapping->begin>cluster_num
1185 || s->current_mapping->end<=cluster_num) {
1186 /* binary search of mappings for file */
1187 mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
1189 assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
1191 if (mapping && mapping->mode & MODE_DIRECTORY) {
1192 vvfat_close_current_file(s);
1193 s->current_mapping = mapping;
1194 read_cluster_directory:
1195 offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
1196 s->cluster = (unsigned char*)s->directory.pointer+offset
1197 + 0x20*s->current_mapping->info.dir.first_dir_index;
1198 assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
1199 assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
1200 s->current_cluster = cluster_num;
1201 return 0;
1204 if(open_file(s,mapping))
1205 return -2;
1206 } else if (s->current_mapping->mode & MODE_DIRECTORY)
1207 goto read_cluster_directory;
1209 assert(s->current_fd);
1211 offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
1212 if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
1213 return -3;
1214 s->cluster=s->cluster_buffer;
1215 result=read(s->current_fd,s->cluster,s->cluster_size);
1216 if(result<0) {
1217 s->current_cluster = -1;
1218 return -1;
1220 s->current_cluster = cluster_num;
1222 return 0;
1225 #ifdef DEBUG
1226 static void hexdump(const void* address, uint32_t len)
1228 const unsigned char* p = address;
1229 int i, j;
1231 for (i = 0; i < len; i += 16) {
1232 for (j = 0; j < 16 && i + j < len; j++)
1233 fprintf(stderr, "%02x ", p[i + j]);
1234 for (; j < 16; j++)
1235 fprintf(stderr, " ");
1236 fprintf(stderr, " ");
1237 for (j = 0; j < 16 && i + j < len; j++)
1238 fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
1239 fprintf(stderr, "\n");
1243 static void print_direntry(const direntry_t* direntry)
1245 int j = 0;
1246 char buffer[1024];
1248 fprintf(stderr, "direntry %p: ", direntry);
1249 if(!direntry)
1250 return;
1251 if(is_long_name(direntry)) {
1252 unsigned char* c=(unsigned char*)direntry;
1253 int i;
1254 for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
1255 #define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;}
1256 ADD_CHAR(c[i]);
1257 for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
1258 ADD_CHAR(c[i]);
1259 for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
1260 ADD_CHAR(c[i]);
1261 buffer[j] = 0;
1262 fprintf(stderr, "%s\n", buffer);
1263 } else {
1264 int i;
1265 for(i=0;i<11;i++)
1266 ADD_CHAR(direntry->name[i]);
1267 buffer[j] = 0;
1268 fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
1269 buffer,
1270 direntry->attributes,
1271 begin_of_direntry(direntry),le32_to_cpu(direntry->size));
1275 static void print_mapping(const mapping_t* mapping)
1277 fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, "
1278 "first_mapping_index = %d, name = %s, mode = 0x%x, " ,
1279 mapping, mapping->begin, mapping->end, mapping->dir_index,
1280 mapping->first_mapping_index, mapping->path, mapping->mode);
1282 if (mapping->mode & MODE_DIRECTORY)
1283 fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
1284 else
1285 fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
1287 #endif
1289 static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
1290 uint8_t *buf, int nb_sectors)
1292 BDRVVVFATState *s = bs->opaque;
1293 int i;
1295 for(i=0;i<nb_sectors;i++,sector_num++) {
1296 if (sector_num >= s->sector_count)
1297 return -1;
1298 if (s->qcow) {
1299 int n;
1300 if (s->qcow->drv->bdrv_is_allocated(s->qcow,
1301 sector_num, nb_sectors-i, &n)) {
1302 DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
1303 if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
1304 return -1;
1305 i += n - 1;
1306 sector_num += n - 1;
1307 continue;
1309 DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
1311 if(sector_num<s->faked_sectors) {
1312 if(sector_num<s->first_sectors_number)
1313 memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
1314 else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
1315 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
1316 else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
1317 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
1318 } else {
1319 uint32_t sector=sector_num-s->faked_sectors,
1320 sector_offset_in_cluster=(sector%s->sectors_per_cluster),
1321 cluster_num=sector/s->sectors_per_cluster;
1322 if(read_cluster(s, cluster_num) != 0) {
1323 /* LATER TODO: strict: return -1; */
1324 memset(buf+i*0x200,0,0x200);
1325 continue;
1327 memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
1330 return 0;
1333 /* LATER TODO: statify all functions */
1336 * Idea of the write support (use snapshot):
1338 * 1. check if all data is consistent, recording renames, modifications,
1339 * new files and directories (in s->commits).
1341 * 2. if the data is not consistent, stop committing
1343 * 3. handle renames, and create new files and directories (do not yet
1344 * write their contents)
1346 * 4. walk the directories, fixing the mapping and direntries, and marking
1347 * the handled mappings as not deleted
1349 * 5. commit the contents of the files
1351 * 6. handle deleted files and directories
1355 typedef struct commit_t {
1356 char* path;
1357 union {
1358 struct { uint32_t cluster; } rename;
1359 struct { int dir_index; uint32_t modified_offset; } writeout;
1360 struct { uint32_t first_cluster; } new_file;
1361 struct { uint32_t cluster; } mkdir;
1362 } param;
1363 /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
1364 enum {
1365 ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
1366 } action;
1367 } commit_t;
1369 static void clear_commits(BDRVVVFATState* s)
1371 int i;
1372 DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
1373 for (i = 0; i < s->commits.next; i++) {
1374 commit_t* commit = array_get(&(s->commits), i);
1375 assert(commit->path || commit->action == ACTION_WRITEOUT);
1376 if (commit->action != ACTION_WRITEOUT) {
1377 assert(commit->path);
1378 free(commit->path);
1379 } else
1380 assert(commit->path == NULL);
1382 s->commits.next = 0;
1385 static void schedule_rename(BDRVVVFATState* s,
1386 uint32_t cluster, char* new_path)
1388 commit_t* commit = array_get_next(&(s->commits));
1389 commit->path = new_path;
1390 commit->param.rename.cluster = cluster;
1391 commit->action = ACTION_RENAME;
1394 static void schedule_writeout(BDRVVVFATState* s,
1395 int dir_index, uint32_t modified_offset)
1397 commit_t* commit = array_get_next(&(s->commits));
1398 commit->path = NULL;
1399 commit->param.writeout.dir_index = dir_index;
1400 commit->param.writeout.modified_offset = modified_offset;
1401 commit->action = ACTION_WRITEOUT;
1404 static void schedule_new_file(BDRVVVFATState* s,
1405 char* path, uint32_t first_cluster)
1407 commit_t* commit = array_get_next(&(s->commits));
1408 commit->path = path;
1409 commit->param.new_file.first_cluster = first_cluster;
1410 commit->action = ACTION_NEW_FILE;
1413 static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
1415 commit_t* commit = array_get_next(&(s->commits));
1416 commit->path = path;
1417 commit->param.mkdir.cluster = cluster;
1418 commit->action = ACTION_MKDIR;
1421 typedef struct {
1423 * Since the sequence number is at most 0x3f, and the filename
1424 * length is at most 13 times the sequence number, the maximal
1425 * filename length is 0x3f * 13 bytes.
1427 unsigned char name[0x3f * 13 + 1];
1428 int checksum, len;
1429 int sequence_number;
1430 } long_file_name;
1432 static void lfn_init(long_file_name* lfn)
1434 lfn->sequence_number = lfn->len = 0;
1435 lfn->checksum = 0x100;
1438 /* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
1439 static int parse_long_name(long_file_name* lfn,
1440 const direntry_t* direntry)
1442 int i, j, offset;
1443 const unsigned char* pointer = (const unsigned char*)direntry;
1445 if (!is_long_name(direntry))
1446 return 1;
1448 if (pointer[0] & 0x40) {
1449 lfn->sequence_number = pointer[0] & 0x3f;
1450 lfn->checksum = pointer[13];
1451 lfn->name[0] = 0;
1452 lfn->name[lfn->sequence_number * 13] = 0;
1453 } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
1454 return -1;
1455 else if (pointer[13] != lfn->checksum)
1456 return -2;
1457 else if (pointer[12] || pointer[26] || pointer[27])
1458 return -3;
1460 offset = 13 * (lfn->sequence_number - 1);
1461 for (i = 0, j = 1; i < 13; i++, j+=2) {
1462 if (j == 11)
1463 j = 14;
1464 else if (j == 26)
1465 j = 28;
1467 if (pointer[j+1] == 0)
1468 lfn->name[offset + i] = pointer[j];
1469 else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
1470 return -4;
1471 else
1472 lfn->name[offset + i] = 0;
1475 if (pointer[0] & 0x40)
1476 lfn->len = offset + strlen((char*)lfn->name + offset);
1478 return 0;
1481 /* returns 0 if successful, >0 if no short_name, and <0 on error */
1482 static int parse_short_name(BDRVVVFATState* s,
1483 long_file_name* lfn, direntry_t* direntry)
1485 int i, j;
1487 if (!is_short_name(direntry))
1488 return 1;
1490 for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
1491 for (i = 0; i <= j; i++) {
1492 if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
1493 return -1;
1494 else if (s->downcase_short_names)
1495 lfn->name[i] = qemu_tolower(direntry->name[i]);
1496 else
1497 lfn->name[i] = direntry->name[i];
1500 for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
1501 if (j >= 0) {
1502 lfn->name[i++] = '.';
1503 lfn->name[i + j + 1] = '\0';
1504 for (;j >= 0; j--) {
1505 if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
1506 return -2;
1507 else if (s->downcase_short_names)
1508 lfn->name[i + j] = qemu_tolower(direntry->extension[j]);
1509 else
1510 lfn->name[i + j] = direntry->extension[j];
1512 } else
1513 lfn->name[i + j + 1] = '\0';
1515 lfn->len = strlen((char*)lfn->name);
1517 return 0;
1520 static inline uint32_t modified_fat_get(BDRVVVFATState* s,
1521 unsigned int cluster)
1523 if (cluster < s->last_cluster_of_root_directory) {
1524 if (cluster + 1 == s->last_cluster_of_root_directory)
1525 return s->max_fat_value;
1526 else
1527 return cluster + 1;
1530 if (s->fat_type==32) {
1531 uint32_t* entry=((uint32_t*)s->fat2)+cluster;
1532 return le32_to_cpu(*entry);
1533 } else if (s->fat_type==16) {
1534 uint16_t* entry=((uint16_t*)s->fat2)+cluster;
1535 return le16_to_cpu(*entry);
1536 } else {
1537 const uint8_t* x=s->fat2+cluster*3/2;
1538 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
1542 static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
1544 int was_modified = 0;
1545 int i, dummy;
1547 if (s->qcow == NULL)
1548 return 0;
1550 for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
1551 was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
1552 cluster2sector(s, cluster_num) + i, 1, &dummy);
1554 return was_modified;
1557 static const char* get_basename(const char* path)
1559 char* basename = strrchr(path, '/');
1560 if (basename == NULL)
1561 return path;
1562 else
1563 return basename + 1; /* strip '/' */
1567 * The array s->used_clusters holds the states of the clusters. If it is
1568 * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
1569 * was modified, bit 3 is set.
1570 * If any cluster is allocated, but not part of a file or directory, this
1571 * driver refuses to commit.
1573 typedef enum {
1574 USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
1575 } used_t;
1578 * get_cluster_count_for_direntry() not only determines how many clusters
1579 * are occupied by direntry, but also if it was renamed or modified.
1581 * A file is thought to be renamed *only* if there already was a file with
1582 * exactly the same first cluster, but a different name.
1584 * Further, the files/directories handled by this function are
1585 * assumed to be *not* deleted (and *only* those).
1587 static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
1588 direntry_t* direntry, const char* path)
1591 * This is a little bit tricky:
1592 * IF the guest OS just inserts a cluster into the file chain,
1593 * and leaves the rest alone, (i.e. the original file had clusters
1594 * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
1596 * - do_commit will write the cluster into the file at the given
1597 * offset, but
1599 * - the cluster which is overwritten should be moved to a later
1600 * position in the file.
1602 * I am not aware that any OS does something as braindead, but this
1603 * situation could happen anyway when not committing for a long time.
1604 * Just to be sure that this does not bite us, detect it, and copy the
1605 * contents of the clusters to-be-overwritten into the qcow.
1607 int copy_it = 0;
1608 int was_modified = 0;
1609 int32_t ret = 0;
1611 uint32_t cluster_num = begin_of_direntry(direntry);
1612 uint32_t offset = 0;
1613 int first_mapping_index = -1;
1614 mapping_t* mapping = NULL;
1615 const char* basename2 = NULL;
1617 vvfat_close_current_file(s);
1619 /* the root directory */
1620 if (cluster_num == 0)
1621 return 0;
1623 /* write support */
1624 if (s->qcow) {
1625 basename2 = get_basename(path);
1627 mapping = find_mapping_for_cluster(s, cluster_num);
1629 if (mapping) {
1630 const char* basename;
1632 assert(mapping->mode & MODE_DELETED);
1633 mapping->mode &= ~MODE_DELETED;
1635 basename = get_basename(mapping->path);
1637 assert(mapping->mode & MODE_NORMAL);
1639 /* rename */
1640 if (strcmp(basename, basename2))
1641 schedule_rename(s, cluster_num, qemu_strdup(path));
1642 } else if (is_file(direntry))
1643 /* new file */
1644 schedule_new_file(s, qemu_strdup(path), cluster_num);
1645 else {
1646 abort();
1647 return 0;
1651 while(1) {
1652 if (s->qcow) {
1653 if (!copy_it && cluster_was_modified(s, cluster_num)) {
1654 if (mapping == NULL ||
1655 mapping->begin > cluster_num ||
1656 mapping->end <= cluster_num)
1657 mapping = find_mapping_for_cluster(s, cluster_num);
1660 if (mapping &&
1661 (mapping->mode & MODE_DIRECTORY) == 0) {
1663 /* was modified in qcow */
1664 if (offset != mapping->info.file.offset + s->cluster_size
1665 * (cluster_num - mapping->begin)) {
1666 /* offset of this cluster in file chain has changed */
1667 abort();
1668 copy_it = 1;
1669 } else if (offset == 0) {
1670 const char* basename = get_basename(mapping->path);
1672 if (strcmp(basename, basename2))
1673 copy_it = 1;
1674 first_mapping_index = array_index(&(s->mapping), mapping);
1677 if (mapping->first_mapping_index != first_mapping_index
1678 && mapping->info.file.offset > 0) {
1679 abort();
1680 copy_it = 1;
1683 /* need to write out? */
1684 if (!was_modified && is_file(direntry)) {
1685 was_modified = 1;
1686 schedule_writeout(s, mapping->dir_index, offset);
1691 if (copy_it) {
1692 int i, dummy;
1694 * This is horribly inefficient, but that is okay, since
1695 * it is rarely executed, if at all.
1697 int64_t offset = cluster2sector(s, cluster_num);
1699 vvfat_close_current_file(s);
1700 for (i = 0; i < s->sectors_per_cluster; i++)
1701 if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
1702 offset + i, 1, &dummy)) {
1703 if (vvfat_read(s->bs,
1704 offset, s->cluster_buffer, 1))
1705 return -1;
1706 if (s->qcow->drv->bdrv_write(s->qcow,
1707 offset, s->cluster_buffer, 1))
1708 return -2;
1713 ret++;
1714 if (s->used_clusters[cluster_num] & USED_ANY)
1715 return 0;
1716 s->used_clusters[cluster_num] = USED_FILE;
1718 cluster_num = modified_fat_get(s, cluster_num);
1720 if (fat_eof(s, cluster_num))
1721 return ret;
1722 else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
1723 return -1;
1725 offset += s->cluster_size;
1730 * This function looks at the modified data (qcow).
1731 * It returns 0 upon inconsistency or error, and the number of clusters
1732 * used by the directory, its subdirectories and their files.
1734 static int check_directory_consistency(BDRVVVFATState *s,
1735 int cluster_num, const char* path)
1737 int ret = 0;
1738 unsigned char* cluster = qemu_malloc(s->cluster_size);
1739 direntry_t* direntries = (direntry_t*)cluster;
1740 mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
1742 long_file_name lfn;
1743 int path_len = strlen(path);
1744 char path2[PATH_MAX];
1746 assert(path_len < PATH_MAX); /* len was tested before! */
1747 pstrcpy(path2, sizeof(path2), path);
1748 path2[path_len] = '/';
1749 path2[path_len + 1] = '\0';
1751 if (mapping) {
1752 const char* basename = get_basename(mapping->path);
1753 const char* basename2 = get_basename(path);
1755 assert(mapping->mode & MODE_DIRECTORY);
1757 assert(mapping->mode & MODE_DELETED);
1758 mapping->mode &= ~MODE_DELETED;
1760 if (strcmp(basename, basename2))
1761 schedule_rename(s, cluster_num, qemu_strdup(path));
1762 } else
1763 /* new directory */
1764 schedule_mkdir(s, cluster_num, qemu_strdup(path));
1766 lfn_init(&lfn);
1767 do {
1768 int i;
1769 int subret = 0;
1771 ret++;
1773 if (s->used_clusters[cluster_num] & USED_ANY) {
1774 fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
1775 return 0;
1777 s->used_clusters[cluster_num] = USED_DIRECTORY;
1779 DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
1780 subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
1781 s->sectors_per_cluster);
1782 if (subret) {
1783 fprintf(stderr, "Error fetching direntries\n");
1784 fail:
1785 free(cluster);
1786 return 0;
1789 for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
1790 int cluster_count = 0;
1792 DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i));
1793 if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
1794 is_free(direntries + i))
1795 continue;
1797 subret = parse_long_name(&lfn, direntries + i);
1798 if (subret < 0) {
1799 fprintf(stderr, "Error in long name\n");
1800 goto fail;
1802 if (subret == 0 || is_free(direntries + i))
1803 continue;
1805 if (fat_chksum(direntries+i) != lfn.checksum) {
1806 subret = parse_short_name(s, &lfn, direntries + i);
1807 if (subret < 0) {
1808 fprintf(stderr, "Error in short name (%d)\n", subret);
1809 goto fail;
1811 if (subret > 0 || !strcmp((char*)lfn.name, ".")
1812 || !strcmp((char*)lfn.name, ".."))
1813 continue;
1815 lfn.checksum = 0x100; /* cannot use long name twice */
1817 if (path_len + 1 + lfn.len >= PATH_MAX) {
1818 fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
1819 goto fail;
1821 pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
1822 (char*)lfn.name);
1824 if (is_directory(direntries + i)) {
1825 if (begin_of_direntry(direntries + i) == 0) {
1826 DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
1827 goto fail;
1829 cluster_count = check_directory_consistency(s,
1830 begin_of_direntry(direntries + i), path2);
1831 if (cluster_count == 0) {
1832 DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
1833 goto fail;
1835 } else if (is_file(direntries + i)) {
1836 /* check file size with FAT */
1837 cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
1838 if (cluster_count !=
1839 (le32_to_cpu(direntries[i].size) + s->cluster_size
1840 - 1) / s->cluster_size) {
1841 DLOG(fprintf(stderr, "Cluster count mismatch\n"));
1842 goto fail;
1844 } else
1845 abort(); /* cluster_count = 0; */
1847 ret += cluster_count;
1850 cluster_num = modified_fat_get(s, cluster_num);
1851 } while(!fat_eof(s, cluster_num));
1853 free(cluster);
1854 return ret;
1857 /* returns 1 on success */
1858 static int is_consistent(BDRVVVFATState* s)
1860 int i, check;
1861 int used_clusters_count = 0;
1863 DLOG(checkpoint());
1865 * - get modified FAT
1866 * - compare the two FATs (TODO)
1867 * - get buffer for marking used clusters
1868 * - recurse direntries from root (using bs->bdrv_read to make
1869 * sure to get the new data)
1870 * - check that the FAT agrees with the size
1871 * - count the number of clusters occupied by this directory and
1872 * its files
1873 * - check that the cumulative used cluster count agrees with the
1874 * FAT
1875 * - if all is fine, return number of used clusters
1877 if (s->fat2 == NULL) {
1878 int size = 0x200 * s->sectors_per_fat;
1879 s->fat2 = qemu_malloc(size);
1880 memcpy(s->fat2, s->fat.pointer, size);
1882 check = vvfat_read(s->bs,
1883 s->first_sectors_number, s->fat2, s->sectors_per_fat);
1884 if (check) {
1885 fprintf(stderr, "Could not copy fat\n");
1886 return 0;
1888 assert (s->used_clusters);
1889 for (i = 0; i < sector2cluster(s, s->sector_count); i++)
1890 s->used_clusters[i] &= ~USED_ANY;
1892 clear_commits(s);
1894 /* mark every mapped file/directory as deleted.
1895 * (check_directory_consistency() will unmark those still present). */
1896 if (s->qcow)
1897 for (i = 0; i < s->mapping.next; i++) {
1898 mapping_t* mapping = array_get(&(s->mapping), i);
1899 if (mapping->first_mapping_index < 0)
1900 mapping->mode |= MODE_DELETED;
1903 used_clusters_count = check_directory_consistency(s, 0, s->path);
1904 if (used_clusters_count <= 0) {
1905 DLOG(fprintf(stderr, "problem in directory\n"));
1906 return 0;
1909 check = s->last_cluster_of_root_directory;
1910 for (i = check; i < sector2cluster(s, s->sector_count); i++) {
1911 if (modified_fat_get(s, i)) {
1912 if(!s->used_clusters[i]) {
1913 DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
1914 return 0;
1916 check++;
1919 if (s->used_clusters[i] == USED_ALLOCATED) {
1920 /* allocated, but not used... */
1921 DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
1922 return 0;
1926 if (check != used_clusters_count)
1927 return 0;
1929 return used_clusters_count;
1932 static inline void adjust_mapping_indices(BDRVVVFATState* s,
1933 int offset, int adjust)
1935 int i;
1937 for (i = 0; i < s->mapping.next; i++) {
1938 mapping_t* mapping = array_get(&(s->mapping), i);
1940 #define ADJUST_MAPPING_INDEX(name) \
1941 if (mapping->name >= offset) \
1942 mapping->name += adjust
1944 ADJUST_MAPPING_INDEX(first_mapping_index);
1945 if (mapping->mode & MODE_DIRECTORY)
1946 ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
1950 /* insert or update mapping */
1951 static mapping_t* insert_mapping(BDRVVVFATState* s,
1952 uint32_t begin, uint32_t end)
1955 * - find mapping where mapping->begin >= begin,
1956 * - if mapping->begin > begin: insert
1957 * - adjust all references to mappings!
1958 * - else: adjust
1959 * - replace name
1961 int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
1962 mapping_t* mapping = NULL;
1963 mapping_t* first_mapping = array_get(&(s->mapping), 0);
1965 if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
1966 && mapping->begin < begin) {
1967 mapping->end = begin;
1968 index++;
1969 mapping = array_get(&(s->mapping), index);
1971 if (index >= s->mapping.next || mapping->begin > begin) {
1972 mapping = array_insert(&(s->mapping), index, 1);
1973 mapping->path = NULL;
1974 adjust_mapping_indices(s, index, +1);
1977 mapping->begin = begin;
1978 mapping->end = end;
1980 DLOG(mapping_t* next_mapping;
1981 assert(index + 1 >= s->mapping.next ||
1982 ((next_mapping = array_get(&(s->mapping), index + 1)) &&
1983 next_mapping->begin >= end)));
1985 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1986 s->current_mapping = array_get(&(s->mapping),
1987 s->current_mapping - first_mapping);
1989 return mapping;
1992 static int remove_mapping(BDRVVVFATState* s, int mapping_index)
1994 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
1995 mapping_t* first_mapping = array_get(&(s->mapping), 0);
1997 /* free mapping */
1998 if (mapping->first_mapping_index < 0)
1999 free(mapping->path);
2001 /* remove from s->mapping */
2002 array_remove(&(s->mapping), mapping_index);
2004 /* adjust all references to mappings */
2005 adjust_mapping_indices(s, mapping_index, -1);
2007 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
2008 s->current_mapping = array_get(&(s->mapping),
2009 s->current_mapping - first_mapping);
2011 return 0;
2014 static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
2016 int i;
2017 for (i = 0; i < s->mapping.next; i++) {
2018 mapping_t* mapping = array_get(&(s->mapping), i);
2019 if (mapping->dir_index >= offset)
2020 mapping->dir_index += adjust;
2021 if ((mapping->mode & MODE_DIRECTORY) &&
2022 mapping->info.dir.first_dir_index >= offset)
2023 mapping->info.dir.first_dir_index += adjust;
2027 static direntry_t* insert_direntries(BDRVVVFATState* s,
2028 int dir_index, int count)
2031 * make room in s->directory,
2032 * adjust_dirindices
2034 direntry_t* result = array_insert(&(s->directory), dir_index, count);
2035 if (result == NULL)
2036 return NULL;
2037 adjust_dirindices(s, dir_index, count);
2038 return result;
2041 static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
2043 int ret = array_remove_slice(&(s->directory), dir_index, count);
2044 if (ret)
2045 return ret;
2046 adjust_dirindices(s, dir_index, -count);
2047 return 0;
2051 * Adapt the mappings of the cluster chain starting at first cluster
2052 * (i.e. if a file starts at first_cluster, the chain is followed according
2053 * to the modified fat, and the corresponding entries in s->mapping are
2054 * adjusted)
2056 static int commit_mappings(BDRVVVFATState* s,
2057 uint32_t first_cluster, int dir_index)
2059 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2060 direntry_t* direntry = array_get(&(s->directory), dir_index);
2061 uint32_t cluster = first_cluster;
2063 vvfat_close_current_file(s);
2065 assert(mapping);
2066 assert(mapping->begin == first_cluster);
2067 mapping->first_mapping_index = -1;
2068 mapping->dir_index = dir_index;
2069 mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
2070 MODE_DIRECTORY : MODE_NORMAL;
2072 while (!fat_eof(s, cluster)) {
2073 uint32_t c, c1;
2075 for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
2076 c = c1, c1 = modified_fat_get(s, c1));
2078 c++;
2079 if (c > mapping->end) {
2080 int index = array_index(&(s->mapping), mapping);
2081 int i, max_i = s->mapping.next - index;
2082 for (i = 1; i < max_i && mapping[i].begin < c; i++);
2083 while (--i > 0)
2084 remove_mapping(s, index + 1);
2086 assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
2087 || mapping[1].begin >= c);
2088 mapping->end = c;
2090 if (!fat_eof(s, c1)) {
2091 int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
2092 mapping_t* next_mapping = i >= s->mapping.next ? NULL :
2093 array_get(&(s->mapping), i);
2095 if (next_mapping == NULL || next_mapping->begin > c1) {
2096 int i1 = array_index(&(s->mapping), mapping);
2098 next_mapping = insert_mapping(s, c1, c1+1);
2100 if (c1 < c)
2101 i1++;
2102 mapping = array_get(&(s->mapping), i1);
2105 next_mapping->dir_index = mapping->dir_index;
2106 next_mapping->first_mapping_index =
2107 mapping->first_mapping_index < 0 ?
2108 array_index(&(s->mapping), mapping) :
2109 mapping->first_mapping_index;
2110 next_mapping->path = mapping->path;
2111 next_mapping->mode = mapping->mode;
2112 next_mapping->read_only = mapping->read_only;
2113 if (mapping->mode & MODE_DIRECTORY) {
2114 next_mapping->info.dir.parent_mapping_index =
2115 mapping->info.dir.parent_mapping_index;
2116 next_mapping->info.dir.first_dir_index =
2117 mapping->info.dir.first_dir_index +
2118 0x10 * s->sectors_per_cluster *
2119 (mapping->end - mapping->begin);
2120 } else
2121 next_mapping->info.file.offset = mapping->info.file.offset +
2122 mapping->end - mapping->begin;
2124 mapping = next_mapping;
2127 cluster = c1;
2130 return 0;
2133 static int commit_direntries(BDRVVVFATState* s,
2134 int dir_index, int parent_mapping_index)
2136 direntry_t* direntry = array_get(&(s->directory), dir_index);
2137 uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
2138 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2140 int factor = 0x10 * s->sectors_per_cluster;
2141 int old_cluster_count, new_cluster_count;
2142 int current_dir_index = mapping->info.dir.first_dir_index;
2143 int first_dir_index = current_dir_index;
2144 int ret, i;
2145 uint32_t c;
2147 DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
2149 assert(direntry);
2150 assert(mapping);
2151 assert(mapping->begin == first_cluster);
2152 assert(mapping->info.dir.first_dir_index < s->directory.next);
2153 assert(mapping->mode & MODE_DIRECTORY);
2154 assert(dir_index == 0 || is_directory(direntry));
2156 mapping->info.dir.parent_mapping_index = parent_mapping_index;
2158 if (first_cluster == 0) {
2159 old_cluster_count = new_cluster_count =
2160 s->last_cluster_of_root_directory;
2161 } else {
2162 for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2163 c = fat_get(s, c))
2164 old_cluster_count++;
2166 for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2167 c = modified_fat_get(s, c))
2168 new_cluster_count++;
2171 if (new_cluster_count > old_cluster_count) {
2172 if (insert_direntries(s,
2173 current_dir_index + factor * old_cluster_count,
2174 factor * (new_cluster_count - old_cluster_count)) == NULL)
2175 return -1;
2176 } else if (new_cluster_count < old_cluster_count)
2177 remove_direntries(s,
2178 current_dir_index + factor * new_cluster_count,
2179 factor * (old_cluster_count - new_cluster_count));
2181 for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
2182 void* direntry = array_get(&(s->directory), current_dir_index);
2183 int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
2184 s->sectors_per_cluster);
2185 if (ret)
2186 return ret;
2187 assert(!strncmp(s->directory.pointer, "QEMU", 4));
2188 current_dir_index += factor;
2191 ret = commit_mappings(s, first_cluster, dir_index);
2192 if (ret)
2193 return ret;
2195 /* recurse */
2196 for (i = 0; i < factor * new_cluster_count; i++) {
2197 direntry = array_get(&(s->directory), first_dir_index + i);
2198 if (is_directory(direntry) && !is_dot(direntry)) {
2199 mapping = find_mapping_for_cluster(s, first_cluster);
2200 assert(mapping->mode & MODE_DIRECTORY);
2201 ret = commit_direntries(s, first_dir_index + i,
2202 array_index(&(s->mapping), mapping));
2203 if (ret)
2204 return ret;
2208 return 0;
2211 /* commit one file (adjust contents, adjust mapping),
2212 return first_mapping_index */
2213 static int commit_one_file(BDRVVVFATState* s,
2214 int dir_index, uint32_t offset)
2216 direntry_t* direntry = array_get(&(s->directory), dir_index);
2217 uint32_t c = begin_of_direntry(direntry);
2218 uint32_t first_cluster = c;
2219 mapping_t* mapping = find_mapping_for_cluster(s, c);
2220 uint32_t size = filesize_of_direntry(direntry);
2221 char* cluster = qemu_malloc(s->cluster_size);
2222 uint32_t i;
2223 int fd = 0;
2225 assert(offset < size);
2226 assert((offset % s->cluster_size) == 0);
2228 for (i = s->cluster_size; i < offset; i += s->cluster_size)
2229 c = modified_fat_get(s, c);
2231 fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
2232 if (fd < 0) {
2233 fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
2234 strerror(errno), errno);
2235 return fd;
2237 if (offset > 0)
2238 if (lseek(fd, offset, SEEK_SET) != offset)
2239 return -3;
2241 while (offset < size) {
2242 uint32_t c1;
2243 int rest_size = (size - offset > s->cluster_size ?
2244 s->cluster_size : size - offset);
2245 int ret;
2247 c1 = modified_fat_get(s, c);
2249 assert((size - offset == 0 && fat_eof(s, c)) ||
2250 (size > offset && c >=2 && !fat_eof(s, c)));
2252 ret = vvfat_read(s->bs, cluster2sector(s, c),
2253 (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
2255 if (ret < 0)
2256 return ret;
2258 if (write(fd, cluster, rest_size) < 0)
2259 return -2;
2261 offset += rest_size;
2262 c = c1;
2265 if (ftruncate(fd, size)) {
2266 perror("ftruncate()");
2267 close(fd);
2268 return -4;
2270 close(fd);
2272 return commit_mappings(s, first_cluster, dir_index);
2275 #ifdef DEBUG
2276 /* test, if all mappings point to valid direntries */
2277 static void check1(BDRVVVFATState* s)
2279 int i;
2280 for (i = 0; i < s->mapping.next; i++) {
2281 mapping_t* mapping = array_get(&(s->mapping), i);
2282 if (mapping->mode & MODE_DELETED) {
2283 fprintf(stderr, "deleted\n");
2284 continue;
2286 assert(mapping->dir_index < s->directory.next);
2287 direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
2288 assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
2289 if (mapping->mode & MODE_DIRECTORY) {
2290 assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
2291 assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
2296 /* test, if all direntries have mappings */
2297 static void check2(BDRVVVFATState* s)
2299 int i;
2300 int first_mapping = -1;
2302 for (i = 0; i < s->directory.next; i++) {
2303 direntry_t* direntry = array_get(&(s->directory), i);
2305 if (is_short_name(direntry) && begin_of_direntry(direntry)) {
2306 mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
2307 assert(mapping);
2308 assert(mapping->dir_index == i || is_dot(direntry));
2309 assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
2312 if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
2313 /* cluster start */
2314 int j, count = 0;
2316 for (j = 0; j < s->mapping.next; j++) {
2317 mapping_t* mapping = array_get(&(s->mapping), j);
2318 if (mapping->mode & MODE_DELETED)
2319 continue;
2320 if (mapping->mode & MODE_DIRECTORY) {
2321 if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
2322 assert(++count == 1);
2323 if (mapping->first_mapping_index == -1)
2324 first_mapping = array_index(&(s->mapping), mapping);
2325 else
2326 assert(first_mapping == mapping->first_mapping_index);
2327 if (mapping->info.dir.parent_mapping_index < 0)
2328 assert(j == 0);
2329 else {
2330 mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
2331 assert(parent->mode & MODE_DIRECTORY);
2332 assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
2337 if (count == 0)
2338 first_mapping = -1;
2342 #endif
2344 static int handle_renames_and_mkdirs(BDRVVVFATState* s)
2346 int i;
2348 #ifdef DEBUG
2349 fprintf(stderr, "handle_renames\n");
2350 for (i = 0; i < s->commits.next; i++) {
2351 commit_t* commit = array_get(&(s->commits), i);
2352 fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
2354 #endif
2356 for (i = 0; i < s->commits.next;) {
2357 commit_t* commit = array_get(&(s->commits), i);
2358 if (commit->action == ACTION_RENAME) {
2359 mapping_t* mapping = find_mapping_for_cluster(s,
2360 commit->param.rename.cluster);
2361 char* old_path = mapping->path;
2363 assert(commit->path);
2364 mapping->path = commit->path;
2365 if (rename(old_path, mapping->path))
2366 return -2;
2368 if (mapping->mode & MODE_DIRECTORY) {
2369 int l1 = strlen(mapping->path);
2370 int l2 = strlen(old_path);
2371 int diff = l1 - l2;
2372 direntry_t* direntry = array_get(&(s->directory),
2373 mapping->info.dir.first_dir_index);
2374 uint32_t c = mapping->begin;
2375 int i = 0;
2377 /* recurse */
2378 while (!fat_eof(s, c)) {
2379 do {
2380 direntry_t* d = direntry + i;
2382 if (is_file(d) || (is_directory(d) && !is_dot(d))) {
2383 mapping_t* m = find_mapping_for_cluster(s,
2384 begin_of_direntry(d));
2385 int l = strlen(m->path);
2386 char* new_path = qemu_malloc(l + diff + 1);
2388 assert(!strncmp(m->path, mapping->path, l2));
2390 pstrcpy(new_path, l + diff + 1, mapping->path);
2391 pstrcpy(new_path + l1, l + diff + 1 - l1,
2392 m->path + l2);
2394 schedule_rename(s, m->begin, new_path);
2396 i++;
2397 } while((i % (0x10 * s->sectors_per_cluster)) != 0);
2398 c = fat_get(s, c);
2402 free(old_path);
2403 array_remove(&(s->commits), i);
2404 continue;
2405 } else if (commit->action == ACTION_MKDIR) {
2406 mapping_t* mapping;
2407 int j, parent_path_len;
2409 #ifdef __MINGW32__
2410 if (mkdir(commit->path))
2411 return -5;
2412 #else
2413 if (mkdir(commit->path, 0755))
2414 return -5;
2415 #endif
2417 mapping = insert_mapping(s, commit->param.mkdir.cluster,
2418 commit->param.mkdir.cluster + 1);
2419 if (mapping == NULL)
2420 return -6;
2422 mapping->mode = MODE_DIRECTORY;
2423 mapping->read_only = 0;
2424 mapping->path = commit->path;
2425 j = s->directory.next;
2426 assert(j);
2427 insert_direntries(s, s->directory.next,
2428 0x10 * s->sectors_per_cluster);
2429 mapping->info.dir.first_dir_index = j;
2431 parent_path_len = strlen(commit->path)
2432 - strlen(get_basename(commit->path)) - 1;
2433 for (j = 0; j < s->mapping.next; j++) {
2434 mapping_t* m = array_get(&(s->mapping), j);
2435 if (m->first_mapping_index < 0 && m != mapping &&
2436 !strncmp(m->path, mapping->path, parent_path_len) &&
2437 strlen(m->path) == parent_path_len)
2438 break;
2440 assert(j < s->mapping.next);
2441 mapping->info.dir.parent_mapping_index = j;
2443 array_remove(&(s->commits), i);
2444 continue;
2447 i++;
2449 return 0;
2453 * TODO: make sure that the short name is not matching *another* file
2455 static int handle_commits(BDRVVVFATState* s)
2457 int i, fail = 0;
2459 vvfat_close_current_file(s);
2461 for (i = 0; !fail && i < s->commits.next; i++) {
2462 commit_t* commit = array_get(&(s->commits), i);
2463 switch(commit->action) {
2464 case ACTION_RENAME: case ACTION_MKDIR:
2465 abort();
2466 fail = -2;
2467 break;
2468 case ACTION_WRITEOUT: {
2469 #ifndef NDEBUG
2470 /* these variables are only used by assert() below */
2471 direntry_t* entry = array_get(&(s->directory),
2472 commit->param.writeout.dir_index);
2473 uint32_t begin = begin_of_direntry(entry);
2474 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2475 #endif
2477 assert(mapping);
2478 assert(mapping->begin == begin);
2479 assert(commit->path == NULL);
2481 if (commit_one_file(s, commit->param.writeout.dir_index,
2482 commit->param.writeout.modified_offset))
2483 fail = -3;
2485 break;
2487 case ACTION_NEW_FILE: {
2488 int begin = commit->param.new_file.first_cluster;
2489 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2490 direntry_t* entry;
2491 int i;
2493 /* find direntry */
2494 for (i = 0; i < s->directory.next; i++) {
2495 entry = array_get(&(s->directory), i);
2496 if (is_file(entry) && begin_of_direntry(entry) == begin)
2497 break;
2500 if (i >= s->directory.next) {
2501 fail = -6;
2502 continue;
2505 /* make sure there exists an initial mapping */
2506 if (mapping && mapping->begin != begin) {
2507 mapping->end = begin;
2508 mapping = NULL;
2510 if (mapping == NULL) {
2511 mapping = insert_mapping(s, begin, begin+1);
2513 /* most members will be fixed in commit_mappings() */
2514 assert(commit->path);
2515 mapping->path = commit->path;
2516 mapping->read_only = 0;
2517 mapping->mode = MODE_NORMAL;
2518 mapping->info.file.offset = 0;
2520 if (commit_one_file(s, i, 0))
2521 fail = -7;
2523 break;
2525 default:
2526 abort();
2529 if (i > 0 && array_remove_slice(&(s->commits), 0, i))
2530 return -1;
2531 return fail;
2534 static int handle_deletes(BDRVVVFATState* s)
2536 int i, deferred = 1, deleted = 1;
2538 /* delete files corresponding to mappings marked as deleted */
2539 /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
2540 while (deferred && deleted) {
2541 deferred = 0;
2542 deleted = 0;
2544 for (i = 1; i < s->mapping.next; i++) {
2545 mapping_t* mapping = array_get(&(s->mapping), i);
2546 if (mapping->mode & MODE_DELETED) {
2547 direntry_t* entry = array_get(&(s->directory),
2548 mapping->dir_index);
2550 if (is_free(entry)) {
2551 /* remove file/directory */
2552 if (mapping->mode & MODE_DIRECTORY) {
2553 int j, next_dir_index = s->directory.next,
2554 first_dir_index = mapping->info.dir.first_dir_index;
2556 if (rmdir(mapping->path) < 0) {
2557 if (errno == ENOTEMPTY) {
2558 deferred++;
2559 continue;
2560 } else
2561 return -5;
2564 for (j = 1; j < s->mapping.next; j++) {
2565 mapping_t* m = array_get(&(s->mapping), j);
2566 if (m->mode & MODE_DIRECTORY &&
2567 m->info.dir.first_dir_index >
2568 first_dir_index &&
2569 m->info.dir.first_dir_index <
2570 next_dir_index)
2571 next_dir_index =
2572 m->info.dir.first_dir_index;
2574 remove_direntries(s, first_dir_index,
2575 next_dir_index - first_dir_index);
2577 deleted++;
2579 } else {
2580 if (unlink(mapping->path))
2581 return -4;
2582 deleted++;
2584 DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
2585 remove_mapping(s, i);
2590 return 0;
2594 * synchronize mapping with new state:
2596 * - copy FAT (with bdrv_read)
2597 * - mark all filenames corresponding to mappings as deleted
2598 * - recurse direntries from root (using bs->bdrv_read)
2599 * - delete files corresponding to mappings marked as deleted
2601 static int do_commit(BDRVVVFATState* s)
2603 int ret = 0;
2605 /* the real meat are the commits. Nothing to do? Move along! */
2606 if (s->commits.next == 0)
2607 return 0;
2609 vvfat_close_current_file(s);
2611 ret = handle_renames_and_mkdirs(s);
2612 if (ret) {
2613 fprintf(stderr, "Error handling renames (%d)\n", ret);
2614 abort();
2615 return ret;
2618 /* copy FAT (with bdrv_read) */
2619 memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
2621 /* recurse direntries from root (using bs->bdrv_read) */
2622 ret = commit_direntries(s, 0, -1);
2623 if (ret) {
2624 fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
2625 abort();
2626 return ret;
2629 ret = handle_commits(s);
2630 if (ret) {
2631 fprintf(stderr, "Error handling commits (%d)\n", ret);
2632 abort();
2633 return ret;
2636 ret = handle_deletes(s);
2637 if (ret) {
2638 fprintf(stderr, "Error deleting\n");
2639 abort();
2640 return ret;
2643 s->qcow->drv->bdrv_make_empty(s->qcow);
2645 memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
2647 DLOG(checkpoint());
2648 return 0;
2651 static int try_commit(BDRVVVFATState* s)
2653 vvfat_close_current_file(s);
2654 DLOG(checkpoint());
2655 if(!is_consistent(s))
2656 return -1;
2657 return do_commit(s);
2660 static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
2661 const uint8_t *buf, int nb_sectors)
2663 BDRVVVFATState *s = bs->opaque;
2664 int i, ret;
2666 DLOG(checkpoint());
2668 /* Check if we're operating in read-only mode */
2669 if (s->qcow == NULL) {
2670 return -EACCES;
2673 vvfat_close_current_file(s);
2676 * Some sanity checks:
2677 * - do not allow writing to the boot sector
2678 * - do not allow to write non-ASCII filenames
2681 if (sector_num < s->first_sectors_number)
2682 return -1;
2684 for (i = sector2cluster(s, sector_num);
2685 i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
2686 mapping_t* mapping = find_mapping_for_cluster(s, i);
2687 if (mapping) {
2688 if (mapping->read_only) {
2689 fprintf(stderr, "Tried to write to write-protected file %s\n",
2690 mapping->path);
2691 return -1;
2694 if (mapping->mode & MODE_DIRECTORY) {
2695 int begin = cluster2sector(s, i);
2696 int end = begin + s->sectors_per_cluster, k;
2697 int dir_index;
2698 const direntry_t* direntries;
2699 long_file_name lfn;
2701 lfn_init(&lfn);
2703 if (begin < sector_num)
2704 begin = sector_num;
2705 if (end > sector_num + nb_sectors)
2706 end = sector_num + nb_sectors;
2707 dir_index = mapping->dir_index +
2708 0x10 * (begin - mapping->begin * s->sectors_per_cluster);
2709 direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
2711 for (k = 0; k < (end - begin) * 0x10; k++) {
2712 /* do not allow non-ASCII filenames */
2713 if (parse_long_name(&lfn, direntries + k) < 0) {
2714 fprintf(stderr, "Warning: non-ASCII filename\n");
2715 return -1;
2717 /* no access to the direntry of a read-only file */
2718 else if (is_short_name(direntries+k) &&
2719 (direntries[k].attributes & 1)) {
2720 if (memcmp(direntries + k,
2721 array_get(&(s->directory), dir_index + k),
2722 sizeof(direntry_t))) {
2723 fprintf(stderr, "Warning: tried to write to write-protected file\n");
2724 return -1;
2729 i = mapping->end;
2730 } else
2731 i++;
2735 * Use qcow backend. Commit later.
2737 DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
2738 ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
2739 if (ret < 0) {
2740 fprintf(stderr, "Error writing to qcow backend\n");
2741 return ret;
2744 for (i = sector2cluster(s, sector_num);
2745 i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
2746 if (i >= 0)
2747 s->used_clusters[i] |= USED_ALLOCATED;
2749 DLOG(checkpoint());
2750 /* TODO: add timeout */
2751 try_commit(s);
2753 DLOG(checkpoint());
2754 return 0;
2757 static int vvfat_is_allocated(BlockDriverState *bs,
2758 int64_t sector_num, int nb_sectors, int* n)
2760 BDRVVVFATState* s = bs->opaque;
2761 *n = s->sector_count - sector_num;
2762 if (*n > nb_sectors)
2763 *n = nb_sectors;
2764 else if (*n < 0)
2765 return 0;
2766 return 1;
2769 static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
2770 const uint8_t* buffer, int nb_sectors) {
2771 BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
2772 return try_commit(s);
2775 static void write_target_close(BlockDriverState *bs) {
2776 BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
2777 bdrv_delete(s->qcow);
2778 free(s->qcow_filename);
2781 static BlockDriver vvfat_write_target = {
2782 .format_name = "vvfat_write_target",
2783 .bdrv_write = write_target_commit,
2784 .bdrv_close = write_target_close,
2787 static int enable_write_target(BDRVVVFATState *s)
2789 BlockDriver *bdrv_qcow;
2790 QEMUOptionParameter *options;
2791 int ret;
2792 int size = sector2cluster(s, s->sector_count);
2793 s->used_clusters = calloc(size, 1);
2795 array_init(&(s->commits), sizeof(commit_t));
2797 s->qcow_filename = qemu_malloc(1024);
2798 get_tmp_filename(s->qcow_filename, 1024);
2800 bdrv_qcow = bdrv_find_format("qcow");
2801 options = parse_option_parameters("", bdrv_qcow->create_options, NULL);
2802 set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
2803 set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
2805 if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0)
2806 return -1;
2808 s->qcow = bdrv_new("");
2809 if (s->qcow == NULL) {
2810 return -1;
2813 ret = bdrv_open(s->qcow, s->qcow_filename,
2814 BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
2815 if (ret < 0) {
2816 return ret;
2819 #ifndef _WIN32
2820 unlink(s->qcow_filename);
2821 #endif
2823 s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
2824 s->bs->backing_hd->drv = &vvfat_write_target;
2825 s->bs->backing_hd->opaque = qemu_malloc(sizeof(void*));
2826 *(void**)s->bs->backing_hd->opaque = s;
2828 return 0;
2831 static void vvfat_close(BlockDriverState *bs)
2833 BDRVVVFATState *s = bs->opaque;
2835 vvfat_close_current_file(s);
2836 array_free(&(s->fat));
2837 array_free(&(s->directory));
2838 array_free(&(s->mapping));
2839 if(s->cluster_buffer)
2840 free(s->cluster_buffer);
2843 static BlockDriver bdrv_vvfat = {
2844 .format_name = "vvfat",
2845 .instance_size = sizeof(BDRVVVFATState),
2846 .bdrv_file_open = vvfat_open,
2847 .bdrv_read = vvfat_read,
2848 .bdrv_write = vvfat_write,
2849 .bdrv_close = vvfat_close,
2850 .bdrv_is_allocated = vvfat_is_allocated,
2851 .protocol_name = "fat",
2854 static void bdrv_vvfat_init(void)
2856 bdrv_register(&bdrv_vvfat);
2859 block_init(bdrv_vvfat_init);
2861 #ifdef DEBUG
2862 static void checkpoint(void) {
2863 assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
2864 check1(vvv);
2865 check2(vvv);
2866 assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
2867 #if 0
2868 if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
2869 fprintf(stderr, "Nonono!\n");
2870 mapping_t* mapping;
2871 direntry_t* direntry;
2872 assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
2873 assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
2874 if (vvv->mapping.next<47)
2875 return;
2876 assert((mapping = array_get(&(vvv->mapping), 47)));
2877 assert(mapping->dir_index < vvv->directory.next);
2878 direntry = array_get(&(vvv->directory), mapping->dir_index);
2879 assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0);
2880 #endif
2881 return;
2882 /* avoid compiler warnings: */
2883 hexdump(NULL, 100);
2884 remove_mapping(vvv, 0);
2885 print_mapping(NULL);
2886 print_direntry(NULL);
2888 #endif