x86: translate.c: remove dead assignment
[qemu/aliguori-queue.git] / block / vvfat.c
blob063f7318cf12d0046f19bdf5eeee978472d32e91
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 return -2;
761 direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
763 /* create mapping for this file */
764 if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
765 s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
766 s->current_mapping->begin=0;
767 s->current_mapping->end=st.st_size;
769 * we get the direntry of the most recent direntry, which
770 * contains the short name and all the relevant information.
772 s->current_mapping->dir_index=s->directory.next-1;
773 s->current_mapping->first_mapping_index = -1;
774 if (S_ISDIR(st.st_mode)) {
775 s->current_mapping->mode = MODE_DIRECTORY;
776 s->current_mapping->info.dir.parent_mapping_index =
777 mapping_index;
778 } else {
779 s->current_mapping->mode = MODE_UNDEFINED;
780 s->current_mapping->info.file.offset = 0;
782 s->current_mapping->path=buffer;
783 s->current_mapping->read_only =
784 (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
787 closedir(dir);
789 /* fill with zeroes up to the end of the cluster */
790 while(s->directory.next%(0x10*s->sectors_per_cluster)) {
791 direntry_t* direntry=array_get_next(&(s->directory));
792 memset(direntry,0,sizeof(direntry_t));
795 /* TODO: if there are more entries, bootsector has to be adjusted! */
796 #define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster)
797 if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) {
798 /* root directory */
799 int cur = s->directory.next;
800 array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
801 memset(array_get(&(s->directory), cur), 0,
802 (ROOT_ENTRIES - cur) * sizeof(direntry_t));
805 /* reget the mapping, since s->mapping was possibly realloc()ed */
806 mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
807 first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
808 * 0x20 / s->cluster_size;
809 mapping->end = first_cluster;
811 direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
812 set_begin_of_direntry(direntry, mapping->begin);
814 return 0;
817 static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
819 return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
822 static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
824 return s->faked_sectors + s->sectors_per_cluster * cluster_num;
827 static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num)
829 return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster;
832 #ifdef DBG
833 static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping)
835 if(mapping->mode==MODE_UNDEFINED)
836 return 0;
837 return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index);
839 #endif
841 static int init_directories(BDRVVVFATState* s,
842 const char* dirname)
844 bootsector_t* bootsector;
845 mapping_t* mapping;
846 unsigned int i;
847 unsigned int cluster;
849 memset(&(s->first_sectors[0]),0,0x40*0x200);
851 s->cluster_size=s->sectors_per_cluster*0x200;
852 s->cluster_buffer=qemu_malloc(s->cluster_size);
855 * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
856 * where sc is sector_count,
857 * spf is sectors_per_fat,
858 * spc is sectors_per_clusters, and
859 * fat_type = 12, 16 or 32.
861 i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
862 s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
864 array_init(&(s->mapping),sizeof(mapping_t));
865 array_init(&(s->directory),sizeof(direntry_t));
867 /* add volume label */
869 direntry_t* entry=array_get_next(&(s->directory));
870 entry->attributes=0x28; /* archive | volume label */
871 snprintf((char*)entry->name,11,"QEMU VVFAT");
874 /* Now build FAT, and write back information into directory */
875 init_fat(s);
877 s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
878 s->cluster_count=sector2cluster(s, s->sector_count);
880 mapping = array_get_next(&(s->mapping));
881 mapping->begin = 0;
882 mapping->dir_index = 0;
883 mapping->info.dir.parent_mapping_index = -1;
884 mapping->first_mapping_index = -1;
885 mapping->path = strdup(dirname);
886 i = strlen(mapping->path);
887 if (i > 0 && mapping->path[i - 1] == '/')
888 mapping->path[i - 1] = '\0';
889 mapping->mode = MODE_DIRECTORY;
890 mapping->read_only = 0;
891 s->path = mapping->path;
893 for (i = 0, cluster = 0; i < s->mapping.next; i++) {
894 /* MS-DOS expects the FAT to be 0 for the root directory
895 * (except for the media byte). */
896 /* LATER TODO: still true for FAT32? */
897 int fix_fat = (i != 0);
898 mapping = array_get(&(s->mapping), i);
900 if (mapping->mode & MODE_DIRECTORY) {
901 mapping->begin = cluster;
902 if(read_directory(s, i)) {
903 fprintf(stderr, "Could not read directory %s\n",
904 mapping->path);
905 return -1;
907 mapping = array_get(&(s->mapping), i);
908 } else {
909 assert(mapping->mode == MODE_UNDEFINED);
910 mapping->mode=MODE_NORMAL;
911 mapping->begin = cluster;
912 if (mapping->end > 0) {
913 direntry_t* direntry = array_get(&(s->directory),
914 mapping->dir_index);
916 mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
917 set_begin_of_direntry(direntry, mapping->begin);
918 } else {
919 mapping->end = cluster + 1;
920 fix_fat = 0;
924 assert(mapping->begin < mapping->end);
926 /* next free cluster */
927 cluster = mapping->end;
929 if(cluster > s->cluster_count) {
930 fprintf(stderr,"Directory does not fit in FAT%d (capacity %s)\n",
931 s->fat_type,
932 s->fat_type == 12 ? s->sector_count == 2880 ? "1.44 MB"
933 : "2.88 MB"
934 : "504MB");
935 return -EINVAL;
938 /* fix fat for entry */
939 if (fix_fat) {
940 int j;
941 for(j = mapping->begin; j < mapping->end - 1; j++)
942 fat_set(s, j, j+1);
943 fat_set(s, mapping->end - 1, s->max_fat_value);
947 mapping = array_get(&(s->mapping), 0);
948 s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
949 s->last_cluster_of_root_directory = mapping->end;
951 /* the FAT signature */
952 fat_set(s,0,s->max_fat_value);
953 fat_set(s,1,s->max_fat_value);
955 s->current_mapping = NULL;
957 bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
958 bootsector->jump[0]=0xeb;
959 bootsector->jump[1]=0x3e;
960 bootsector->jump[2]=0x90;
961 memcpy(bootsector->name,"QEMU ",8);
962 bootsector->sector_size=cpu_to_le16(0x200);
963 bootsector->sectors_per_cluster=s->sectors_per_cluster;
964 bootsector->reserved_sectors=cpu_to_le16(1);
965 bootsector->number_of_fats=0x2; /* number of FATs */
966 bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
967 bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
968 bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
969 s->fat.pointer[0] = bootsector->media_type;
970 bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
971 bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
972 bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
973 bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
974 bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
976 /* LATER TODO: if FAT32, this is wrong */
977 bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
978 bootsector->u.fat16.current_head=0;
979 bootsector->u.fat16.signature=0x29;
980 bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
982 memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
983 memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8);
984 bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
986 return 0;
989 #ifdef DEBUG
990 static BDRVVVFATState *vvv = NULL;
991 #endif
993 static int enable_write_target(BDRVVVFATState *s);
994 static int is_consistent(BDRVVVFATState *s);
996 static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
998 BDRVVVFATState *s = bs->opaque;
999 int floppy = 0;
1000 int i;
1002 #ifdef DEBUG
1003 vvv = s;
1004 #endif
1006 DLOG(if (stderr == NULL) {
1007 stderr = fopen("vvfat.log", "a");
1008 setbuf(stderr, NULL);
1011 s->bs = bs;
1013 s->fat_type=16;
1014 /* LATER TODO: if FAT32, adjust */
1015 s->sectors_per_cluster=0x10;
1016 /* 504MB disk*/
1017 bs->cyls=1024; bs->heads=16; bs->secs=63;
1019 s->current_cluster=0xffffffff;
1021 s->first_sectors_number=0x40;
1022 /* read only is the default for safety */
1023 bs->read_only = 1;
1024 s->qcow = s->write_target = NULL;
1025 s->qcow_filename = NULL;
1026 s->fat2 = NULL;
1027 s->downcase_short_names = 1;
1029 if (!strstart(dirname, "fat:", NULL))
1030 return -1;
1032 if (strstr(dirname, ":floppy:")) {
1033 floppy = 1;
1034 s->fat_type = 12;
1035 s->first_sectors_number = 1;
1036 s->sectors_per_cluster=2;
1037 bs->cyls = 80; bs->heads = 2; bs->secs = 36;
1040 s->sector_count=bs->cyls*bs->heads*bs->secs;
1042 if (strstr(dirname, ":32:")) {
1043 fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
1044 s->fat_type = 32;
1045 } else if (strstr(dirname, ":16:")) {
1046 s->fat_type = 16;
1047 } else if (strstr(dirname, ":12:")) {
1048 s->fat_type = 12;
1049 s->sector_count=2880;
1052 if (strstr(dirname, ":rw:")) {
1053 if (enable_write_target(s))
1054 return -1;
1055 bs->read_only = 0;
1058 i = strrchr(dirname, ':') - dirname;
1059 assert(i >= 3);
1060 if (dirname[i-2] == ':' && qemu_isalpha(dirname[i-1]))
1061 /* workaround for DOS drive names */
1062 dirname += i-1;
1063 else
1064 dirname += i+1;
1066 bs->total_sectors=bs->cyls*bs->heads*bs->secs;
1068 if(init_directories(s, dirname))
1069 return -1;
1071 s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
1073 if(s->first_sectors_number==0x40)
1074 init_mbr(s);
1076 /* for some reason or other, MS-DOS does not like to know about CHS... */
1077 if (floppy)
1078 bs->heads = bs->cyls = bs->secs = 0;
1080 // assert(is_consistent(s));
1081 return 0;
1084 static inline void vvfat_close_current_file(BDRVVVFATState *s)
1086 if(s->current_mapping) {
1087 s->current_mapping = NULL;
1088 if (s->current_fd) {
1089 close(s->current_fd);
1090 s->current_fd = 0;
1093 s->current_cluster = -1;
1096 /* mappings between index1 and index2-1 are supposed to be ordered
1097 * return value is the index of the last mapping for which end>cluster_num
1099 static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
1101 int index3=index1+1;
1102 while(1) {
1103 mapping_t* mapping;
1104 index3=(index1+index2)/2;
1105 mapping=array_get(&(s->mapping),index3);
1106 assert(mapping->begin < mapping->end);
1107 if(mapping->begin>=cluster_num) {
1108 assert(index2!=index3 || index2==0);
1109 if(index2==index3)
1110 return index1;
1111 index2=index3;
1112 } else {
1113 if(index1==index3)
1114 return mapping->end<=cluster_num ? index2 : index1;
1115 index1=index3;
1117 assert(index1<=index2);
1118 DLOG(mapping=array_get(&(s->mapping),index1);
1119 assert(mapping->begin<=cluster_num);
1120 assert(index2 >= s->mapping.next ||
1121 ((mapping = array_get(&(s->mapping),index2)) &&
1122 mapping->end>cluster_num)));
1126 static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
1128 int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
1129 mapping_t* mapping;
1130 if(index>=s->mapping.next)
1131 return NULL;
1132 mapping=array_get(&(s->mapping),index);
1133 if(mapping->begin>cluster_num)
1134 return NULL;
1135 assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
1136 return mapping;
1140 * This function simply compares path == mapping->path. Since the mappings
1141 * are sorted by cluster, this is expensive: O(n).
1143 static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s,
1144 const char* path)
1146 int i;
1148 for (i = 0; i < s->mapping.next; i++) {
1149 mapping_t* mapping = array_get(&(s->mapping), i);
1150 if (mapping->first_mapping_index < 0 &&
1151 !strcmp(path, mapping->path))
1152 return mapping;
1155 return NULL;
1158 static int open_file(BDRVVVFATState* s,mapping_t* mapping)
1160 if(!mapping)
1161 return -1;
1162 if(!s->current_mapping ||
1163 strcmp(s->current_mapping->path,mapping->path)) {
1164 /* open file */
1165 int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
1166 if(fd<0)
1167 return -1;
1168 vvfat_close_current_file(s);
1169 s->current_fd = fd;
1170 s->current_mapping = mapping;
1172 return 0;
1175 static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
1177 if(s->current_cluster != cluster_num) {
1178 int result=0;
1179 off_t offset;
1180 assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
1181 if(!s->current_mapping
1182 || s->current_mapping->begin>cluster_num
1183 || s->current_mapping->end<=cluster_num) {
1184 /* binary search of mappings for file */
1185 mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
1187 assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
1189 if (mapping && mapping->mode & MODE_DIRECTORY) {
1190 vvfat_close_current_file(s);
1191 s->current_mapping = mapping;
1192 read_cluster_directory:
1193 offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
1194 s->cluster = (unsigned char*)s->directory.pointer+offset
1195 + 0x20*s->current_mapping->info.dir.first_dir_index;
1196 assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
1197 assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
1198 s->current_cluster = cluster_num;
1199 return 0;
1202 if(open_file(s,mapping))
1203 return -2;
1204 } else if (s->current_mapping->mode & MODE_DIRECTORY)
1205 goto read_cluster_directory;
1207 assert(s->current_fd);
1209 offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
1210 if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
1211 return -3;
1212 s->cluster=s->cluster_buffer;
1213 result=read(s->current_fd,s->cluster,s->cluster_size);
1214 if(result<0) {
1215 s->current_cluster = -1;
1216 return -1;
1218 s->current_cluster = cluster_num;
1220 return 0;
1223 #ifdef DEBUG
1224 static void hexdump(const void* address, uint32_t len)
1226 const unsigned char* p = address;
1227 int i, j;
1229 for (i = 0; i < len; i += 16) {
1230 for (j = 0; j < 16 && i + j < len; j++)
1231 fprintf(stderr, "%02x ", p[i + j]);
1232 for (; j < 16; j++)
1233 fprintf(stderr, " ");
1234 fprintf(stderr, " ");
1235 for (j = 0; j < 16 && i + j < len; j++)
1236 fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
1237 fprintf(stderr, "\n");
1241 static void print_direntry(const direntry_t* direntry)
1243 int j = 0;
1244 char buffer[1024];
1246 fprintf(stderr, "direntry 0x%x: ", (int)direntry);
1247 if(!direntry)
1248 return;
1249 if(is_long_name(direntry)) {
1250 unsigned char* c=(unsigned char*)direntry;
1251 int i;
1252 for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
1253 #define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;}
1254 ADD_CHAR(c[i]);
1255 for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
1256 ADD_CHAR(c[i]);
1257 for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
1258 ADD_CHAR(c[i]);
1259 buffer[j] = 0;
1260 fprintf(stderr, "%s\n", buffer);
1261 } else {
1262 int i;
1263 for(i=0;i<11;i++)
1264 ADD_CHAR(direntry->name[i]);
1265 buffer[j] = 0;
1266 fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
1267 buffer,
1268 direntry->attributes,
1269 begin_of_direntry(direntry),le32_to_cpu(direntry->size));
1273 static void print_mapping(const mapping_t* mapping)
1275 fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode);
1276 if (mapping->mode & MODE_DIRECTORY)
1277 fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
1278 else
1279 fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
1281 #endif
1283 static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
1284 uint8_t *buf, int nb_sectors)
1286 BDRVVVFATState *s = bs->opaque;
1287 int i;
1289 for(i=0;i<nb_sectors;i++,sector_num++) {
1290 if (sector_num >= s->sector_count)
1291 return -1;
1292 if (s->qcow) {
1293 int n;
1294 if (s->qcow->drv->bdrv_is_allocated(s->qcow,
1295 sector_num, nb_sectors-i, &n)) {
1296 DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
1297 if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
1298 return -1;
1299 i += n - 1;
1300 sector_num += n - 1;
1301 continue;
1303 DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
1305 if(sector_num<s->faked_sectors) {
1306 if(sector_num<s->first_sectors_number)
1307 memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
1308 else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
1309 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
1310 else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
1311 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
1312 } else {
1313 uint32_t sector=sector_num-s->faked_sectors,
1314 sector_offset_in_cluster=(sector%s->sectors_per_cluster),
1315 cluster_num=sector/s->sectors_per_cluster;
1316 if(read_cluster(s, cluster_num) != 0) {
1317 /* LATER TODO: strict: return -1; */
1318 memset(buf+i*0x200,0,0x200);
1319 continue;
1321 memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
1324 return 0;
1327 /* LATER TODO: statify all functions */
1330 * Idea of the write support (use snapshot):
1332 * 1. check if all data is consistent, recording renames, modifications,
1333 * new files and directories (in s->commits).
1335 * 2. if the data is not consistent, stop committing
1337 * 3. handle renames, and create new files and directories (do not yet
1338 * write their contents)
1340 * 4. walk the directories, fixing the mapping and direntries, and marking
1341 * the handled mappings as not deleted
1343 * 5. commit the contents of the files
1345 * 6. handle deleted files and directories
1349 typedef struct commit_t {
1350 char* path;
1351 union {
1352 struct { uint32_t cluster; } rename;
1353 struct { int dir_index; uint32_t modified_offset; } writeout;
1354 struct { uint32_t first_cluster; } new_file;
1355 struct { uint32_t cluster; } mkdir;
1356 } param;
1357 /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
1358 enum {
1359 ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
1360 } action;
1361 } commit_t;
1363 static void clear_commits(BDRVVVFATState* s)
1365 int i;
1366 DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
1367 for (i = 0; i < s->commits.next; i++) {
1368 commit_t* commit = array_get(&(s->commits), i);
1369 assert(commit->path || commit->action == ACTION_WRITEOUT);
1370 if (commit->action != ACTION_WRITEOUT) {
1371 assert(commit->path);
1372 free(commit->path);
1373 } else
1374 assert(commit->path == NULL);
1376 s->commits.next = 0;
1379 static void schedule_rename(BDRVVVFATState* s,
1380 uint32_t cluster, char* new_path)
1382 commit_t* commit = array_get_next(&(s->commits));
1383 commit->path = new_path;
1384 commit->param.rename.cluster = cluster;
1385 commit->action = ACTION_RENAME;
1388 static void schedule_writeout(BDRVVVFATState* s,
1389 int dir_index, uint32_t modified_offset)
1391 commit_t* commit = array_get_next(&(s->commits));
1392 commit->path = NULL;
1393 commit->param.writeout.dir_index = dir_index;
1394 commit->param.writeout.modified_offset = modified_offset;
1395 commit->action = ACTION_WRITEOUT;
1398 static void schedule_new_file(BDRVVVFATState* s,
1399 char* path, uint32_t first_cluster)
1401 commit_t* commit = array_get_next(&(s->commits));
1402 commit->path = path;
1403 commit->param.new_file.first_cluster = first_cluster;
1404 commit->action = ACTION_NEW_FILE;
1407 static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
1409 commit_t* commit = array_get_next(&(s->commits));
1410 commit->path = path;
1411 commit->param.mkdir.cluster = cluster;
1412 commit->action = ACTION_MKDIR;
1415 typedef struct {
1417 * Since the sequence number is at most 0x3f, and the filename
1418 * length is at most 13 times the sequence number, the maximal
1419 * filename length is 0x3f * 13 bytes.
1421 unsigned char name[0x3f * 13 + 1];
1422 int checksum, len;
1423 int sequence_number;
1424 } long_file_name;
1426 static void lfn_init(long_file_name* lfn)
1428 lfn->sequence_number = lfn->len = 0;
1429 lfn->checksum = 0x100;
1432 /* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
1433 static int parse_long_name(long_file_name* lfn,
1434 const direntry_t* direntry)
1436 int i, j, offset;
1437 const unsigned char* pointer = (const unsigned char*)direntry;
1439 if (!is_long_name(direntry))
1440 return 1;
1442 if (pointer[0] & 0x40) {
1443 lfn->sequence_number = pointer[0] & 0x3f;
1444 lfn->checksum = pointer[13];
1445 lfn->name[0] = 0;
1446 lfn->name[lfn->sequence_number * 13] = 0;
1447 } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
1448 return -1;
1449 else if (pointer[13] != lfn->checksum)
1450 return -2;
1451 else if (pointer[12] || pointer[26] || pointer[27])
1452 return -3;
1454 offset = 13 * (lfn->sequence_number - 1);
1455 for (i = 0, j = 1; i < 13; i++, j+=2) {
1456 if (j == 11)
1457 j = 14;
1458 else if (j == 26)
1459 j = 28;
1461 if (pointer[j+1] == 0)
1462 lfn->name[offset + i] = pointer[j];
1463 else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
1464 return -4;
1465 else
1466 lfn->name[offset + i] = 0;
1469 if (pointer[0] & 0x40)
1470 lfn->len = offset + strlen((char*)lfn->name + offset);
1472 return 0;
1475 /* returns 0 if successful, >0 if no short_name, and <0 on error */
1476 static int parse_short_name(BDRVVVFATState* s,
1477 long_file_name* lfn, direntry_t* direntry)
1479 int i, j;
1481 if (!is_short_name(direntry))
1482 return 1;
1484 for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
1485 for (i = 0; i <= j; i++) {
1486 if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
1487 return -1;
1488 else if (s->downcase_short_names)
1489 lfn->name[i] = qemu_tolower(direntry->name[i]);
1490 else
1491 lfn->name[i] = direntry->name[i];
1494 for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
1495 if (j >= 0) {
1496 lfn->name[i++] = '.';
1497 lfn->name[i + j + 1] = '\0';
1498 for (;j >= 0; j--) {
1499 if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
1500 return -2;
1501 else if (s->downcase_short_names)
1502 lfn->name[i + j] = qemu_tolower(direntry->extension[j]);
1503 else
1504 lfn->name[i + j] = direntry->extension[j];
1506 } else
1507 lfn->name[i + j + 1] = '\0';
1509 lfn->len = strlen((char*)lfn->name);
1511 return 0;
1514 static inline uint32_t modified_fat_get(BDRVVVFATState* s,
1515 unsigned int cluster)
1517 if (cluster < s->last_cluster_of_root_directory) {
1518 if (cluster + 1 == s->last_cluster_of_root_directory)
1519 return s->max_fat_value;
1520 else
1521 return cluster + 1;
1524 if (s->fat_type==32) {
1525 uint32_t* entry=((uint32_t*)s->fat2)+cluster;
1526 return le32_to_cpu(*entry);
1527 } else if (s->fat_type==16) {
1528 uint16_t* entry=((uint16_t*)s->fat2)+cluster;
1529 return le16_to_cpu(*entry);
1530 } else {
1531 const uint8_t* x=s->fat2+cluster*3/2;
1532 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
1536 static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
1538 int was_modified = 0;
1539 int i, dummy;
1541 if (s->qcow == NULL)
1542 return 0;
1544 for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
1545 was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
1546 cluster2sector(s, cluster_num) + i, 1, &dummy);
1548 return was_modified;
1551 static const char* get_basename(const char* path)
1553 char* basename = strrchr(path, '/');
1554 if (basename == NULL)
1555 return path;
1556 else
1557 return basename + 1; /* strip '/' */
1561 * The array s->used_clusters holds the states of the clusters. If it is
1562 * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
1563 * was modified, bit 3 is set.
1564 * If any cluster is allocated, but not part of a file or directory, this
1565 * driver refuses to commit.
1567 typedef enum {
1568 USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
1569 } used_t;
1572 * get_cluster_count_for_direntry() not only determines how many clusters
1573 * are occupied by direntry, but also if it was renamed or modified.
1575 * A file is thought to be renamed *only* if there already was a file with
1576 * exactly the same first cluster, but a different name.
1578 * Further, the files/directories handled by this function are
1579 * assumed to be *not* deleted (and *only* those).
1581 static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
1582 direntry_t* direntry, const char* path)
1585 * This is a little bit tricky:
1586 * IF the guest OS just inserts a cluster into the file chain,
1587 * and leaves the rest alone, (i.e. the original file had clusters
1588 * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
1590 * - do_commit will write the cluster into the file at the given
1591 * offset, but
1593 * - the cluster which is overwritten should be moved to a later
1594 * position in the file.
1596 * I am not aware that any OS does something as braindead, but this
1597 * situation could happen anyway when not committing for a long time.
1598 * Just to be sure that this does not bite us, detect it, and copy the
1599 * contents of the clusters to-be-overwritten into the qcow.
1601 int copy_it = 0;
1602 int was_modified = 0;
1603 int32_t ret = 0;
1605 uint32_t cluster_num = begin_of_direntry(direntry);
1606 uint32_t offset = 0;
1607 int first_mapping_index = -1;
1608 mapping_t* mapping = NULL;
1609 const char* basename2 = NULL;
1611 vvfat_close_current_file(s);
1613 /* the root directory */
1614 if (cluster_num == 0)
1615 return 0;
1617 /* write support */
1618 if (s->qcow) {
1619 basename2 = get_basename(path);
1621 mapping = find_mapping_for_cluster(s, cluster_num);
1623 if (mapping) {
1624 const char* basename;
1626 assert(mapping->mode & MODE_DELETED);
1627 mapping->mode &= ~MODE_DELETED;
1629 basename = get_basename(mapping->path);
1631 assert(mapping->mode & MODE_NORMAL);
1633 /* rename */
1634 if (strcmp(basename, basename2))
1635 schedule_rename(s, cluster_num, strdup(path));
1636 } else if (is_file(direntry))
1637 /* new file */
1638 schedule_new_file(s, strdup(path), cluster_num);
1639 else {
1640 assert(0);
1641 return 0;
1645 while(1) {
1646 if (s->qcow) {
1647 if (!copy_it && cluster_was_modified(s, cluster_num)) {
1648 if (mapping == NULL ||
1649 mapping->begin > cluster_num ||
1650 mapping->end <= cluster_num)
1651 mapping = find_mapping_for_cluster(s, cluster_num);
1654 if (mapping &&
1655 (mapping->mode & MODE_DIRECTORY) == 0) {
1657 /* was modified in qcow */
1658 if (offset != mapping->info.file.offset + s->cluster_size
1659 * (cluster_num - mapping->begin)) {
1660 /* offset of this cluster in file chain has changed */
1661 assert(0);
1662 copy_it = 1;
1663 } else if (offset == 0) {
1664 const char* basename = get_basename(mapping->path);
1666 if (strcmp(basename, basename2))
1667 copy_it = 1;
1668 first_mapping_index = array_index(&(s->mapping), mapping);
1671 if (mapping->first_mapping_index != first_mapping_index
1672 && mapping->info.file.offset > 0) {
1673 assert(0);
1674 copy_it = 1;
1677 /* need to write out? */
1678 if (!was_modified && is_file(direntry)) {
1679 was_modified = 1;
1680 schedule_writeout(s, mapping->dir_index, offset);
1685 if (copy_it) {
1686 int i, dummy;
1688 * This is horribly inefficient, but that is okay, since
1689 * it is rarely executed, if at all.
1691 int64_t offset = cluster2sector(s, cluster_num);
1693 vvfat_close_current_file(s);
1694 for (i = 0; i < s->sectors_per_cluster; i++)
1695 if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
1696 offset + i, 1, &dummy)) {
1697 if (vvfat_read(s->bs,
1698 offset, s->cluster_buffer, 1))
1699 return -1;
1700 if (s->qcow->drv->bdrv_write(s->qcow,
1701 offset, s->cluster_buffer, 1))
1702 return -2;
1707 ret++;
1708 if (s->used_clusters[cluster_num] & USED_ANY)
1709 return 0;
1710 s->used_clusters[cluster_num] = USED_FILE;
1712 cluster_num = modified_fat_get(s, cluster_num);
1714 if (fat_eof(s, cluster_num))
1715 return ret;
1716 else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
1717 return -1;
1719 offset += s->cluster_size;
1724 * This function looks at the modified data (qcow).
1725 * It returns 0 upon inconsistency or error, and the number of clusters
1726 * used by the directory, its subdirectories and their files.
1728 static int check_directory_consistency(BDRVVVFATState *s,
1729 int cluster_num, const char* path)
1731 int ret = 0;
1732 unsigned char* cluster = qemu_malloc(s->cluster_size);
1733 direntry_t* direntries = (direntry_t*)cluster;
1734 mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
1736 long_file_name lfn;
1737 int path_len = strlen(path);
1738 char path2[PATH_MAX];
1740 assert(path_len < PATH_MAX); /* len was tested before! */
1741 pstrcpy(path2, sizeof(path2), path);
1742 path2[path_len] = '/';
1743 path2[path_len + 1] = '\0';
1745 if (mapping) {
1746 const char* basename = get_basename(mapping->path);
1747 const char* basename2 = get_basename(path);
1749 assert(mapping->mode & MODE_DIRECTORY);
1751 assert(mapping->mode & MODE_DELETED);
1752 mapping->mode &= ~MODE_DELETED;
1754 if (strcmp(basename, basename2))
1755 schedule_rename(s, cluster_num, strdup(path));
1756 } else
1757 /* new directory */
1758 schedule_mkdir(s, cluster_num, strdup(path));
1760 lfn_init(&lfn);
1761 do {
1762 int i;
1763 int subret = 0;
1765 ret++;
1767 if (s->used_clusters[cluster_num] & USED_ANY) {
1768 fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
1769 return 0;
1771 s->used_clusters[cluster_num] = USED_DIRECTORY;
1773 DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
1774 subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
1775 s->sectors_per_cluster);
1776 if (subret) {
1777 fprintf(stderr, "Error fetching direntries\n");
1778 fail:
1779 free(cluster);
1780 return 0;
1783 for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
1784 int cluster_count = 0;
1786 DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i));
1787 if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
1788 is_free(direntries + i))
1789 continue;
1791 subret = parse_long_name(&lfn, direntries + i);
1792 if (subret < 0) {
1793 fprintf(stderr, "Error in long name\n");
1794 goto fail;
1796 if (subret == 0 || is_free(direntries + i))
1797 continue;
1799 if (fat_chksum(direntries+i) != lfn.checksum) {
1800 subret = parse_short_name(s, &lfn, direntries + i);
1801 if (subret < 0) {
1802 fprintf(stderr, "Error in short name (%d)\n", subret);
1803 goto fail;
1805 if (subret > 0 || !strcmp((char*)lfn.name, ".")
1806 || !strcmp((char*)lfn.name, ".."))
1807 continue;
1809 lfn.checksum = 0x100; /* cannot use long name twice */
1811 if (path_len + 1 + lfn.len >= PATH_MAX) {
1812 fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
1813 goto fail;
1815 pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
1816 (char*)lfn.name);
1818 if (is_directory(direntries + i)) {
1819 if (begin_of_direntry(direntries + i) == 0) {
1820 DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
1821 goto fail;
1823 cluster_count = check_directory_consistency(s,
1824 begin_of_direntry(direntries + i), path2);
1825 if (cluster_count == 0) {
1826 DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
1827 goto fail;
1829 } else if (is_file(direntries + i)) {
1830 /* check file size with FAT */
1831 cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
1832 if (cluster_count !=
1833 (le32_to_cpu(direntries[i].size) + s->cluster_size
1834 - 1) / s->cluster_size) {
1835 DLOG(fprintf(stderr, "Cluster count mismatch\n"));
1836 goto fail;
1838 } else
1839 assert(0); /* cluster_count = 0; */
1841 ret += cluster_count;
1844 cluster_num = modified_fat_get(s, cluster_num);
1845 } while(!fat_eof(s, cluster_num));
1847 free(cluster);
1848 return ret;
1851 /* returns 1 on success */
1852 static int is_consistent(BDRVVVFATState* s)
1854 int i, check;
1855 int used_clusters_count = 0;
1857 DLOG(checkpoint());
1859 * - get modified FAT
1860 * - compare the two FATs (TODO)
1861 * - get buffer for marking used clusters
1862 * - recurse direntries from root (using bs->bdrv_read to make
1863 * sure to get the new data)
1864 * - check that the FAT agrees with the size
1865 * - count the number of clusters occupied by this directory and
1866 * its files
1867 * - check that the cumulative used cluster count agrees with the
1868 * FAT
1869 * - if all is fine, return number of used clusters
1871 if (s->fat2 == NULL) {
1872 int size = 0x200 * s->sectors_per_fat;
1873 s->fat2 = qemu_malloc(size);
1874 memcpy(s->fat2, s->fat.pointer, size);
1876 check = vvfat_read(s->bs,
1877 s->first_sectors_number, s->fat2, s->sectors_per_fat);
1878 if (check) {
1879 fprintf(stderr, "Could not copy fat\n");
1880 return 0;
1882 assert (s->used_clusters);
1883 for (i = 0; i < sector2cluster(s, s->sector_count); i++)
1884 s->used_clusters[i] &= ~USED_ANY;
1886 clear_commits(s);
1888 /* mark every mapped file/directory as deleted.
1889 * (check_directory_consistency() will unmark those still present). */
1890 if (s->qcow)
1891 for (i = 0; i < s->mapping.next; i++) {
1892 mapping_t* mapping = array_get(&(s->mapping), i);
1893 if (mapping->first_mapping_index < 0)
1894 mapping->mode |= MODE_DELETED;
1897 used_clusters_count = check_directory_consistency(s, 0, s->path);
1898 if (used_clusters_count <= 0) {
1899 DLOG(fprintf(stderr, "problem in directory\n"));
1900 return 0;
1903 check = s->last_cluster_of_root_directory;
1904 for (i = check; i < sector2cluster(s, s->sector_count); i++) {
1905 if (modified_fat_get(s, i)) {
1906 if(!s->used_clusters[i]) {
1907 DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
1908 return 0;
1910 check++;
1913 if (s->used_clusters[i] == USED_ALLOCATED) {
1914 /* allocated, but not used... */
1915 DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
1916 return 0;
1920 if (check != used_clusters_count)
1921 return 0;
1923 return used_clusters_count;
1926 static inline void adjust_mapping_indices(BDRVVVFATState* s,
1927 int offset, int adjust)
1929 int i;
1931 for (i = 0; i < s->mapping.next; i++) {
1932 mapping_t* mapping = array_get(&(s->mapping), i);
1934 #define ADJUST_MAPPING_INDEX(name) \
1935 if (mapping->name >= offset) \
1936 mapping->name += adjust
1938 ADJUST_MAPPING_INDEX(first_mapping_index);
1939 if (mapping->mode & MODE_DIRECTORY)
1940 ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
1944 /* insert or update mapping */
1945 static mapping_t* insert_mapping(BDRVVVFATState* s,
1946 uint32_t begin, uint32_t end)
1949 * - find mapping where mapping->begin >= begin,
1950 * - if mapping->begin > begin: insert
1951 * - adjust all references to mappings!
1952 * - else: adjust
1953 * - replace name
1955 int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
1956 mapping_t* mapping = NULL;
1957 mapping_t* first_mapping = array_get(&(s->mapping), 0);
1959 if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
1960 && mapping->begin < begin) {
1961 mapping->end = begin;
1962 index++;
1963 mapping = array_get(&(s->mapping), index);
1965 if (index >= s->mapping.next || mapping->begin > begin) {
1966 mapping = array_insert(&(s->mapping), index, 1);
1967 mapping->path = NULL;
1968 adjust_mapping_indices(s, index, +1);
1971 mapping->begin = begin;
1972 mapping->end = end;
1974 DLOG(mapping_t* next_mapping;
1975 assert(index + 1 >= s->mapping.next ||
1976 ((next_mapping = array_get(&(s->mapping), index + 1)) &&
1977 next_mapping->begin >= end)));
1979 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1980 s->current_mapping = array_get(&(s->mapping),
1981 s->current_mapping - first_mapping);
1983 return mapping;
1986 static int remove_mapping(BDRVVVFATState* s, int mapping_index)
1988 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
1989 mapping_t* first_mapping = array_get(&(s->mapping), 0);
1991 /* free mapping */
1992 if (mapping->first_mapping_index < 0)
1993 free(mapping->path);
1995 /* remove from s->mapping */
1996 array_remove(&(s->mapping), mapping_index);
1998 /* adjust all references to mappings */
1999 adjust_mapping_indices(s, mapping_index, -1);
2001 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
2002 s->current_mapping = array_get(&(s->mapping),
2003 s->current_mapping - first_mapping);
2005 return 0;
2008 static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
2010 int i;
2011 for (i = 0; i < s->mapping.next; i++) {
2012 mapping_t* mapping = array_get(&(s->mapping), i);
2013 if (mapping->dir_index >= offset)
2014 mapping->dir_index += adjust;
2015 if ((mapping->mode & MODE_DIRECTORY) &&
2016 mapping->info.dir.first_dir_index >= offset)
2017 mapping->info.dir.first_dir_index += adjust;
2021 static direntry_t* insert_direntries(BDRVVVFATState* s,
2022 int dir_index, int count)
2025 * make room in s->directory,
2026 * adjust_dirindices
2028 direntry_t* result = array_insert(&(s->directory), dir_index, count);
2029 if (result == NULL)
2030 return NULL;
2031 adjust_dirindices(s, dir_index, count);
2032 return result;
2035 static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
2037 int ret = array_remove_slice(&(s->directory), dir_index, count);
2038 if (ret)
2039 return ret;
2040 adjust_dirindices(s, dir_index, -count);
2041 return 0;
2045 * Adapt the mappings of the cluster chain starting at first cluster
2046 * (i.e. if a file starts at first_cluster, the chain is followed according
2047 * to the modified fat, and the corresponding entries in s->mapping are
2048 * adjusted)
2050 static int commit_mappings(BDRVVVFATState* s,
2051 uint32_t first_cluster, int dir_index)
2053 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2054 direntry_t* direntry = array_get(&(s->directory), dir_index);
2055 uint32_t cluster = first_cluster;
2057 vvfat_close_current_file(s);
2059 assert(mapping);
2060 assert(mapping->begin == first_cluster);
2061 mapping->first_mapping_index = -1;
2062 mapping->dir_index = dir_index;
2063 mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
2064 MODE_DIRECTORY : MODE_NORMAL;
2066 while (!fat_eof(s, cluster)) {
2067 uint32_t c, c1;
2069 for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
2070 c = c1, c1 = modified_fat_get(s, c1));
2072 c++;
2073 if (c > mapping->end) {
2074 int index = array_index(&(s->mapping), mapping);
2075 int i, max_i = s->mapping.next - index;
2076 for (i = 1; i < max_i && mapping[i].begin < c; i++);
2077 while (--i > 0)
2078 remove_mapping(s, index + 1);
2080 assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
2081 || mapping[1].begin >= c);
2082 mapping->end = c;
2084 if (!fat_eof(s, c1)) {
2085 int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
2086 mapping_t* next_mapping = i >= s->mapping.next ? NULL :
2087 array_get(&(s->mapping), i);
2089 if (next_mapping == NULL || next_mapping->begin > c1) {
2090 int i1 = array_index(&(s->mapping), mapping);
2092 next_mapping = insert_mapping(s, c1, c1+1);
2094 if (c1 < c)
2095 i1++;
2096 mapping = array_get(&(s->mapping), i1);
2099 next_mapping->dir_index = mapping->dir_index;
2100 next_mapping->first_mapping_index =
2101 mapping->first_mapping_index < 0 ?
2102 array_index(&(s->mapping), mapping) :
2103 mapping->first_mapping_index;
2104 next_mapping->path = mapping->path;
2105 next_mapping->mode = mapping->mode;
2106 next_mapping->read_only = mapping->read_only;
2107 if (mapping->mode & MODE_DIRECTORY) {
2108 next_mapping->info.dir.parent_mapping_index =
2109 mapping->info.dir.parent_mapping_index;
2110 next_mapping->info.dir.first_dir_index =
2111 mapping->info.dir.first_dir_index +
2112 0x10 * s->sectors_per_cluster *
2113 (mapping->end - mapping->begin);
2114 } else
2115 next_mapping->info.file.offset = mapping->info.file.offset +
2116 mapping->end - mapping->begin;
2118 mapping = next_mapping;
2121 cluster = c1;
2124 return 0;
2127 static int commit_direntries(BDRVVVFATState* s,
2128 int dir_index, int parent_mapping_index)
2130 direntry_t* direntry = array_get(&(s->directory), dir_index);
2131 uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
2132 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2134 int factor = 0x10 * s->sectors_per_cluster;
2135 int old_cluster_count, new_cluster_count;
2136 int current_dir_index = mapping->info.dir.first_dir_index;
2137 int first_dir_index = current_dir_index;
2138 int ret, i;
2139 uint32_t c;
2141 DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
2143 assert(direntry);
2144 assert(mapping);
2145 assert(mapping->begin == first_cluster);
2146 assert(mapping->info.dir.first_dir_index < s->directory.next);
2147 assert(mapping->mode & MODE_DIRECTORY);
2148 assert(dir_index == 0 || is_directory(direntry));
2150 mapping->info.dir.parent_mapping_index = parent_mapping_index;
2152 if (first_cluster == 0) {
2153 old_cluster_count = new_cluster_count =
2154 s->last_cluster_of_root_directory;
2155 } else {
2156 for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2157 c = fat_get(s, c))
2158 old_cluster_count++;
2160 for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2161 c = modified_fat_get(s, c))
2162 new_cluster_count++;
2165 if (new_cluster_count > old_cluster_count) {
2166 if (insert_direntries(s,
2167 current_dir_index + factor * old_cluster_count,
2168 factor * (new_cluster_count - old_cluster_count)) == NULL)
2169 return -1;
2170 } else if (new_cluster_count < old_cluster_count)
2171 remove_direntries(s,
2172 current_dir_index + factor * new_cluster_count,
2173 factor * (old_cluster_count - new_cluster_count));
2175 for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
2176 void* direntry = array_get(&(s->directory), current_dir_index);
2177 int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
2178 s->sectors_per_cluster);
2179 if (ret)
2180 return ret;
2181 assert(!strncmp(s->directory.pointer, "QEMU", 4));
2182 current_dir_index += factor;
2185 ret = commit_mappings(s, first_cluster, dir_index);
2186 if (ret)
2187 return ret;
2189 /* recurse */
2190 for (i = 0; i < factor * new_cluster_count; i++) {
2191 direntry = array_get(&(s->directory), first_dir_index + i);
2192 if (is_directory(direntry) && !is_dot(direntry)) {
2193 mapping = find_mapping_for_cluster(s, first_cluster);
2194 assert(mapping->mode & MODE_DIRECTORY);
2195 ret = commit_direntries(s, first_dir_index + i,
2196 array_index(&(s->mapping), mapping));
2197 if (ret)
2198 return ret;
2202 return 0;
2205 /* commit one file (adjust contents, adjust mapping),
2206 return first_mapping_index */
2207 static int commit_one_file(BDRVVVFATState* s,
2208 int dir_index, uint32_t offset)
2210 direntry_t* direntry = array_get(&(s->directory), dir_index);
2211 uint32_t c = begin_of_direntry(direntry);
2212 uint32_t first_cluster = c;
2213 mapping_t* mapping = find_mapping_for_cluster(s, c);
2214 uint32_t size = filesize_of_direntry(direntry);
2215 char* cluster = qemu_malloc(s->cluster_size);
2216 uint32_t i;
2217 int fd = 0;
2219 assert(offset < size);
2220 assert((offset % s->cluster_size) == 0);
2222 for (i = s->cluster_size; i < offset; i += s->cluster_size)
2223 c = modified_fat_get(s, c);
2225 fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
2226 if (fd < 0) {
2227 fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
2228 strerror(errno), errno);
2229 return fd;
2231 if (offset > 0)
2232 if (lseek(fd, offset, SEEK_SET) != offset)
2233 return -3;
2235 while (offset < size) {
2236 uint32_t c1;
2237 int rest_size = (size - offset > s->cluster_size ?
2238 s->cluster_size : size - offset);
2239 int ret;
2241 c1 = modified_fat_get(s, c);
2243 assert((size - offset == 0 && fat_eof(s, c)) ||
2244 (size > offset && c >=2 && !fat_eof(s, c)));
2246 ret = vvfat_read(s->bs, cluster2sector(s, c),
2247 (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
2249 if (ret < 0)
2250 return ret;
2252 if (write(fd, cluster, rest_size) < 0)
2253 return -2;
2255 offset += rest_size;
2256 c = c1;
2259 ftruncate(fd, size);
2260 close(fd);
2262 return commit_mappings(s, first_cluster, dir_index);
2265 #ifdef DEBUG
2266 /* test, if all mappings point to valid direntries */
2267 static void check1(BDRVVVFATState* s)
2269 int i;
2270 for (i = 0; i < s->mapping.next; i++) {
2271 mapping_t* mapping = array_get(&(s->mapping), i);
2272 if (mapping->mode & MODE_DELETED) {
2273 fprintf(stderr, "deleted\n");
2274 continue;
2276 assert(mapping->dir_index >= 0);
2277 assert(mapping->dir_index < s->directory.next);
2278 direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
2279 assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
2280 if (mapping->mode & MODE_DIRECTORY) {
2281 assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
2282 assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
2287 /* test, if all direntries have mappings */
2288 static void check2(BDRVVVFATState* s)
2290 int i;
2291 int first_mapping = -1;
2293 for (i = 0; i < s->directory.next; i++) {
2294 direntry_t* direntry = array_get(&(s->directory), i);
2296 if (is_short_name(direntry) && begin_of_direntry(direntry)) {
2297 mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
2298 assert(mapping);
2299 assert(mapping->dir_index == i || is_dot(direntry));
2300 assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
2303 if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
2304 /* cluster start */
2305 int j, count = 0;
2307 for (j = 0; j < s->mapping.next; j++) {
2308 mapping_t* mapping = array_get(&(s->mapping), j);
2309 if (mapping->mode & MODE_DELETED)
2310 continue;
2311 if (mapping->mode & MODE_DIRECTORY) {
2312 if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
2313 assert(++count == 1);
2314 if (mapping->first_mapping_index == -1)
2315 first_mapping = array_index(&(s->mapping), mapping);
2316 else
2317 assert(first_mapping == mapping->first_mapping_index);
2318 if (mapping->info.dir.parent_mapping_index < 0)
2319 assert(j == 0);
2320 else {
2321 mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
2322 assert(parent->mode & MODE_DIRECTORY);
2323 assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
2328 if (count == 0)
2329 first_mapping = -1;
2333 #endif
2335 static int handle_renames_and_mkdirs(BDRVVVFATState* s)
2337 int i;
2339 #ifdef DEBUG
2340 fprintf(stderr, "handle_renames\n");
2341 for (i = 0; i < s->commits.next; i++) {
2342 commit_t* commit = array_get(&(s->commits), i);
2343 fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
2345 #endif
2347 for (i = 0; i < s->commits.next;) {
2348 commit_t* commit = array_get(&(s->commits), i);
2349 if (commit->action == ACTION_RENAME) {
2350 mapping_t* mapping = find_mapping_for_cluster(s,
2351 commit->param.rename.cluster);
2352 char* old_path = mapping->path;
2354 assert(commit->path);
2355 mapping->path = commit->path;
2356 if (rename(old_path, mapping->path))
2357 return -2;
2359 if (mapping->mode & MODE_DIRECTORY) {
2360 int l1 = strlen(mapping->path);
2361 int l2 = strlen(old_path);
2362 int diff = l1 - l2;
2363 direntry_t* direntry = array_get(&(s->directory),
2364 mapping->info.dir.first_dir_index);
2365 uint32_t c = mapping->begin;
2366 int i = 0;
2368 /* recurse */
2369 while (!fat_eof(s, c)) {
2370 do {
2371 direntry_t* d = direntry + i;
2373 if (is_file(d) || (is_directory(d) && !is_dot(d))) {
2374 mapping_t* m = find_mapping_for_cluster(s,
2375 begin_of_direntry(d));
2376 int l = strlen(m->path);
2377 char* new_path = qemu_malloc(l + diff + 1);
2379 assert(!strncmp(m->path, mapping->path, l2));
2381 pstrcpy(new_path, l + diff + 1, mapping->path);
2382 pstrcpy(new_path + l1, l + diff + 1 - l1,
2383 m->path + l2);
2385 schedule_rename(s, m->begin, new_path);
2387 i++;
2388 } while((i % (0x10 * s->sectors_per_cluster)) != 0);
2389 c = fat_get(s, c);
2393 free(old_path);
2394 array_remove(&(s->commits), i);
2395 continue;
2396 } else if (commit->action == ACTION_MKDIR) {
2397 mapping_t* mapping;
2398 int j, parent_path_len;
2400 #ifdef __MINGW32__
2401 if (mkdir(commit->path))
2402 return -5;
2403 #else
2404 if (mkdir(commit->path, 0755))
2405 return -5;
2406 #endif
2408 mapping = insert_mapping(s, commit->param.mkdir.cluster,
2409 commit->param.mkdir.cluster + 1);
2410 if (mapping == NULL)
2411 return -6;
2413 mapping->mode = MODE_DIRECTORY;
2414 mapping->read_only = 0;
2415 mapping->path = commit->path;
2416 j = s->directory.next;
2417 assert(j);
2418 insert_direntries(s, s->directory.next,
2419 0x10 * s->sectors_per_cluster);
2420 mapping->info.dir.first_dir_index = j;
2422 parent_path_len = strlen(commit->path)
2423 - strlen(get_basename(commit->path)) - 1;
2424 for (j = 0; j < s->mapping.next; j++) {
2425 mapping_t* m = array_get(&(s->mapping), j);
2426 if (m->first_mapping_index < 0 && m != mapping &&
2427 !strncmp(m->path, mapping->path, parent_path_len) &&
2428 strlen(m->path) == parent_path_len)
2429 break;
2431 assert(j < s->mapping.next);
2432 mapping->info.dir.parent_mapping_index = j;
2434 array_remove(&(s->commits), i);
2435 continue;
2438 i++;
2440 return 0;
2444 * TODO: make sure that the short name is not matching *another* file
2446 static int handle_commits(BDRVVVFATState* s)
2448 int i, fail = 0;
2450 vvfat_close_current_file(s);
2452 for (i = 0; !fail && i < s->commits.next; i++) {
2453 commit_t* commit = array_get(&(s->commits), i);
2454 switch(commit->action) {
2455 case ACTION_RENAME: case ACTION_MKDIR:
2456 assert(0);
2457 fail = -2;
2458 break;
2459 case ACTION_WRITEOUT: {
2460 direntry_t* entry = array_get(&(s->directory),
2461 commit->param.writeout.dir_index);
2462 uint32_t begin = begin_of_direntry(entry);
2463 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2465 assert(mapping);
2466 assert(mapping->begin == begin);
2467 assert(commit->path == NULL);
2469 if (commit_one_file(s, commit->param.writeout.dir_index,
2470 commit->param.writeout.modified_offset))
2471 fail = -3;
2473 break;
2475 case ACTION_NEW_FILE: {
2476 int begin = commit->param.new_file.first_cluster;
2477 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2478 direntry_t* entry;
2479 int i;
2481 /* find direntry */
2482 for (i = 0; i < s->directory.next; i++) {
2483 entry = array_get(&(s->directory), i);
2484 if (is_file(entry) && begin_of_direntry(entry) == begin)
2485 break;
2488 if (i >= s->directory.next) {
2489 fail = -6;
2490 continue;
2493 /* make sure there exists an initial mapping */
2494 if (mapping && mapping->begin != begin) {
2495 mapping->end = begin;
2496 mapping = NULL;
2498 if (mapping == NULL) {
2499 mapping = insert_mapping(s, begin, begin+1);
2501 /* most members will be fixed in commit_mappings() */
2502 assert(commit->path);
2503 mapping->path = commit->path;
2504 mapping->read_only = 0;
2505 mapping->mode = MODE_NORMAL;
2506 mapping->info.file.offset = 0;
2508 if (commit_one_file(s, i, 0))
2509 fail = -7;
2511 break;
2513 default:
2514 assert(0);
2517 if (i > 0 && array_remove_slice(&(s->commits), 0, i))
2518 return -1;
2519 return fail;
2522 static int handle_deletes(BDRVVVFATState* s)
2524 int i, deferred = 1, deleted = 1;
2526 /* delete files corresponding to mappings marked as deleted */
2527 /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
2528 while (deferred && deleted) {
2529 deferred = 0;
2530 deleted = 0;
2532 for (i = 1; i < s->mapping.next; i++) {
2533 mapping_t* mapping = array_get(&(s->mapping), i);
2534 if (mapping->mode & MODE_DELETED) {
2535 direntry_t* entry = array_get(&(s->directory),
2536 mapping->dir_index);
2538 if (is_free(entry)) {
2539 /* remove file/directory */
2540 if (mapping->mode & MODE_DIRECTORY) {
2541 int j, next_dir_index = s->directory.next,
2542 first_dir_index = mapping->info.dir.first_dir_index;
2544 if (rmdir(mapping->path) < 0) {
2545 if (errno == ENOTEMPTY) {
2546 deferred++;
2547 continue;
2548 } else
2549 return -5;
2552 for (j = 1; j < s->mapping.next; j++) {
2553 mapping_t* m = array_get(&(s->mapping), j);
2554 if (m->mode & MODE_DIRECTORY &&
2555 m->info.dir.first_dir_index >
2556 first_dir_index &&
2557 m->info.dir.first_dir_index <
2558 next_dir_index)
2559 next_dir_index =
2560 m->info.dir.first_dir_index;
2562 remove_direntries(s, first_dir_index,
2563 next_dir_index - first_dir_index);
2565 deleted++;
2567 } else {
2568 if (unlink(mapping->path))
2569 return -4;
2570 deleted++;
2572 DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
2573 remove_mapping(s, i);
2578 return 0;
2582 * synchronize mapping with new state:
2584 * - copy FAT (with bdrv_read)
2585 * - mark all filenames corresponding to mappings as deleted
2586 * - recurse direntries from root (using bs->bdrv_read)
2587 * - delete files corresponding to mappings marked as deleted
2589 static int do_commit(BDRVVVFATState* s)
2591 int ret = 0;
2593 /* the real meat are the commits. Nothing to do? Move along! */
2594 if (s->commits.next == 0)
2595 return 0;
2597 vvfat_close_current_file(s);
2599 ret = handle_renames_and_mkdirs(s);
2600 if (ret) {
2601 fprintf(stderr, "Error handling renames (%d)\n", ret);
2602 assert(0);
2603 return ret;
2606 /* copy FAT (with bdrv_read) */
2607 memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
2609 /* recurse direntries from root (using bs->bdrv_read) */
2610 ret = commit_direntries(s, 0, -1);
2611 if (ret) {
2612 fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
2613 assert(0);
2614 return ret;
2617 ret = handle_commits(s);
2618 if (ret) {
2619 fprintf(stderr, "Error handling commits (%d)\n", ret);
2620 assert(0);
2621 return ret;
2624 ret = handle_deletes(s);
2625 if (ret) {
2626 fprintf(stderr, "Error deleting\n");
2627 assert(0);
2628 return ret;
2631 s->qcow->drv->bdrv_make_empty(s->qcow);
2633 memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
2635 DLOG(checkpoint());
2636 return 0;
2639 static int try_commit(BDRVVVFATState* s)
2641 vvfat_close_current_file(s);
2642 DLOG(checkpoint());
2643 if(!is_consistent(s))
2644 return -1;
2645 return do_commit(s);
2648 static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
2649 const uint8_t *buf, int nb_sectors)
2651 BDRVVVFATState *s = bs->opaque;
2652 int i, ret;
2654 DLOG(checkpoint());
2656 vvfat_close_current_file(s);
2659 * Some sanity checks:
2660 * - do not allow writing to the boot sector
2661 * - do not allow to write non-ASCII filenames
2664 if (sector_num < s->first_sectors_number)
2665 return -1;
2667 for (i = sector2cluster(s, sector_num);
2668 i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
2669 mapping_t* mapping = find_mapping_for_cluster(s, i);
2670 if (mapping) {
2671 if (mapping->read_only) {
2672 fprintf(stderr, "Tried to write to write-protected file %s\n",
2673 mapping->path);
2674 return -1;
2677 if (mapping->mode & MODE_DIRECTORY) {
2678 int begin = cluster2sector(s, i);
2679 int end = begin + s->sectors_per_cluster, k;
2680 int dir_index;
2681 const direntry_t* direntries;
2682 long_file_name lfn;
2684 lfn_init(&lfn);
2686 if (begin < sector_num)
2687 begin = sector_num;
2688 if (end > sector_num + nb_sectors)
2689 end = sector_num + nb_sectors;
2690 dir_index = mapping->dir_index +
2691 0x10 * (begin - mapping->begin * s->sectors_per_cluster);
2692 direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
2694 for (k = 0; k < (end - begin) * 0x10; k++) {
2695 /* do not allow non-ASCII filenames */
2696 if (parse_long_name(&lfn, direntries + k) < 0) {
2697 fprintf(stderr, "Warning: non-ASCII filename\n");
2698 return -1;
2700 /* no access to the direntry of a read-only file */
2701 else if (is_short_name(direntries+k) &&
2702 (direntries[k].attributes & 1)) {
2703 if (memcmp(direntries + k,
2704 array_get(&(s->directory), dir_index + k),
2705 sizeof(direntry_t))) {
2706 fprintf(stderr, "Warning: tried to write to write-protected file\n");
2707 return -1;
2712 i = mapping->end;
2713 } else
2714 i++;
2718 * Use qcow backend. Commit later.
2720 DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
2721 ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
2722 if (ret < 0) {
2723 fprintf(stderr, "Error writing to qcow backend\n");
2724 return ret;
2727 for (i = sector2cluster(s, sector_num);
2728 i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
2729 if (i >= 0)
2730 s->used_clusters[i] |= USED_ALLOCATED;
2732 DLOG(checkpoint());
2733 /* TODO: add timeout */
2734 try_commit(s);
2736 DLOG(checkpoint());
2737 return 0;
2740 static int vvfat_is_allocated(BlockDriverState *bs,
2741 int64_t sector_num, int nb_sectors, int* n)
2743 BDRVVVFATState* s = bs->opaque;
2744 *n = s->sector_count - sector_num;
2745 if (*n > nb_sectors)
2746 *n = nb_sectors;
2747 else if (*n < 0)
2748 return 0;
2749 return 1;
2752 static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
2753 const uint8_t* buffer, int nb_sectors) {
2754 BDRVVVFATState* s = bs->opaque;
2755 return try_commit(s);
2758 static void write_target_close(BlockDriverState *bs) {
2759 BDRVVVFATState* s = bs->opaque;
2760 bdrv_delete(s->qcow);
2761 free(s->qcow_filename);
2764 static BlockDriver vvfat_write_target = {
2765 .format_name = "vvfat_write_target",
2766 .bdrv_write = write_target_commit,
2767 .bdrv_close = write_target_close,
2770 static int enable_write_target(BDRVVVFATState *s)
2772 BlockDriver *bdrv_qcow;
2773 QEMUOptionParameter *options;
2774 int size = sector2cluster(s, s->sector_count);
2775 s->used_clusters = calloc(size, 1);
2777 array_init(&(s->commits), sizeof(commit_t));
2779 s->qcow_filename = qemu_malloc(1024);
2780 get_tmp_filename(s->qcow_filename, 1024);
2782 bdrv_qcow = bdrv_find_format("qcow");
2783 options = parse_option_parameters("", bdrv_qcow->create_options, NULL);
2784 set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
2785 set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
2787 if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0)
2788 return -1;
2789 s->qcow = bdrv_new("");
2790 if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0)
2791 return -1;
2793 #ifndef _WIN32
2794 unlink(s->qcow_filename);
2795 #endif
2797 s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
2798 s->bs->backing_hd->drv = &vvfat_write_target;
2799 s->bs->backing_hd->opaque = s;
2801 return 0;
2804 static void vvfat_close(BlockDriverState *bs)
2806 BDRVVVFATState *s = bs->opaque;
2808 vvfat_close_current_file(s);
2809 array_free(&(s->fat));
2810 array_free(&(s->directory));
2811 array_free(&(s->mapping));
2812 if(s->cluster_buffer)
2813 free(s->cluster_buffer);
2816 static BlockDriver bdrv_vvfat = {
2817 .format_name = "vvfat",
2818 .instance_size = sizeof(BDRVVVFATState),
2819 .bdrv_open = vvfat_open,
2820 .bdrv_read = vvfat_read,
2821 .bdrv_write = vvfat_write,
2822 .bdrv_close = vvfat_close,
2823 .bdrv_is_allocated = vvfat_is_allocated,
2824 .protocol_name = "fat",
2827 static void bdrv_vvfat_init(void)
2829 bdrv_register(&bdrv_vvfat);
2832 block_init(bdrv_vvfat_init);
2834 #ifdef DEBUG
2835 static void checkpoint(void) {
2836 assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
2837 check1(vvv);
2838 check2(vvv);
2839 assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
2840 #if 0
2841 if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
2842 fprintf(stderr, "Nonono!\n");
2843 mapping_t* mapping;
2844 direntry_t* direntry;
2845 assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
2846 assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
2847 if (vvv->mapping.next<47)
2848 return;
2849 assert((mapping = array_get(&(vvv->mapping), 47)));
2850 assert(mapping->dir_index < vvv->directory.next);
2851 direntry = array_get(&(vvv->directory), mapping->dir_index);
2852 assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0);
2853 #endif
2854 return;
2855 /* avoid compiler warnings: */
2856 hexdump(NULL, 100);
2857 remove_mapping(vvv, NULL);
2858 print_mapping(NULL);
2859 print_direntry(NULL);
2861 #endif