usb-host: fix host close
[qemu.git] / block / vvfat.c
blobe1fcdbc45bfd2926a2beefafdf9008d03a044979
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 g_free(array->pointer);
90 array->size=array->next=0;
93 /* does not automatically grow */
94 static inline void* array_get(array_t* array,unsigned int index) {
95 assert(index < array->next);
96 return array->pointer + index * array->item_size;
99 static inline int array_ensure_allocated(array_t* array, int index)
101 if((index + 1) * array->item_size > array->size) {
102 int new_size = (index + 32) * array->item_size;
103 array->pointer = g_realloc(array->pointer, new_size);
104 if (!array->pointer)
105 return -1;
106 array->size = new_size;
107 array->next = index + 1;
110 return 0;
113 static inline void* array_get_next(array_t* array) {
114 unsigned int next = array->next;
115 void* result;
117 if (array_ensure_allocated(array, next) < 0)
118 return NULL;
120 array->next = next + 1;
121 result = array_get(array, next);
123 return result;
126 static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
127 if((array->next+count)*array->item_size>array->size) {
128 int increment=count*array->item_size;
129 array->pointer=g_realloc(array->pointer,array->size+increment);
130 if(!array->pointer)
131 return NULL;
132 array->size+=increment;
134 memmove(array->pointer+(index+count)*array->item_size,
135 array->pointer+index*array->item_size,
136 (array->next-index)*array->item_size);
137 array->next+=count;
138 return array->pointer+index*array->item_size;
141 /* this performs a "roll", so that the element which was at index_from becomes
142 * index_to, but the order of all other elements is preserved. */
143 static inline int array_roll(array_t* array,int index_to,int index_from,int count)
145 char* buf;
146 char* from;
147 char* to;
148 int is;
150 if(!array ||
151 index_to<0 || index_to>=array->next ||
152 index_from<0 || index_from>=array->next)
153 return -1;
155 if(index_to==index_from)
156 return 0;
158 is=array->item_size;
159 from=array->pointer+index_from*is;
160 to=array->pointer+index_to*is;
161 buf=g_malloc(is*count);
162 memcpy(buf,from,is*count);
164 if(index_to<index_from)
165 memmove(to+is*count,to,from-to);
166 else
167 memmove(from,from+is*count,to-from);
169 memcpy(to,buf,is*count);
171 g_free(buf);
173 return 0;
176 static inline int array_remove_slice(array_t* array,int index, int count)
178 assert(index >=0);
179 assert(count > 0);
180 assert(index + count <= array->next);
181 if(array_roll(array,array->next-1,index,count))
182 return -1;
183 array->next -= count;
184 return 0;
187 static int array_remove(array_t* array,int index)
189 return array_remove_slice(array, index, 1);
192 /* return the index for a given member */
193 static int array_index(array_t* array, void* pointer)
195 size_t offset = (char*)pointer - array->pointer;
196 assert((offset % array->item_size) == 0);
197 assert(offset/array->item_size < array->next);
198 return offset/array->item_size;
201 /* These structures are used to fake a disk and the VFAT filesystem.
202 * For this reason we need to use QEMU_PACKED. */
204 typedef struct bootsector_t {
205 uint8_t jump[3];
206 uint8_t name[8];
207 uint16_t sector_size;
208 uint8_t sectors_per_cluster;
209 uint16_t reserved_sectors;
210 uint8_t number_of_fats;
211 uint16_t root_entries;
212 uint16_t total_sectors16;
213 uint8_t media_type;
214 uint16_t sectors_per_fat;
215 uint16_t sectors_per_track;
216 uint16_t number_of_heads;
217 uint32_t hidden_sectors;
218 uint32_t total_sectors;
219 union {
220 struct {
221 uint8_t drive_number;
222 uint8_t current_head;
223 uint8_t signature;
224 uint32_t id;
225 uint8_t volume_label[11];
226 } QEMU_PACKED fat16;
227 struct {
228 uint32_t sectors_per_fat;
229 uint16_t flags;
230 uint8_t major,minor;
231 uint32_t first_cluster_of_root_directory;
232 uint16_t info_sector;
233 uint16_t backup_boot_sector;
234 uint16_t ignored;
235 } QEMU_PACKED fat32;
236 } u;
237 uint8_t fat_type[8];
238 uint8_t ignored[0x1c0];
239 uint8_t magic[2];
240 } QEMU_PACKED bootsector_t;
242 typedef struct {
243 uint8_t head;
244 uint8_t sector;
245 uint8_t cylinder;
246 } mbr_chs_t;
248 typedef struct partition_t {
249 uint8_t attributes; /* 0x80 = bootable */
250 mbr_chs_t start_CHS;
251 uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
252 mbr_chs_t end_CHS;
253 uint32_t start_sector_long;
254 uint32_t length_sector_long;
255 } QEMU_PACKED partition_t;
257 typedef struct mbr_t {
258 uint8_t ignored[0x1b8];
259 uint32_t nt_id;
260 uint8_t ignored2[2];
261 partition_t partition[4];
262 uint8_t magic[2];
263 } QEMU_PACKED mbr_t;
265 typedef struct direntry_t {
266 uint8_t name[8];
267 uint8_t extension[3];
268 uint8_t attributes;
269 uint8_t reserved[2];
270 uint16_t ctime;
271 uint16_t cdate;
272 uint16_t adate;
273 uint16_t begin_hi;
274 uint16_t mtime;
275 uint16_t mdate;
276 uint16_t begin;
277 uint32_t size;
278 } QEMU_PACKED direntry_t;
280 /* this structure are used to transparently access the files */
282 typedef struct mapping_t {
283 /* begin is the first cluster, end is the last+1 */
284 uint32_t begin,end;
285 /* as s->directory is growable, no pointer may be used here */
286 unsigned int dir_index;
287 /* the clusters of a file may be in any order; this points to the first */
288 int first_mapping_index;
289 union {
290 /* offset is
291 * - the offset in the file (in clusters) for a file, or
292 * - the next cluster of the directory for a directory, and
293 * - the address of the buffer for a faked entry
295 struct {
296 uint32_t offset;
297 } file;
298 struct {
299 int parent_mapping_index;
300 int first_dir_index;
301 } dir;
302 } info;
303 /* path contains the full path, i.e. it always starts with s->path */
304 char* path;
306 enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2,
307 MODE_DIRECTORY = 4, MODE_FAKED = 8,
308 MODE_DELETED = 16, MODE_RENAMED = 32 } mode;
309 int read_only;
310 } mapping_t;
312 #ifdef DEBUG
313 static void print_direntry(const struct direntry_t*);
314 static void print_mapping(const struct mapping_t* mapping);
315 #endif
317 /* here begins the real VVFAT driver */
319 typedef struct BDRVVVFATState {
320 CoMutex lock;
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*)g_malloc(length);
732 snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
734 if(stat(buffer,&st)<0) {
735 g_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 g_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 int init_directories(BDRVVVFATState* s,
829 const char* dirname)
831 bootsector_t* bootsector;
832 mapping_t* mapping;
833 unsigned int i;
834 unsigned int cluster;
836 memset(&(s->first_sectors[0]),0,0x40*0x200);
838 s->cluster_size=s->sectors_per_cluster*0x200;
839 s->cluster_buffer=g_malloc(s->cluster_size);
842 * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
843 * where sc is sector_count,
844 * spf is sectors_per_fat,
845 * spc is sectors_per_clusters, and
846 * fat_type = 12, 16 or 32.
848 i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
849 s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
851 array_init(&(s->mapping),sizeof(mapping_t));
852 array_init(&(s->directory),sizeof(direntry_t));
854 /* add volume label */
856 direntry_t* entry=array_get_next(&(s->directory));
857 entry->attributes=0x28; /* archive | volume label */
858 memcpy(entry->name,"QEMU VVF",8);
859 memcpy(entry->extension,"AT ",3);
862 /* Now build FAT, and write back information into directory */
863 init_fat(s);
865 s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
866 s->cluster_count=sector2cluster(s, s->sector_count);
868 mapping = array_get_next(&(s->mapping));
869 mapping->begin = 0;
870 mapping->dir_index = 0;
871 mapping->info.dir.parent_mapping_index = -1;
872 mapping->first_mapping_index = -1;
873 mapping->path = g_strdup(dirname);
874 i = strlen(mapping->path);
875 if (i > 0 && mapping->path[i - 1] == '/')
876 mapping->path[i - 1] = '\0';
877 mapping->mode = MODE_DIRECTORY;
878 mapping->read_only = 0;
879 s->path = mapping->path;
881 for (i = 0, cluster = 0; i < s->mapping.next; i++) {
882 /* MS-DOS expects the FAT to be 0 for the root directory
883 * (except for the media byte). */
884 /* LATER TODO: still true for FAT32? */
885 int fix_fat = (i != 0);
886 mapping = array_get(&(s->mapping), i);
888 if (mapping->mode & MODE_DIRECTORY) {
889 mapping->begin = cluster;
890 if(read_directory(s, i)) {
891 fprintf(stderr, "Could not read directory %s\n",
892 mapping->path);
893 return -1;
895 mapping = array_get(&(s->mapping), i);
896 } else {
897 assert(mapping->mode == MODE_UNDEFINED);
898 mapping->mode=MODE_NORMAL;
899 mapping->begin = cluster;
900 if (mapping->end > 0) {
901 direntry_t* direntry = array_get(&(s->directory),
902 mapping->dir_index);
904 mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
905 set_begin_of_direntry(direntry, mapping->begin);
906 } else {
907 mapping->end = cluster + 1;
908 fix_fat = 0;
912 assert(mapping->begin < mapping->end);
914 /* next free cluster */
915 cluster = mapping->end;
917 if(cluster > s->cluster_count) {
918 fprintf(stderr,"Directory does not fit in FAT%d (capacity %s)\n",
919 s->fat_type,
920 s->fat_type == 12 ? s->sector_count == 2880 ? "1.44 MB"
921 : "2.88 MB"
922 : "504MB");
923 return -EINVAL;
926 /* fix fat for entry */
927 if (fix_fat) {
928 int j;
929 for(j = mapping->begin; j < mapping->end - 1; j++)
930 fat_set(s, j, j+1);
931 fat_set(s, mapping->end - 1, s->max_fat_value);
935 mapping = array_get(&(s->mapping), 0);
936 s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
937 s->last_cluster_of_root_directory = mapping->end;
939 /* the FAT signature */
940 fat_set(s,0,s->max_fat_value);
941 fat_set(s,1,s->max_fat_value);
943 s->current_mapping = NULL;
945 bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
946 bootsector->jump[0]=0xeb;
947 bootsector->jump[1]=0x3e;
948 bootsector->jump[2]=0x90;
949 memcpy(bootsector->name,"QEMU ",8);
950 bootsector->sector_size=cpu_to_le16(0x200);
951 bootsector->sectors_per_cluster=s->sectors_per_cluster;
952 bootsector->reserved_sectors=cpu_to_le16(1);
953 bootsector->number_of_fats=0x2; /* number of FATs */
954 bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
955 bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
956 bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
957 s->fat.pointer[0] = bootsector->media_type;
958 bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
959 bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
960 bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
961 bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
962 bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
964 /* LATER TODO: if FAT32, this is wrong */
965 bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
966 bootsector->u.fat16.current_head=0;
967 bootsector->u.fat16.signature=0x29;
968 bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
970 memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
971 memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8);
972 bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
974 return 0;
977 #ifdef DEBUG
978 static BDRVVVFATState *vvv = NULL;
979 #endif
981 static int enable_write_target(BDRVVVFATState *s);
982 static int is_consistent(BDRVVVFATState *s);
984 static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
986 BDRVVVFATState *s = bs->opaque;
987 int floppy = 0;
988 int i;
990 #ifdef DEBUG
991 vvv = s;
992 #endif
994 DLOG(if (stderr == NULL) {
995 stderr = fopen("vvfat.log", "a");
996 setbuf(stderr, NULL);
999 s->bs = bs;
1001 s->fat_type=16;
1002 /* LATER TODO: if FAT32, adjust */
1003 s->sectors_per_cluster=0x10;
1004 /* 504MB disk*/
1005 bs->cyls=1024; bs->heads=16; bs->secs=63;
1007 s->current_cluster=0xffffffff;
1009 s->first_sectors_number=0x40;
1010 /* read only is the default for safety */
1011 bs->read_only = 1;
1012 s->qcow = s->write_target = NULL;
1013 s->qcow_filename = NULL;
1014 s->fat2 = NULL;
1015 s->downcase_short_names = 1;
1017 if (!strstart(dirname, "fat:", NULL))
1018 return -1;
1020 if (strstr(dirname, ":floppy:")) {
1021 floppy = 1;
1022 s->fat_type = 12;
1023 s->first_sectors_number = 1;
1024 s->sectors_per_cluster=2;
1025 bs->cyls = 80; bs->heads = 2; bs->secs = 36;
1028 s->sector_count=bs->cyls*bs->heads*bs->secs;
1030 if (strstr(dirname, ":32:")) {
1031 fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
1032 s->fat_type = 32;
1033 } else if (strstr(dirname, ":16:")) {
1034 s->fat_type = 16;
1035 } else if (strstr(dirname, ":12:")) {
1036 s->fat_type = 12;
1037 s->sector_count=2880;
1040 if (strstr(dirname, ":rw:")) {
1041 if (enable_write_target(s))
1042 return -1;
1043 bs->read_only = 0;
1046 i = strrchr(dirname, ':') - dirname;
1047 assert(i >= 3);
1048 if (dirname[i-2] == ':' && qemu_isalpha(dirname[i-1]))
1049 /* workaround for DOS drive names */
1050 dirname += i-1;
1051 else
1052 dirname += i+1;
1054 bs->total_sectors=bs->cyls*bs->heads*bs->secs;
1056 if(init_directories(s, dirname))
1057 return -1;
1059 s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
1061 if(s->first_sectors_number==0x40)
1062 init_mbr(s);
1064 /* for some reason or other, MS-DOS does not like to know about CHS... */
1065 if (floppy)
1066 bs->heads = bs->cyls = bs->secs = 0;
1068 // assert(is_consistent(s));
1069 qemu_co_mutex_init(&s->lock);
1070 return 0;
1073 static inline void vvfat_close_current_file(BDRVVVFATState *s)
1075 if(s->current_mapping) {
1076 s->current_mapping = NULL;
1077 if (s->current_fd) {
1078 close(s->current_fd);
1079 s->current_fd = 0;
1082 s->current_cluster = -1;
1085 /* mappings between index1 and index2-1 are supposed to be ordered
1086 * return value is the index of the last mapping for which end>cluster_num
1088 static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
1090 while(1) {
1091 int index3;
1092 mapping_t* mapping;
1093 index3=(index1+index2)/2;
1094 mapping=array_get(&(s->mapping),index3);
1095 assert(mapping->begin < mapping->end);
1096 if(mapping->begin>=cluster_num) {
1097 assert(index2!=index3 || index2==0);
1098 if(index2==index3)
1099 return index1;
1100 index2=index3;
1101 } else {
1102 if(index1==index3)
1103 return mapping->end<=cluster_num ? index2 : index1;
1104 index1=index3;
1106 assert(index1<=index2);
1107 DLOG(mapping=array_get(&(s->mapping),index1);
1108 assert(mapping->begin<=cluster_num);
1109 assert(index2 >= s->mapping.next ||
1110 ((mapping = array_get(&(s->mapping),index2)) &&
1111 mapping->end>cluster_num)));
1115 static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
1117 int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
1118 mapping_t* mapping;
1119 if(index>=s->mapping.next)
1120 return NULL;
1121 mapping=array_get(&(s->mapping),index);
1122 if(mapping->begin>cluster_num)
1123 return NULL;
1124 assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
1125 return mapping;
1128 static int open_file(BDRVVVFATState* s,mapping_t* mapping)
1130 if(!mapping)
1131 return -1;
1132 if(!s->current_mapping ||
1133 strcmp(s->current_mapping->path,mapping->path)) {
1134 /* open file */
1135 int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
1136 if(fd<0)
1137 return -1;
1138 vvfat_close_current_file(s);
1139 s->current_fd = fd;
1140 s->current_mapping = mapping;
1142 return 0;
1145 static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
1147 if(s->current_cluster != cluster_num) {
1148 int result=0;
1149 off_t offset;
1150 assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
1151 if(!s->current_mapping
1152 || s->current_mapping->begin>cluster_num
1153 || s->current_mapping->end<=cluster_num) {
1154 /* binary search of mappings for file */
1155 mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
1157 assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
1159 if (mapping && mapping->mode & MODE_DIRECTORY) {
1160 vvfat_close_current_file(s);
1161 s->current_mapping = mapping;
1162 read_cluster_directory:
1163 offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
1164 s->cluster = (unsigned char*)s->directory.pointer+offset
1165 + 0x20*s->current_mapping->info.dir.first_dir_index;
1166 assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
1167 assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
1168 s->current_cluster = cluster_num;
1169 return 0;
1172 if(open_file(s,mapping))
1173 return -2;
1174 } else if (s->current_mapping->mode & MODE_DIRECTORY)
1175 goto read_cluster_directory;
1177 assert(s->current_fd);
1179 offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
1180 if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
1181 return -3;
1182 s->cluster=s->cluster_buffer;
1183 result=read(s->current_fd,s->cluster,s->cluster_size);
1184 if(result<0) {
1185 s->current_cluster = -1;
1186 return -1;
1188 s->current_cluster = cluster_num;
1190 return 0;
1193 #ifdef DEBUG
1194 static void print_direntry(const direntry_t* direntry)
1196 int j = 0;
1197 char buffer[1024];
1199 fprintf(stderr, "direntry %p: ", direntry);
1200 if(!direntry)
1201 return;
1202 if(is_long_name(direntry)) {
1203 unsigned char* c=(unsigned char*)direntry;
1204 int i;
1205 for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
1206 #define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;}
1207 ADD_CHAR(c[i]);
1208 for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
1209 ADD_CHAR(c[i]);
1210 for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
1211 ADD_CHAR(c[i]);
1212 buffer[j] = 0;
1213 fprintf(stderr, "%s\n", buffer);
1214 } else {
1215 int i;
1216 for(i=0;i<11;i++)
1217 ADD_CHAR(direntry->name[i]);
1218 buffer[j] = 0;
1219 fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
1220 buffer,
1221 direntry->attributes,
1222 begin_of_direntry(direntry),le32_to_cpu(direntry->size));
1226 static void print_mapping(const mapping_t* mapping)
1228 fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, "
1229 "first_mapping_index = %d, name = %s, mode = 0x%x, " ,
1230 mapping, mapping->begin, mapping->end, mapping->dir_index,
1231 mapping->first_mapping_index, mapping->path, mapping->mode);
1233 if (mapping->mode & MODE_DIRECTORY)
1234 fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
1235 else
1236 fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
1238 #endif
1240 static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
1241 uint8_t *buf, int nb_sectors)
1243 BDRVVVFATState *s = bs->opaque;
1244 int i;
1246 for(i=0;i<nb_sectors;i++,sector_num++) {
1247 if (sector_num >= s->sector_count)
1248 return -1;
1249 if (s->qcow) {
1250 int n;
1251 if (s->qcow->drv->bdrv_is_allocated(s->qcow,
1252 sector_num, nb_sectors-i, &n)) {
1253 DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
1254 if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
1255 return -1;
1256 i += n - 1;
1257 sector_num += n - 1;
1258 continue;
1260 DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
1262 if(sector_num<s->faked_sectors) {
1263 if(sector_num<s->first_sectors_number)
1264 memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
1265 else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
1266 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
1267 else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
1268 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
1269 } else {
1270 uint32_t sector=sector_num-s->faked_sectors,
1271 sector_offset_in_cluster=(sector%s->sectors_per_cluster),
1272 cluster_num=sector/s->sectors_per_cluster;
1273 if(read_cluster(s, cluster_num) != 0) {
1274 /* LATER TODO: strict: return -1; */
1275 memset(buf+i*0x200,0,0x200);
1276 continue;
1278 memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
1281 return 0;
1284 static coroutine_fn int vvfat_co_read(BlockDriverState *bs, int64_t sector_num,
1285 uint8_t *buf, int nb_sectors)
1287 int ret;
1288 BDRVVVFATState *s = bs->opaque;
1289 qemu_co_mutex_lock(&s->lock);
1290 ret = vvfat_read(bs, sector_num, buf, nb_sectors);
1291 qemu_co_mutex_unlock(&s->lock);
1292 return ret;
1295 /* LATER TODO: statify all functions */
1298 * Idea of the write support (use snapshot):
1300 * 1. check if all data is consistent, recording renames, modifications,
1301 * new files and directories (in s->commits).
1303 * 2. if the data is not consistent, stop committing
1305 * 3. handle renames, and create new files and directories (do not yet
1306 * write their contents)
1308 * 4. walk the directories, fixing the mapping and direntries, and marking
1309 * the handled mappings as not deleted
1311 * 5. commit the contents of the files
1313 * 6. handle deleted files and directories
1317 typedef struct commit_t {
1318 char* path;
1319 union {
1320 struct { uint32_t cluster; } rename;
1321 struct { int dir_index; uint32_t modified_offset; } writeout;
1322 struct { uint32_t first_cluster; } new_file;
1323 struct { uint32_t cluster; } mkdir;
1324 } param;
1325 /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
1326 enum {
1327 ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
1328 } action;
1329 } commit_t;
1331 static void clear_commits(BDRVVVFATState* s)
1333 int i;
1334 DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
1335 for (i = 0; i < s->commits.next; i++) {
1336 commit_t* commit = array_get(&(s->commits), i);
1337 assert(commit->path || commit->action == ACTION_WRITEOUT);
1338 if (commit->action != ACTION_WRITEOUT) {
1339 assert(commit->path);
1340 g_free(commit->path);
1341 } else
1342 assert(commit->path == NULL);
1344 s->commits.next = 0;
1347 static void schedule_rename(BDRVVVFATState* s,
1348 uint32_t cluster, char* new_path)
1350 commit_t* commit = array_get_next(&(s->commits));
1351 commit->path = new_path;
1352 commit->param.rename.cluster = cluster;
1353 commit->action = ACTION_RENAME;
1356 static void schedule_writeout(BDRVVVFATState* s,
1357 int dir_index, uint32_t modified_offset)
1359 commit_t* commit = array_get_next(&(s->commits));
1360 commit->path = NULL;
1361 commit->param.writeout.dir_index = dir_index;
1362 commit->param.writeout.modified_offset = modified_offset;
1363 commit->action = ACTION_WRITEOUT;
1366 static void schedule_new_file(BDRVVVFATState* s,
1367 char* path, uint32_t first_cluster)
1369 commit_t* commit = array_get_next(&(s->commits));
1370 commit->path = path;
1371 commit->param.new_file.first_cluster = first_cluster;
1372 commit->action = ACTION_NEW_FILE;
1375 static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
1377 commit_t* commit = array_get_next(&(s->commits));
1378 commit->path = path;
1379 commit->param.mkdir.cluster = cluster;
1380 commit->action = ACTION_MKDIR;
1383 typedef struct {
1385 * Since the sequence number is at most 0x3f, and the filename
1386 * length is at most 13 times the sequence number, the maximal
1387 * filename length is 0x3f * 13 bytes.
1389 unsigned char name[0x3f * 13 + 1];
1390 int checksum, len;
1391 int sequence_number;
1392 } long_file_name;
1394 static void lfn_init(long_file_name* lfn)
1396 lfn->sequence_number = lfn->len = 0;
1397 lfn->checksum = 0x100;
1400 /* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
1401 static int parse_long_name(long_file_name* lfn,
1402 const direntry_t* direntry)
1404 int i, j, offset;
1405 const unsigned char* pointer = (const unsigned char*)direntry;
1407 if (!is_long_name(direntry))
1408 return 1;
1410 if (pointer[0] & 0x40) {
1411 lfn->sequence_number = pointer[0] & 0x3f;
1412 lfn->checksum = pointer[13];
1413 lfn->name[0] = 0;
1414 lfn->name[lfn->sequence_number * 13] = 0;
1415 } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
1416 return -1;
1417 else if (pointer[13] != lfn->checksum)
1418 return -2;
1419 else if (pointer[12] || pointer[26] || pointer[27])
1420 return -3;
1422 offset = 13 * (lfn->sequence_number - 1);
1423 for (i = 0, j = 1; i < 13; i++, j+=2) {
1424 if (j == 11)
1425 j = 14;
1426 else if (j == 26)
1427 j = 28;
1429 if (pointer[j+1] == 0)
1430 lfn->name[offset + i] = pointer[j];
1431 else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
1432 return -4;
1433 else
1434 lfn->name[offset + i] = 0;
1437 if (pointer[0] & 0x40)
1438 lfn->len = offset + strlen((char*)lfn->name + offset);
1440 return 0;
1443 /* returns 0 if successful, >0 if no short_name, and <0 on error */
1444 static int parse_short_name(BDRVVVFATState* s,
1445 long_file_name* lfn, direntry_t* direntry)
1447 int i, j;
1449 if (!is_short_name(direntry))
1450 return 1;
1452 for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
1453 for (i = 0; i <= j; i++) {
1454 if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
1455 return -1;
1456 else if (s->downcase_short_names)
1457 lfn->name[i] = qemu_tolower(direntry->name[i]);
1458 else
1459 lfn->name[i] = direntry->name[i];
1462 for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
1463 if (j >= 0) {
1464 lfn->name[i++] = '.';
1465 lfn->name[i + j + 1] = '\0';
1466 for (;j >= 0; j--) {
1467 if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
1468 return -2;
1469 else if (s->downcase_short_names)
1470 lfn->name[i + j] = qemu_tolower(direntry->extension[j]);
1471 else
1472 lfn->name[i + j] = direntry->extension[j];
1474 } else
1475 lfn->name[i + j + 1] = '\0';
1477 lfn->len = strlen((char*)lfn->name);
1479 return 0;
1482 static inline uint32_t modified_fat_get(BDRVVVFATState* s,
1483 unsigned int cluster)
1485 if (cluster < s->last_cluster_of_root_directory) {
1486 if (cluster + 1 == s->last_cluster_of_root_directory)
1487 return s->max_fat_value;
1488 else
1489 return cluster + 1;
1492 if (s->fat_type==32) {
1493 uint32_t* entry=((uint32_t*)s->fat2)+cluster;
1494 return le32_to_cpu(*entry);
1495 } else if (s->fat_type==16) {
1496 uint16_t* entry=((uint16_t*)s->fat2)+cluster;
1497 return le16_to_cpu(*entry);
1498 } else {
1499 const uint8_t* x=s->fat2+cluster*3/2;
1500 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
1504 static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
1506 int was_modified = 0;
1507 int i, dummy;
1509 if (s->qcow == NULL)
1510 return 0;
1512 for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
1513 was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
1514 cluster2sector(s, cluster_num) + i, 1, &dummy);
1516 return was_modified;
1519 static const char* get_basename(const char* path)
1521 char* basename = strrchr(path, '/');
1522 if (basename == NULL)
1523 return path;
1524 else
1525 return basename + 1; /* strip '/' */
1529 * The array s->used_clusters holds the states of the clusters. If it is
1530 * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
1531 * was modified, bit 3 is set.
1532 * If any cluster is allocated, but not part of a file or directory, this
1533 * driver refuses to commit.
1535 typedef enum {
1536 USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
1537 } used_t;
1540 * get_cluster_count_for_direntry() not only determines how many clusters
1541 * are occupied by direntry, but also if it was renamed or modified.
1543 * A file is thought to be renamed *only* if there already was a file with
1544 * exactly the same first cluster, but a different name.
1546 * Further, the files/directories handled by this function are
1547 * assumed to be *not* deleted (and *only* those).
1549 static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
1550 direntry_t* direntry, const char* path)
1553 * This is a little bit tricky:
1554 * IF the guest OS just inserts a cluster into the file chain,
1555 * and leaves the rest alone, (i.e. the original file had clusters
1556 * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
1558 * - do_commit will write the cluster into the file at the given
1559 * offset, but
1561 * - the cluster which is overwritten should be moved to a later
1562 * position in the file.
1564 * I am not aware that any OS does something as braindead, but this
1565 * situation could happen anyway when not committing for a long time.
1566 * Just to be sure that this does not bite us, detect it, and copy the
1567 * contents of the clusters to-be-overwritten into the qcow.
1569 int copy_it = 0;
1570 int was_modified = 0;
1571 int32_t ret = 0;
1573 uint32_t cluster_num = begin_of_direntry(direntry);
1574 uint32_t offset = 0;
1575 int first_mapping_index = -1;
1576 mapping_t* mapping = NULL;
1577 const char* basename2 = NULL;
1579 vvfat_close_current_file(s);
1581 /* the root directory */
1582 if (cluster_num == 0)
1583 return 0;
1585 /* write support */
1586 if (s->qcow) {
1587 basename2 = get_basename(path);
1589 mapping = find_mapping_for_cluster(s, cluster_num);
1591 if (mapping) {
1592 const char* basename;
1594 assert(mapping->mode & MODE_DELETED);
1595 mapping->mode &= ~MODE_DELETED;
1597 basename = get_basename(mapping->path);
1599 assert(mapping->mode & MODE_NORMAL);
1601 /* rename */
1602 if (strcmp(basename, basename2))
1603 schedule_rename(s, cluster_num, g_strdup(path));
1604 } else if (is_file(direntry))
1605 /* new file */
1606 schedule_new_file(s, g_strdup(path), cluster_num);
1607 else {
1608 abort();
1609 return 0;
1613 while(1) {
1614 if (s->qcow) {
1615 if (!copy_it && cluster_was_modified(s, cluster_num)) {
1616 if (mapping == NULL ||
1617 mapping->begin > cluster_num ||
1618 mapping->end <= cluster_num)
1619 mapping = find_mapping_for_cluster(s, cluster_num);
1622 if (mapping &&
1623 (mapping->mode & MODE_DIRECTORY) == 0) {
1625 /* was modified in qcow */
1626 if (offset != mapping->info.file.offset + s->cluster_size
1627 * (cluster_num - mapping->begin)) {
1628 /* offset of this cluster in file chain has changed */
1629 abort();
1630 copy_it = 1;
1631 } else if (offset == 0) {
1632 const char* basename = get_basename(mapping->path);
1634 if (strcmp(basename, basename2))
1635 copy_it = 1;
1636 first_mapping_index = array_index(&(s->mapping), mapping);
1639 if (mapping->first_mapping_index != first_mapping_index
1640 && mapping->info.file.offset > 0) {
1641 abort();
1642 copy_it = 1;
1645 /* need to write out? */
1646 if (!was_modified && is_file(direntry)) {
1647 was_modified = 1;
1648 schedule_writeout(s, mapping->dir_index, offset);
1653 if (copy_it) {
1654 int i, dummy;
1656 * This is horribly inefficient, but that is okay, since
1657 * it is rarely executed, if at all.
1659 int64_t offset = cluster2sector(s, cluster_num);
1661 vvfat_close_current_file(s);
1662 for (i = 0; i < s->sectors_per_cluster; i++)
1663 if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
1664 offset + i, 1, &dummy)) {
1665 if (vvfat_read(s->bs,
1666 offset, s->cluster_buffer, 1))
1667 return -1;
1668 if (s->qcow->drv->bdrv_write(s->qcow,
1669 offset, s->cluster_buffer, 1))
1670 return -2;
1675 ret++;
1676 if (s->used_clusters[cluster_num] & USED_ANY)
1677 return 0;
1678 s->used_clusters[cluster_num] = USED_FILE;
1680 cluster_num = modified_fat_get(s, cluster_num);
1682 if (fat_eof(s, cluster_num))
1683 return ret;
1684 else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
1685 return -1;
1687 offset += s->cluster_size;
1692 * This function looks at the modified data (qcow).
1693 * It returns 0 upon inconsistency or error, and the number of clusters
1694 * used by the directory, its subdirectories and their files.
1696 static int check_directory_consistency(BDRVVVFATState *s,
1697 int cluster_num, const char* path)
1699 int ret = 0;
1700 unsigned char* cluster = g_malloc(s->cluster_size);
1701 direntry_t* direntries = (direntry_t*)cluster;
1702 mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
1704 long_file_name lfn;
1705 int path_len = strlen(path);
1706 char path2[PATH_MAX + 1];
1708 assert(path_len < PATH_MAX); /* len was tested before! */
1709 pstrcpy(path2, sizeof(path2), path);
1710 path2[path_len] = '/';
1711 path2[path_len + 1] = '\0';
1713 if (mapping) {
1714 const char* basename = get_basename(mapping->path);
1715 const char* basename2 = get_basename(path);
1717 assert(mapping->mode & MODE_DIRECTORY);
1719 assert(mapping->mode & MODE_DELETED);
1720 mapping->mode &= ~MODE_DELETED;
1722 if (strcmp(basename, basename2))
1723 schedule_rename(s, cluster_num, g_strdup(path));
1724 } else
1725 /* new directory */
1726 schedule_mkdir(s, cluster_num, g_strdup(path));
1728 lfn_init(&lfn);
1729 do {
1730 int i;
1731 int subret = 0;
1733 ret++;
1735 if (s->used_clusters[cluster_num] & USED_ANY) {
1736 fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
1737 return 0;
1739 s->used_clusters[cluster_num] = USED_DIRECTORY;
1741 DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
1742 subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
1743 s->sectors_per_cluster);
1744 if (subret) {
1745 fprintf(stderr, "Error fetching direntries\n");
1746 fail:
1747 g_free(cluster);
1748 return 0;
1751 for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
1752 int cluster_count = 0;
1754 DLOG(fprintf(stderr, "check direntry %d:\n", i); print_direntry(direntries + i));
1755 if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
1756 is_free(direntries + i))
1757 continue;
1759 subret = parse_long_name(&lfn, direntries + i);
1760 if (subret < 0) {
1761 fprintf(stderr, "Error in long name\n");
1762 goto fail;
1764 if (subret == 0 || is_free(direntries + i))
1765 continue;
1767 if (fat_chksum(direntries+i) != lfn.checksum) {
1768 subret = parse_short_name(s, &lfn, direntries + i);
1769 if (subret < 0) {
1770 fprintf(stderr, "Error in short name (%d)\n", subret);
1771 goto fail;
1773 if (subret > 0 || !strcmp((char*)lfn.name, ".")
1774 || !strcmp((char*)lfn.name, ".."))
1775 continue;
1777 lfn.checksum = 0x100; /* cannot use long name twice */
1779 if (path_len + 1 + lfn.len >= PATH_MAX) {
1780 fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
1781 goto fail;
1783 pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
1784 (char*)lfn.name);
1786 if (is_directory(direntries + i)) {
1787 if (begin_of_direntry(direntries + i) == 0) {
1788 DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
1789 goto fail;
1791 cluster_count = check_directory_consistency(s,
1792 begin_of_direntry(direntries + i), path2);
1793 if (cluster_count == 0) {
1794 DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
1795 goto fail;
1797 } else if (is_file(direntries + i)) {
1798 /* check file size with FAT */
1799 cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
1800 if (cluster_count !=
1801 (le32_to_cpu(direntries[i].size) + s->cluster_size
1802 - 1) / s->cluster_size) {
1803 DLOG(fprintf(stderr, "Cluster count mismatch\n"));
1804 goto fail;
1806 } else
1807 abort(); /* cluster_count = 0; */
1809 ret += cluster_count;
1812 cluster_num = modified_fat_get(s, cluster_num);
1813 } while(!fat_eof(s, cluster_num));
1815 g_free(cluster);
1816 return ret;
1819 /* returns 1 on success */
1820 static int is_consistent(BDRVVVFATState* s)
1822 int i, check;
1823 int used_clusters_count = 0;
1825 DLOG(checkpoint());
1827 * - get modified FAT
1828 * - compare the two FATs (TODO)
1829 * - get buffer for marking used clusters
1830 * - recurse direntries from root (using bs->bdrv_read to make
1831 * sure to get the new data)
1832 * - check that the FAT agrees with the size
1833 * - count the number of clusters occupied by this directory and
1834 * its files
1835 * - check that the cumulative used cluster count agrees with the
1836 * FAT
1837 * - if all is fine, return number of used clusters
1839 if (s->fat2 == NULL) {
1840 int size = 0x200 * s->sectors_per_fat;
1841 s->fat2 = g_malloc(size);
1842 memcpy(s->fat2, s->fat.pointer, size);
1844 check = vvfat_read(s->bs,
1845 s->first_sectors_number, s->fat2, s->sectors_per_fat);
1846 if (check) {
1847 fprintf(stderr, "Could not copy fat\n");
1848 return 0;
1850 assert (s->used_clusters);
1851 for (i = 0; i < sector2cluster(s, s->sector_count); i++)
1852 s->used_clusters[i] &= ~USED_ANY;
1854 clear_commits(s);
1856 /* mark every mapped file/directory as deleted.
1857 * (check_directory_consistency() will unmark those still present). */
1858 if (s->qcow)
1859 for (i = 0; i < s->mapping.next; i++) {
1860 mapping_t* mapping = array_get(&(s->mapping), i);
1861 if (mapping->first_mapping_index < 0)
1862 mapping->mode |= MODE_DELETED;
1865 used_clusters_count = check_directory_consistency(s, 0, s->path);
1866 if (used_clusters_count <= 0) {
1867 DLOG(fprintf(stderr, "problem in directory\n"));
1868 return 0;
1871 check = s->last_cluster_of_root_directory;
1872 for (i = check; i < sector2cluster(s, s->sector_count); i++) {
1873 if (modified_fat_get(s, i)) {
1874 if(!s->used_clusters[i]) {
1875 DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
1876 return 0;
1878 check++;
1881 if (s->used_clusters[i] == USED_ALLOCATED) {
1882 /* allocated, but not used... */
1883 DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
1884 return 0;
1888 if (check != used_clusters_count)
1889 return 0;
1891 return used_clusters_count;
1894 static inline void adjust_mapping_indices(BDRVVVFATState* s,
1895 int offset, int adjust)
1897 int i;
1899 for (i = 0; i < s->mapping.next; i++) {
1900 mapping_t* mapping = array_get(&(s->mapping), i);
1902 #define ADJUST_MAPPING_INDEX(name) \
1903 if (mapping->name >= offset) \
1904 mapping->name += adjust
1906 ADJUST_MAPPING_INDEX(first_mapping_index);
1907 if (mapping->mode & MODE_DIRECTORY)
1908 ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
1912 /* insert or update mapping */
1913 static mapping_t* insert_mapping(BDRVVVFATState* s,
1914 uint32_t begin, uint32_t end)
1917 * - find mapping where mapping->begin >= begin,
1918 * - if mapping->begin > begin: insert
1919 * - adjust all references to mappings!
1920 * - else: adjust
1921 * - replace name
1923 int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
1924 mapping_t* mapping = NULL;
1925 mapping_t* first_mapping = array_get(&(s->mapping), 0);
1927 if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
1928 && mapping->begin < begin) {
1929 mapping->end = begin;
1930 index++;
1931 mapping = array_get(&(s->mapping), index);
1933 if (index >= s->mapping.next || mapping->begin > begin) {
1934 mapping = array_insert(&(s->mapping), index, 1);
1935 mapping->path = NULL;
1936 adjust_mapping_indices(s, index, +1);
1939 mapping->begin = begin;
1940 mapping->end = end;
1942 DLOG(mapping_t* next_mapping;
1943 assert(index + 1 >= s->mapping.next ||
1944 ((next_mapping = array_get(&(s->mapping), index + 1)) &&
1945 next_mapping->begin >= end)));
1947 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1948 s->current_mapping = array_get(&(s->mapping),
1949 s->current_mapping - first_mapping);
1951 return mapping;
1954 static int remove_mapping(BDRVVVFATState* s, int mapping_index)
1956 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
1957 mapping_t* first_mapping = array_get(&(s->mapping), 0);
1959 /* free mapping */
1960 if (mapping->first_mapping_index < 0) {
1961 g_free(mapping->path);
1964 /* remove from s->mapping */
1965 array_remove(&(s->mapping), mapping_index);
1967 /* adjust all references to mappings */
1968 adjust_mapping_indices(s, mapping_index, -1);
1970 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1971 s->current_mapping = array_get(&(s->mapping),
1972 s->current_mapping - first_mapping);
1974 return 0;
1977 static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
1979 int i;
1980 for (i = 0; i < s->mapping.next; i++) {
1981 mapping_t* mapping = array_get(&(s->mapping), i);
1982 if (mapping->dir_index >= offset)
1983 mapping->dir_index += adjust;
1984 if ((mapping->mode & MODE_DIRECTORY) &&
1985 mapping->info.dir.first_dir_index >= offset)
1986 mapping->info.dir.first_dir_index += adjust;
1990 static direntry_t* insert_direntries(BDRVVVFATState* s,
1991 int dir_index, int count)
1994 * make room in s->directory,
1995 * adjust_dirindices
1997 direntry_t* result = array_insert(&(s->directory), dir_index, count);
1998 if (result == NULL)
1999 return NULL;
2000 adjust_dirindices(s, dir_index, count);
2001 return result;
2004 static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
2006 int ret = array_remove_slice(&(s->directory), dir_index, count);
2007 if (ret)
2008 return ret;
2009 adjust_dirindices(s, dir_index, -count);
2010 return 0;
2014 * Adapt the mappings of the cluster chain starting at first cluster
2015 * (i.e. if a file starts at first_cluster, the chain is followed according
2016 * to the modified fat, and the corresponding entries in s->mapping are
2017 * adjusted)
2019 static int commit_mappings(BDRVVVFATState* s,
2020 uint32_t first_cluster, int dir_index)
2022 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2023 direntry_t* direntry = array_get(&(s->directory), dir_index);
2024 uint32_t cluster = first_cluster;
2026 vvfat_close_current_file(s);
2028 assert(mapping);
2029 assert(mapping->begin == first_cluster);
2030 mapping->first_mapping_index = -1;
2031 mapping->dir_index = dir_index;
2032 mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
2033 MODE_DIRECTORY : MODE_NORMAL;
2035 while (!fat_eof(s, cluster)) {
2036 uint32_t c, c1;
2038 for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
2039 c = c1, c1 = modified_fat_get(s, c1));
2041 c++;
2042 if (c > mapping->end) {
2043 int index = array_index(&(s->mapping), mapping);
2044 int i, max_i = s->mapping.next - index;
2045 for (i = 1; i < max_i && mapping[i].begin < c; i++);
2046 while (--i > 0)
2047 remove_mapping(s, index + 1);
2049 assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
2050 || mapping[1].begin >= c);
2051 mapping->end = c;
2053 if (!fat_eof(s, c1)) {
2054 int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
2055 mapping_t* next_mapping = i >= s->mapping.next ? NULL :
2056 array_get(&(s->mapping), i);
2058 if (next_mapping == NULL || next_mapping->begin > c1) {
2059 int i1 = array_index(&(s->mapping), mapping);
2061 next_mapping = insert_mapping(s, c1, c1+1);
2063 if (c1 < c)
2064 i1++;
2065 mapping = array_get(&(s->mapping), i1);
2068 next_mapping->dir_index = mapping->dir_index;
2069 next_mapping->first_mapping_index =
2070 mapping->first_mapping_index < 0 ?
2071 array_index(&(s->mapping), mapping) :
2072 mapping->first_mapping_index;
2073 next_mapping->path = mapping->path;
2074 next_mapping->mode = mapping->mode;
2075 next_mapping->read_only = mapping->read_only;
2076 if (mapping->mode & MODE_DIRECTORY) {
2077 next_mapping->info.dir.parent_mapping_index =
2078 mapping->info.dir.parent_mapping_index;
2079 next_mapping->info.dir.first_dir_index =
2080 mapping->info.dir.first_dir_index +
2081 0x10 * s->sectors_per_cluster *
2082 (mapping->end - mapping->begin);
2083 } else
2084 next_mapping->info.file.offset = mapping->info.file.offset +
2085 mapping->end - mapping->begin;
2087 mapping = next_mapping;
2090 cluster = c1;
2093 return 0;
2096 static int commit_direntries(BDRVVVFATState* s,
2097 int dir_index, int parent_mapping_index)
2099 direntry_t* direntry = array_get(&(s->directory), dir_index);
2100 uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
2101 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2103 int factor = 0x10 * s->sectors_per_cluster;
2104 int old_cluster_count, new_cluster_count;
2105 int current_dir_index = mapping->info.dir.first_dir_index;
2106 int first_dir_index = current_dir_index;
2107 int ret, i;
2108 uint32_t c;
2110 DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
2112 assert(direntry);
2113 assert(mapping);
2114 assert(mapping->begin == first_cluster);
2115 assert(mapping->info.dir.first_dir_index < s->directory.next);
2116 assert(mapping->mode & MODE_DIRECTORY);
2117 assert(dir_index == 0 || is_directory(direntry));
2119 mapping->info.dir.parent_mapping_index = parent_mapping_index;
2121 if (first_cluster == 0) {
2122 old_cluster_count = new_cluster_count =
2123 s->last_cluster_of_root_directory;
2124 } else {
2125 for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2126 c = fat_get(s, c))
2127 old_cluster_count++;
2129 for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2130 c = modified_fat_get(s, c))
2131 new_cluster_count++;
2134 if (new_cluster_count > old_cluster_count) {
2135 if (insert_direntries(s,
2136 current_dir_index + factor * old_cluster_count,
2137 factor * (new_cluster_count - old_cluster_count)) == NULL)
2138 return -1;
2139 } else if (new_cluster_count < old_cluster_count)
2140 remove_direntries(s,
2141 current_dir_index + factor * new_cluster_count,
2142 factor * (old_cluster_count - new_cluster_count));
2144 for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
2145 void* direntry = array_get(&(s->directory), current_dir_index);
2146 int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
2147 s->sectors_per_cluster);
2148 if (ret)
2149 return ret;
2150 assert(!strncmp(s->directory.pointer, "QEMU", 4));
2151 current_dir_index += factor;
2154 ret = commit_mappings(s, first_cluster, dir_index);
2155 if (ret)
2156 return ret;
2158 /* recurse */
2159 for (i = 0; i < factor * new_cluster_count; i++) {
2160 direntry = array_get(&(s->directory), first_dir_index + i);
2161 if (is_directory(direntry) && !is_dot(direntry)) {
2162 mapping = find_mapping_for_cluster(s, first_cluster);
2163 assert(mapping->mode & MODE_DIRECTORY);
2164 ret = commit_direntries(s, first_dir_index + i,
2165 array_index(&(s->mapping), mapping));
2166 if (ret)
2167 return ret;
2171 return 0;
2174 /* commit one file (adjust contents, adjust mapping),
2175 return first_mapping_index */
2176 static int commit_one_file(BDRVVVFATState* s,
2177 int dir_index, uint32_t offset)
2179 direntry_t* direntry = array_get(&(s->directory), dir_index);
2180 uint32_t c = begin_of_direntry(direntry);
2181 uint32_t first_cluster = c;
2182 mapping_t* mapping = find_mapping_for_cluster(s, c);
2183 uint32_t size = filesize_of_direntry(direntry);
2184 char* cluster = g_malloc(s->cluster_size);
2185 uint32_t i;
2186 int fd = 0;
2188 assert(offset < size);
2189 assert((offset % s->cluster_size) == 0);
2191 for (i = s->cluster_size; i < offset; i += s->cluster_size)
2192 c = modified_fat_get(s, c);
2194 fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
2195 if (fd < 0) {
2196 fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
2197 strerror(errno), errno);
2198 g_free(cluster);
2199 return fd;
2201 if (offset > 0) {
2202 if (lseek(fd, offset, SEEK_SET) != offset) {
2203 g_free(cluster);
2204 return -3;
2208 while (offset < size) {
2209 uint32_t c1;
2210 int rest_size = (size - offset > s->cluster_size ?
2211 s->cluster_size : size - offset);
2212 int ret;
2214 c1 = modified_fat_get(s, c);
2216 assert((size - offset == 0 && fat_eof(s, c)) ||
2217 (size > offset && c >=2 && !fat_eof(s, c)));
2219 ret = vvfat_read(s->bs, cluster2sector(s, c),
2220 (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
2222 if (ret < 0) {
2223 g_free(cluster);
2224 return ret;
2227 if (write(fd, cluster, rest_size) < 0) {
2228 g_free(cluster);
2229 return -2;
2232 offset += rest_size;
2233 c = c1;
2236 if (ftruncate(fd, size)) {
2237 perror("ftruncate()");
2238 close(fd);
2239 g_free(cluster);
2240 return -4;
2242 close(fd);
2243 g_free(cluster);
2245 return commit_mappings(s, first_cluster, dir_index);
2248 #ifdef DEBUG
2249 /* test, if all mappings point to valid direntries */
2250 static void check1(BDRVVVFATState* s)
2252 int i;
2253 for (i = 0; i < s->mapping.next; i++) {
2254 mapping_t* mapping = array_get(&(s->mapping), i);
2255 if (mapping->mode & MODE_DELETED) {
2256 fprintf(stderr, "deleted\n");
2257 continue;
2259 assert(mapping->dir_index < s->directory.next);
2260 direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
2261 assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
2262 if (mapping->mode & MODE_DIRECTORY) {
2263 assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
2264 assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
2269 /* test, if all direntries have mappings */
2270 static void check2(BDRVVVFATState* s)
2272 int i;
2273 int first_mapping = -1;
2275 for (i = 0; i < s->directory.next; i++) {
2276 direntry_t* direntry = array_get(&(s->directory), i);
2278 if (is_short_name(direntry) && begin_of_direntry(direntry)) {
2279 mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
2280 assert(mapping);
2281 assert(mapping->dir_index == i || is_dot(direntry));
2282 assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
2285 if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
2286 /* cluster start */
2287 int j, count = 0;
2289 for (j = 0; j < s->mapping.next; j++) {
2290 mapping_t* mapping = array_get(&(s->mapping), j);
2291 if (mapping->mode & MODE_DELETED)
2292 continue;
2293 if (mapping->mode & MODE_DIRECTORY) {
2294 if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
2295 assert(++count == 1);
2296 if (mapping->first_mapping_index == -1)
2297 first_mapping = array_index(&(s->mapping), mapping);
2298 else
2299 assert(first_mapping == mapping->first_mapping_index);
2300 if (mapping->info.dir.parent_mapping_index < 0)
2301 assert(j == 0);
2302 else {
2303 mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
2304 assert(parent->mode & MODE_DIRECTORY);
2305 assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
2310 if (count == 0)
2311 first_mapping = -1;
2315 #endif
2317 static int handle_renames_and_mkdirs(BDRVVVFATState* s)
2319 int i;
2321 #ifdef DEBUG
2322 fprintf(stderr, "handle_renames\n");
2323 for (i = 0; i < s->commits.next; i++) {
2324 commit_t* commit = array_get(&(s->commits), i);
2325 fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
2327 #endif
2329 for (i = 0; i < s->commits.next;) {
2330 commit_t* commit = array_get(&(s->commits), i);
2331 if (commit->action == ACTION_RENAME) {
2332 mapping_t* mapping = find_mapping_for_cluster(s,
2333 commit->param.rename.cluster);
2334 char* old_path = mapping->path;
2336 assert(commit->path);
2337 mapping->path = commit->path;
2338 if (rename(old_path, mapping->path))
2339 return -2;
2341 if (mapping->mode & MODE_DIRECTORY) {
2342 int l1 = strlen(mapping->path);
2343 int l2 = strlen(old_path);
2344 int diff = l1 - l2;
2345 direntry_t* direntry = array_get(&(s->directory),
2346 mapping->info.dir.first_dir_index);
2347 uint32_t c = mapping->begin;
2348 int i = 0;
2350 /* recurse */
2351 while (!fat_eof(s, c)) {
2352 do {
2353 direntry_t* d = direntry + i;
2355 if (is_file(d) || (is_directory(d) && !is_dot(d))) {
2356 mapping_t* m = find_mapping_for_cluster(s,
2357 begin_of_direntry(d));
2358 int l = strlen(m->path);
2359 char* new_path = g_malloc(l + diff + 1);
2361 assert(!strncmp(m->path, mapping->path, l2));
2363 pstrcpy(new_path, l + diff + 1, mapping->path);
2364 pstrcpy(new_path + l1, l + diff + 1 - l1,
2365 m->path + l2);
2367 schedule_rename(s, m->begin, new_path);
2369 i++;
2370 } while((i % (0x10 * s->sectors_per_cluster)) != 0);
2371 c = fat_get(s, c);
2375 g_free(old_path);
2376 array_remove(&(s->commits), i);
2377 continue;
2378 } else if (commit->action == ACTION_MKDIR) {
2379 mapping_t* mapping;
2380 int j, parent_path_len;
2382 #ifdef __MINGW32__
2383 if (mkdir(commit->path))
2384 return -5;
2385 #else
2386 if (mkdir(commit->path, 0755))
2387 return -5;
2388 #endif
2390 mapping = insert_mapping(s, commit->param.mkdir.cluster,
2391 commit->param.mkdir.cluster + 1);
2392 if (mapping == NULL)
2393 return -6;
2395 mapping->mode = MODE_DIRECTORY;
2396 mapping->read_only = 0;
2397 mapping->path = commit->path;
2398 j = s->directory.next;
2399 assert(j);
2400 insert_direntries(s, s->directory.next,
2401 0x10 * s->sectors_per_cluster);
2402 mapping->info.dir.first_dir_index = j;
2404 parent_path_len = strlen(commit->path)
2405 - strlen(get_basename(commit->path)) - 1;
2406 for (j = 0; j < s->mapping.next; j++) {
2407 mapping_t* m = array_get(&(s->mapping), j);
2408 if (m->first_mapping_index < 0 && m != mapping &&
2409 !strncmp(m->path, mapping->path, parent_path_len) &&
2410 strlen(m->path) == parent_path_len)
2411 break;
2413 assert(j < s->mapping.next);
2414 mapping->info.dir.parent_mapping_index = j;
2416 array_remove(&(s->commits), i);
2417 continue;
2420 i++;
2422 return 0;
2426 * TODO: make sure that the short name is not matching *another* file
2428 static int handle_commits(BDRVVVFATState* s)
2430 int i, fail = 0;
2432 vvfat_close_current_file(s);
2434 for (i = 0; !fail && i < s->commits.next; i++) {
2435 commit_t* commit = array_get(&(s->commits), i);
2436 switch(commit->action) {
2437 case ACTION_RENAME: case ACTION_MKDIR:
2438 abort();
2439 fail = -2;
2440 break;
2441 case ACTION_WRITEOUT: {
2442 #ifndef NDEBUG
2443 /* these variables are only used by assert() below */
2444 direntry_t* entry = array_get(&(s->directory),
2445 commit->param.writeout.dir_index);
2446 uint32_t begin = begin_of_direntry(entry);
2447 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2448 #endif
2450 assert(mapping);
2451 assert(mapping->begin == begin);
2452 assert(commit->path == NULL);
2454 if (commit_one_file(s, commit->param.writeout.dir_index,
2455 commit->param.writeout.modified_offset))
2456 fail = -3;
2458 break;
2460 case ACTION_NEW_FILE: {
2461 int begin = commit->param.new_file.first_cluster;
2462 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2463 direntry_t* entry;
2464 int i;
2466 /* find direntry */
2467 for (i = 0; i < s->directory.next; i++) {
2468 entry = array_get(&(s->directory), i);
2469 if (is_file(entry) && begin_of_direntry(entry) == begin)
2470 break;
2473 if (i >= s->directory.next) {
2474 fail = -6;
2475 continue;
2478 /* make sure there exists an initial mapping */
2479 if (mapping && mapping->begin != begin) {
2480 mapping->end = begin;
2481 mapping = NULL;
2483 if (mapping == NULL) {
2484 mapping = insert_mapping(s, begin, begin+1);
2486 /* most members will be fixed in commit_mappings() */
2487 assert(commit->path);
2488 mapping->path = commit->path;
2489 mapping->read_only = 0;
2490 mapping->mode = MODE_NORMAL;
2491 mapping->info.file.offset = 0;
2493 if (commit_one_file(s, i, 0))
2494 fail = -7;
2496 break;
2498 default:
2499 abort();
2502 if (i > 0 && array_remove_slice(&(s->commits), 0, i))
2503 return -1;
2504 return fail;
2507 static int handle_deletes(BDRVVVFATState* s)
2509 int i, deferred = 1, deleted = 1;
2511 /* delete files corresponding to mappings marked as deleted */
2512 /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
2513 while (deferred && deleted) {
2514 deferred = 0;
2515 deleted = 0;
2517 for (i = 1; i < s->mapping.next; i++) {
2518 mapping_t* mapping = array_get(&(s->mapping), i);
2519 if (mapping->mode & MODE_DELETED) {
2520 direntry_t* entry = array_get(&(s->directory),
2521 mapping->dir_index);
2523 if (is_free(entry)) {
2524 /* remove file/directory */
2525 if (mapping->mode & MODE_DIRECTORY) {
2526 int j, next_dir_index = s->directory.next,
2527 first_dir_index = mapping->info.dir.first_dir_index;
2529 if (rmdir(mapping->path) < 0) {
2530 if (errno == ENOTEMPTY) {
2531 deferred++;
2532 continue;
2533 } else
2534 return -5;
2537 for (j = 1; j < s->mapping.next; j++) {
2538 mapping_t* m = array_get(&(s->mapping), j);
2539 if (m->mode & MODE_DIRECTORY &&
2540 m->info.dir.first_dir_index >
2541 first_dir_index &&
2542 m->info.dir.first_dir_index <
2543 next_dir_index)
2544 next_dir_index =
2545 m->info.dir.first_dir_index;
2547 remove_direntries(s, first_dir_index,
2548 next_dir_index - first_dir_index);
2550 deleted++;
2552 } else {
2553 if (unlink(mapping->path))
2554 return -4;
2555 deleted++;
2557 DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
2558 remove_mapping(s, i);
2563 return 0;
2567 * synchronize mapping with new state:
2569 * - copy FAT (with bdrv_read)
2570 * - mark all filenames corresponding to mappings as deleted
2571 * - recurse direntries from root (using bs->bdrv_read)
2572 * - delete files corresponding to mappings marked as deleted
2574 static int do_commit(BDRVVVFATState* s)
2576 int ret = 0;
2578 /* the real meat are the commits. Nothing to do? Move along! */
2579 if (s->commits.next == 0)
2580 return 0;
2582 vvfat_close_current_file(s);
2584 ret = handle_renames_and_mkdirs(s);
2585 if (ret) {
2586 fprintf(stderr, "Error handling renames (%d)\n", ret);
2587 abort();
2588 return ret;
2591 /* copy FAT (with bdrv_read) */
2592 memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
2594 /* recurse direntries from root (using bs->bdrv_read) */
2595 ret = commit_direntries(s, 0, -1);
2596 if (ret) {
2597 fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
2598 abort();
2599 return ret;
2602 ret = handle_commits(s);
2603 if (ret) {
2604 fprintf(stderr, "Error handling commits (%d)\n", ret);
2605 abort();
2606 return ret;
2609 ret = handle_deletes(s);
2610 if (ret) {
2611 fprintf(stderr, "Error deleting\n");
2612 abort();
2613 return ret;
2616 s->qcow->drv->bdrv_make_empty(s->qcow);
2618 memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
2620 DLOG(checkpoint());
2621 return 0;
2624 static int try_commit(BDRVVVFATState* s)
2626 vvfat_close_current_file(s);
2627 DLOG(checkpoint());
2628 if(!is_consistent(s))
2629 return -1;
2630 return do_commit(s);
2633 static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
2634 const uint8_t *buf, int nb_sectors)
2636 BDRVVVFATState *s = bs->opaque;
2637 int i, ret;
2639 DLOG(checkpoint());
2641 /* Check if we're operating in read-only mode */
2642 if (s->qcow == NULL) {
2643 return -EACCES;
2646 vvfat_close_current_file(s);
2649 * Some sanity checks:
2650 * - do not allow writing to the boot sector
2651 * - do not allow to write non-ASCII filenames
2654 if (sector_num < s->first_sectors_number)
2655 return -1;
2657 for (i = sector2cluster(s, sector_num);
2658 i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
2659 mapping_t* mapping = find_mapping_for_cluster(s, i);
2660 if (mapping) {
2661 if (mapping->read_only) {
2662 fprintf(stderr, "Tried to write to write-protected file %s\n",
2663 mapping->path);
2664 return -1;
2667 if (mapping->mode & MODE_DIRECTORY) {
2668 int begin = cluster2sector(s, i);
2669 int end = begin + s->sectors_per_cluster, k;
2670 int dir_index;
2671 const direntry_t* direntries;
2672 long_file_name lfn;
2674 lfn_init(&lfn);
2676 if (begin < sector_num)
2677 begin = sector_num;
2678 if (end > sector_num + nb_sectors)
2679 end = sector_num + nb_sectors;
2680 dir_index = mapping->dir_index +
2681 0x10 * (begin - mapping->begin * s->sectors_per_cluster);
2682 direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
2684 for (k = 0; k < (end - begin) * 0x10; k++) {
2685 /* do not allow non-ASCII filenames */
2686 if (parse_long_name(&lfn, direntries + k) < 0) {
2687 fprintf(stderr, "Warning: non-ASCII filename\n");
2688 return -1;
2690 /* no access to the direntry of a read-only file */
2691 else if (is_short_name(direntries+k) &&
2692 (direntries[k].attributes & 1)) {
2693 if (memcmp(direntries + k,
2694 array_get(&(s->directory), dir_index + k),
2695 sizeof(direntry_t))) {
2696 fprintf(stderr, "Warning: tried to write to write-protected file\n");
2697 return -1;
2702 i = mapping->end;
2703 } else
2704 i++;
2708 * Use qcow backend. Commit later.
2710 DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
2711 ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
2712 if (ret < 0) {
2713 fprintf(stderr, "Error writing to qcow backend\n");
2714 return ret;
2717 for (i = sector2cluster(s, sector_num);
2718 i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
2719 if (i >= 0)
2720 s->used_clusters[i] |= USED_ALLOCATED;
2722 DLOG(checkpoint());
2723 /* TODO: add timeout */
2724 try_commit(s);
2726 DLOG(checkpoint());
2727 return 0;
2730 static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num,
2731 const uint8_t *buf, int nb_sectors)
2733 int ret;
2734 BDRVVVFATState *s = bs->opaque;
2735 qemu_co_mutex_lock(&s->lock);
2736 ret = vvfat_write(bs, sector_num, buf, nb_sectors);
2737 qemu_co_mutex_unlock(&s->lock);
2738 return ret;
2741 static int vvfat_is_allocated(BlockDriverState *bs,
2742 int64_t sector_num, int nb_sectors, int* n)
2744 BDRVVVFATState* s = bs->opaque;
2745 *n = s->sector_count - sector_num;
2746 if (*n > nb_sectors)
2747 *n = nb_sectors;
2748 else if (*n < 0)
2749 return 0;
2750 return 1;
2753 static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
2754 const uint8_t* buffer, int nb_sectors) {
2755 BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
2756 return try_commit(s);
2759 static void write_target_close(BlockDriverState *bs) {
2760 BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
2761 bdrv_delete(s->qcow);
2762 g_free(s->qcow_filename);
2765 static BlockDriver vvfat_write_target = {
2766 .format_name = "vvfat_write_target",
2767 .bdrv_write = write_target_commit,
2768 .bdrv_close = write_target_close,
2771 static int enable_write_target(BDRVVVFATState *s)
2773 BlockDriver *bdrv_qcow;
2774 QEMUOptionParameter *options;
2775 int ret;
2776 int size = sector2cluster(s, s->sector_count);
2777 s->used_clusters = calloc(size, 1);
2779 array_init(&(s->commits), sizeof(commit_t));
2781 s->qcow_filename = g_malloc(1024);
2782 get_tmp_filename(s->qcow_filename, 1024);
2784 bdrv_qcow = bdrv_find_format("qcow");
2785 options = parse_option_parameters("", bdrv_qcow->create_options, NULL);
2786 set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
2787 set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
2789 if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0)
2790 return -1;
2792 s->qcow = bdrv_new("");
2793 if (s->qcow == NULL) {
2794 return -1;
2797 ret = bdrv_open(s->qcow, s->qcow_filename,
2798 BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
2799 if (ret < 0) {
2800 return ret;
2803 #ifndef _WIN32
2804 unlink(s->qcow_filename);
2805 #endif
2807 s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
2808 s->bs->backing_hd->drv = &vvfat_write_target;
2809 s->bs->backing_hd->opaque = g_malloc(sizeof(void*));
2810 *(void**)s->bs->backing_hd->opaque = s;
2812 return 0;
2815 static void vvfat_close(BlockDriverState *bs)
2817 BDRVVVFATState *s = bs->opaque;
2819 vvfat_close_current_file(s);
2820 array_free(&(s->fat));
2821 array_free(&(s->directory));
2822 array_free(&(s->mapping));
2823 g_free(s->cluster_buffer);
2826 static BlockDriver bdrv_vvfat = {
2827 .format_name = "vvfat",
2828 .instance_size = sizeof(BDRVVVFATState),
2829 .bdrv_file_open = vvfat_open,
2830 .bdrv_read = vvfat_co_read,
2831 .bdrv_write = vvfat_co_write,
2832 .bdrv_close = vvfat_close,
2833 .bdrv_is_allocated = vvfat_is_allocated,
2834 .protocol_name = "fat",
2837 static void bdrv_vvfat_init(void)
2839 bdrv_register(&bdrv_vvfat);
2842 block_init(bdrv_vvfat_init);
2844 #ifdef DEBUG
2845 static void checkpoint(void) {
2846 assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
2847 check1(vvv);
2848 check2(vvv);
2849 assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
2850 #if 0
2851 if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
2852 fprintf(stderr, "Nonono!\n");
2853 mapping_t* mapping;
2854 direntry_t* direntry;
2855 assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
2856 assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
2857 if (vvv->mapping.next<47)
2858 return;
2859 assert((mapping = array_get(&(vvv->mapping), 47)));
2860 assert(mapping->dir_index < vvv->directory.next);
2861 direntry = array_get(&(vvv->directory), mapping->dir_index);
2862 assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0);
2863 #endif
2865 #endif