1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "qemu/osdep.h"
26#include <dirent.h>
27#include "qapi/error.h"
28#include "block/block_int.h"
29#include "qemu/module.h"
30#include "migration/migration.h"
31#include "qapi/qmp/qint.h"
32#include "qapi/qmp/qbool.h"
33#include "qapi/qmp/qstring.h"
34#include "qemu/cutils.h"
35
36#ifndef S_IWGRP
37#define S_IWGRP 0
38#endif
39#ifndef S_IWOTH
40#define S_IWOTH 0
41#endif
42
43
44
45
46
47
48
49
50
51
52
53#ifdef DEBUG
54
55#define DLOG(a) a
56
57static void checkpoint(void);
58
59#ifdef __MINGW32__
60void nonono(const char* file, int line, const char* msg) {
61 fprintf(stderr, "Nonono! %s:%d %s\n", file, line, msg);
62 exit(-5);
63}
64#undef assert
65#define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0)
66#endif
67
68#else
69
70#define DLOG(a)
71
72#endif
73
74
75typedef struct array_t {
76 char* pointer;
77 unsigned int size,next,item_size;
78} array_t;
79
80static inline void array_init(array_t* array,unsigned int item_size)
81{
82 array->pointer = NULL;
83 array->size=0;
84 array->next=0;
85 array->item_size=item_size;
86}
87
88static inline void array_free(array_t* array)
89{
90 g_free(array->pointer);
91 array->size=array->next=0;
92}
93
94
95static inline void* array_get(array_t* array,unsigned int index) {
96 assert(index < array->next);
97 return array->pointer + index * array->item_size;
98}
99
100static inline int array_ensure_allocated(array_t* array, int index)
101{
102 if((index + 1) * array->item_size > array->size) {
103 int new_size = (index + 32) * array->item_size;
104 array->pointer = g_realloc(array->pointer, new_size);
105 if (!array->pointer)
106 return -1;
107 array->size = new_size;
108 array->next = index + 1;
109 }
110
111 return 0;
112}
113
114static inline void* array_get_next(array_t* array) {
115 unsigned int next = array->next;
116 void* result;
117
118 if (array_ensure_allocated(array, next) < 0)
119 return NULL;
120
121 array->next = next + 1;
122 result = array_get(array, next);
123
124 return result;
125}
126
127static 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=g_realloc(array->pointer,array->size+increment);
131 if(!array->pointer)
132 return NULL;
133 array->size+=increment;
134 }
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;
140}
141
142
143
144static inline int array_roll(array_t* array,int index_to,int index_from,int count)
145{
146 char* buf;
147 char* from;
148 char* to;
149 int is;
150
151 if(!array ||
152 index_to<0 || index_to>=array->next ||
153 index_from<0 || index_from>=array->next)
154 return -1;
155
156 if(index_to==index_from)
157 return 0;
158
159 is=array->item_size;
160 from=array->pointer+index_from*is;
161 to=array->pointer+index_to*is;
162 buf=g_malloc(is*count);
163 memcpy(buf,from,is*count);
164
165 if(index_to<index_from)
166 memmove(to+is*count,to,from-to);
167 else
168 memmove(from,from+is*count,to-from);
169
170 memcpy(to,buf,is*count);
171
172 g_free(buf);
173
174 return 0;
175}
176
177static inline int array_remove_slice(array_t* array,int index, int count)
178{
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;
186}
187
188static int array_remove(array_t* array,int index)
189{
190 return array_remove_slice(array, index, 1);
191}
192
193
194static int array_index(array_t* array, void* pointer)
195{
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;
200}
201
202
203
204
205typedef 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 } QEMU_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 } QEMU_PACKED fat32;
237 } u;
238 uint8_t fat_type[8];
239 uint8_t ignored[0x1c0];
240 uint8_t magic[2];
241} QEMU_PACKED bootsector_t;
242
243typedef struct {
244 uint8_t head;
245 uint8_t sector;
246 uint8_t cylinder;
247} mbr_chs_t;
248
249typedef struct partition_t {
250 uint8_t attributes;
251 mbr_chs_t start_CHS;
252 uint8_t fs_type;
253 mbr_chs_t end_CHS;
254 uint32_t start_sector_long;
255 uint32_t length_sector_long;
256} QEMU_PACKED partition_t;
257
258typedef 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} QEMU_PACKED mbr_t;
265
266typedef struct direntry_t {
267 uint8_t name[8 + 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;
279
280
281
282typedef struct mapping_t {
283
284 uint32_t begin,end;
285
286 unsigned int dir_index;
287
288 int first_mapping_index;
289 union {
290
291
292
293
294
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
304 char* path;
305
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;
311
312#ifdef DEBUG
313static void print_direntry(const struct direntry_t*);
314static void print_mapping(const struct mapping_t* mapping);
315#endif
316
317
318
319typedef struct BDRVVVFATState {
320 CoMutex lock;
321 BlockDriverState* bs;
322 unsigned int first_sectors_number;
323 unsigned char first_sectors[0x40*0x200];
324
325 int fat_type;
326 array_t fat,directory,mapping;
327 char volume_label[11];
328
329 unsigned int cluster_size;
330 unsigned int sectors_per_cluster;
331 unsigned int sectors_per_fat;
332 unsigned int sectors_of_root_directory;
333 uint32_t last_cluster_of_root_directory;
334 unsigned int faked_sectors;
335 uint32_t sector_count;
336 uint32_t cluster_count;
337 uint32_t max_fat_value;
338
339 int current_fd;
340 mapping_t* current_mapping;
341 unsigned char* cluster;
342 unsigned char* cluster_buffer;
343 unsigned int current_cluster;
344
345
346 BlockDriverState* write_target;
347 char* qcow_filename;
348 BlockDriverState* qcow;
349 void* fat2;
350 char* used_clusters;
351 array_t commits;
352 const char* path;
353 int downcase_short_names;
354
355 Error *migration_blocker;
356} BDRVVVFATState;
357
358
359
360
361
362static int sector2CHS(mbr_chs_t *chs, int spos, int cyls, int heads, int secs)
363{
364 int head,sector;
365 sector = spos % secs; spos /= secs;
366 head = spos % heads; spos /= heads;
367 if (spos >= cyls) {
368
369
370
371 chs->head = 0xFF;
372 chs->sector = 0xFF;
373 chs->cylinder = 0xFF;
374 return 1;
375 }
376 chs->head = (uint8_t)head;
377 chs->sector = (uint8_t)( (sector+1) | ((spos>>8)<<6) );
378 chs->cylinder = (uint8_t)spos;
379 return 0;
380}
381
382static void init_mbr(BDRVVVFATState *s, int cyls, int heads, int secs)
383{
384
385 mbr_t* real_mbr=(mbr_t*)s->first_sectors;
386 partition_t* partition = &(real_mbr->partition[0]);
387 int lba;
388
389 memset(s->first_sectors,0,512);
390
391
392 real_mbr->nt_id= cpu_to_le32(0xbe1afdfa);
393
394 partition->attributes=0x80;
395
396
397 lba = sector2CHS(&partition->start_CHS, s->first_sectors_number - 1,
398 cyls, heads, secs);
399 lba |= sector2CHS(&partition->end_CHS, s->bs->total_sectors - 1,
400 cyls, heads, secs);
401
402
403 partition->start_sector_long = cpu_to_le32(s->first_sectors_number - 1);
404 partition->length_sector_long = cpu_to_le32(s->bs->total_sectors
405 - s->first_sectors_number + 1);
406
407
408
409
410 partition->fs_type= s->fat_type==12 ? 0x1:
411 s->fat_type==16 ? (lba?0xe:0x06):
412 (lba?0xc:0x0b);
413
414 real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
415}
416
417
418
419
420static inline int short2long_name(char* dest,const char* src)
421{
422 int i;
423 int len;
424 for(i=0;i<129 && src[i];i++) {
425 dest[2*i]=src[i];
426 dest[2*i+1]=0;
427 }
428 len=2*i;
429 dest[2*i]=dest[2*i+1]=0;
430 for(i=2*i+2;(i%26);i++)
431 dest[i]=0xff;
432 return len;
433}
434
435static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
436{
437 char buffer[258];
438 int length=short2long_name(buffer,filename),
439 number_of_entries=(length+25)/26,i;
440 direntry_t* entry;
441
442 for(i=0;i<number_of_entries;i++) {
443 entry=array_get_next(&(s->directory));
444 entry->attributes=0xf;
445 entry->reserved[0]=0;
446 entry->begin=0;
447 entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
448 }
449 for(i=0;i<26*number_of_entries;i++) {
450 int offset=(i%26);
451 if(offset<10) offset=1+offset;
452 else if(offset<22) offset=14+offset-10;
453 else offset=28+offset-22;
454 entry=array_get(&(s->directory),s->directory.next-1-(i/26));
455 entry->name[offset]=buffer[i];
456 }
457 return array_get(&(s->directory),s->directory.next-number_of_entries);
458}
459
460static char is_free(const direntry_t* direntry)
461{
462 return direntry->name[0]==0xe5 || direntry->name[0]==0x00;
463}
464
465static char is_volume_label(const direntry_t* direntry)
466{
467 return direntry->attributes == 0x28;
468}
469
470static char is_long_name(const direntry_t* direntry)
471{
472 return direntry->attributes == 0xf;
473}
474
475static char is_short_name(const direntry_t* direntry)
476{
477 return !is_volume_label(direntry) && !is_long_name(direntry)
478 && !is_free(direntry);
479}
480
481static char is_directory(const direntry_t* direntry)
482{
483 return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
484}
485
486static inline char is_dot(const direntry_t* direntry)
487{
488 return is_short_name(direntry) && direntry->name[0] == '.';
489}
490
491static char is_file(const direntry_t* direntry)
492{
493 return is_short_name(direntry) && !is_directory(direntry);
494}
495
496static inline uint32_t begin_of_direntry(const direntry_t* direntry)
497{
498 return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
499}
500
501static inline uint32_t filesize_of_direntry(const direntry_t* direntry)
502{
503 return le32_to_cpu(direntry->size);
504}
505
506static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
507{
508 direntry->begin = cpu_to_le16(begin & 0xffff);
509 direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
510}
511
512
513
514static inline uint8_t fat_chksum(const direntry_t* entry)
515{
516 uint8_t chksum=0;
517 int i;
518
519 for (i = 0; i < ARRAY_SIZE(entry->name); i++) {
520 chksum = (((chksum & 0xfe) >> 1) |
521 ((chksum & 0x01) ? 0x80 : 0)) + entry->name[i];
522 }
523
524 return chksum;
525}
526
527
528static uint16_t fat_datetime(time_t time,int return_time) {
529 struct tm* t;
530 struct tm t1;
531 t = &t1;
532 localtime_r(&time,t);
533 if(return_time)
534 return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
535 return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
536}
537
538static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
539{
540 if(s->fat_type==32) {
541 uint32_t* entry=array_get(&(s->fat),cluster);
542 *entry=cpu_to_le32(value);
543 } else if(s->fat_type==16) {
544 uint16_t* entry=array_get(&(s->fat),cluster);
545 *entry=cpu_to_le16(value&0xffff);
546 } else {
547 int offset = (cluster*3/2);
548 unsigned char* p = array_get(&(s->fat), offset);
549 switch (cluster&1) {
550 case 0:
551 p[0] = value&0xff;
552 p[1] = (p[1]&0xf0) | ((value>>8)&0xf);
553 break;
554 case 1:
555 p[0] = (p[0]&0xf) | ((value&0xf)<<4);
556 p[1] = (value>>4);
557 break;
558 }
559 }
560}
561
562static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
563{
564 if(s->fat_type==32) {
565 uint32_t* entry=array_get(&(s->fat),cluster);
566 return le32_to_cpu(*entry);
567 } else if(s->fat_type==16) {
568 uint16_t* entry=array_get(&(s->fat),cluster);
569 return le16_to_cpu(*entry);
570 } else {
571 const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2;
572 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
573 }
574}
575
576static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
577{
578 if(fat_entry>s->max_fat_value-8)
579 return -1;
580 return 0;
581}
582
583static inline void init_fat(BDRVVVFATState* s)
584{
585 if (s->fat_type == 12) {
586 array_init(&(s->fat),1);
587 array_ensure_allocated(&(s->fat),
588 s->sectors_per_fat * 0x200 * 3 / 2 - 1);
589 } else {
590 array_init(&(s->fat),(s->fat_type==32?4:2));
591 array_ensure_allocated(&(s->fat),
592 s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
593 }
594 memset(s->fat.pointer,0,s->fat.size);
595
596 switch(s->fat_type) {
597 case 12: s->max_fat_value=0xfff; break;
598 case 16: s->max_fat_value=0xffff; break;
599 case 32: s->max_fat_value=0x0fffffff; break;
600 default: s->max_fat_value=0;
601 }
602
603}
604
605
606
607static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
608 unsigned int directory_start, const char* filename, int is_dot)
609{
610 int i,j,long_index=s->directory.next;
611 direntry_t* entry = NULL;
612 direntry_t* entry_long = NULL;
613
614 if(is_dot) {
615 entry=array_get_next(&(s->directory));
616 memset(entry->name, 0x20, sizeof(entry->name));
617 memcpy(entry->name,filename,strlen(filename));
618 return entry;
619 }
620
621 entry_long=create_long_filename(s,filename);
622
623 i = strlen(filename);
624 for(j = i - 1; j>0 && filename[j]!='.';j--);
625 if (j > 0)
626 i = (j > 8 ? 8 : j);
627 else if (i > 8)
628 i = 8;
629
630 entry=array_get_next(&(s->directory));
631 memset(entry->name, 0x20, sizeof(entry->name));
632 memcpy(entry->name, filename, i);
633
634 if (j > 0) {
635 for (i = 0; i < 3 && filename[j + 1 + i]; i++) {
636 entry->name[8 + i] = filename[j + 1 + i];
637 }
638 }
639
640
641 for(i=10;i>=0;i--) {
642 if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--);
643 if(entry->name[i]<=' ' || entry->name[i]>0x7f
644 || strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
645 entry->name[i]='_';
646 else if(entry->name[i]>='a' && entry->name[i]<='z')
647 entry->name[i]+='A'-'a';
648 }
649
650
651 while(1) {
652 direntry_t* entry1=array_get(&(s->directory),directory_start);
653 int j;
654
655 for(;entry1<entry;entry1++)
656 if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11))
657 break;
658 if(entry1==entry)
659 break;
660
661
662 if(entry->name[7]==' ') {
663 int j;
664 for(j=6;j>0 && entry->name[j]==' ';j--)
665 entry->name[j]='~';
666 }
667
668
669 for(j=7;j>0 && entry->name[j]=='9';j--)
670 entry->name[j]='0';
671 if(j>0) {
672 if(entry->name[j]<'0' || entry->name[j]>'9')
673 entry->name[j]='0';
674 else
675 entry->name[j]++;
676 }
677 }
678
679
680 if(entry_long) {
681 uint8_t chksum=fat_chksum(entry);
682
683
684 entry_long=array_get(&(s->directory),long_index);
685 while(entry_long<entry && is_long_name(entry_long)) {
686 entry_long->reserved[1]=chksum;
687 entry_long++;
688 }
689 }
690
691 return entry;
692}
693
694
695
696
697static int read_directory(BDRVVVFATState* s, int mapping_index)
698{
699 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
700 direntry_t* direntry;
701 const char* dirname = mapping->path;
702 int first_cluster = mapping->begin;
703 int parent_index = mapping->info.dir.parent_mapping_index;
704 mapping_t* parent_mapping = (mapping_t*)
705 (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : NULL);
706 int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
707
708 DIR* dir=opendir(dirname);
709 struct dirent* entry;
710 int i;
711
712 assert(mapping->mode & MODE_DIRECTORY);
713
714 if(!dir) {
715 mapping->end = mapping->begin;
716 return -1;
717 }
718
719 i = mapping->info.dir.first_dir_index =
720 first_cluster == 0 ? 0 : s->directory.next;
721
722
723 while((entry=readdir(dir))) {
724 unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
725 char* buffer;
726 direntry_t* direntry;
727 struct stat st;
728 int is_dot=!strcmp(entry->d_name,".");
729 int is_dotdot=!strcmp(entry->d_name,"..");
730
731 if(first_cluster == 0 && (is_dotdot || is_dot))
732 continue;
733
734 buffer = g_malloc(length);
735 snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
736
737 if(stat(buffer,&st)<0) {
738 g_free(buffer);
739 continue;
740 }
741
742
743 direntry=create_short_and_long_name(s, i, entry->d_name,
744 is_dot || is_dotdot);
745 direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
746 direntry->reserved[0]=direntry->reserved[1]=0;
747 direntry->ctime=fat_datetime(st.st_ctime,1);
748 direntry->cdate=fat_datetime(st.st_ctime,0);
749 direntry->adate=fat_datetime(st.st_atime,0);
750 direntry->begin_hi=0;
751 direntry->mtime=fat_datetime(st.st_mtime,1);
752 direntry->mdate=fat_datetime(st.st_mtime,0);
753 if(is_dotdot)
754 set_begin_of_direntry(direntry, first_cluster_of_parent);
755 else if(is_dot)
756 set_begin_of_direntry(direntry, first_cluster);
757 else
758 direntry->begin=0;
759 if (st.st_size > 0x7fffffff) {
760 fprintf(stderr, "File %s is larger than 2GB\n", buffer);
761 g_free(buffer);
762 closedir(dir);
763 return -2;
764 }
765 direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
766
767
768 if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
769 s->current_mapping = array_get_next(&(s->mapping));
770 s->current_mapping->begin=0;
771 s->current_mapping->end=st.st_size;
772
773
774
775
776 s->current_mapping->dir_index=s->directory.next-1;
777 s->current_mapping->first_mapping_index = -1;
778 if (S_ISDIR(st.st_mode)) {
779 s->current_mapping->mode = MODE_DIRECTORY;
780 s->current_mapping->info.dir.parent_mapping_index =
781 mapping_index;
782 } else {
783 s->current_mapping->mode = MODE_UNDEFINED;
784 s->current_mapping->info.file.offset = 0;
785 }
786 s->current_mapping->path=buffer;
787 s->current_mapping->read_only =
788 (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
789 } else {
790 g_free(buffer);
791 }
792 }
793 closedir(dir);
794
795
796 while(s->directory.next%(0x10*s->sectors_per_cluster)) {
797 direntry_t* direntry=array_get_next(&(s->directory));
798 memset(direntry,0,sizeof(direntry_t));
799 }
800
801
802#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster)
803 if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) {
804
805 int cur = s->directory.next;
806 array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
807 s->directory.next = ROOT_ENTRIES;
808 memset(array_get(&(s->directory), cur), 0,
809 (ROOT_ENTRIES - cur) * sizeof(direntry_t));
810 }
811
812
813 mapping = array_get(&(s->mapping), mapping_index);
814 first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
815 * 0x20 / s->cluster_size;
816 mapping->end = first_cluster;
817
818 direntry = array_get(&(s->directory), mapping->dir_index);
819 set_begin_of_direntry(direntry, mapping->begin);
820
821 return 0;
822}
823
824static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
825{
826 return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
827}
828
829static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
830{
831 return s->faked_sectors + s->sectors_per_cluster * cluster_num;
832}
833
834static int init_directories(BDRVVVFATState* s,
835 const char *dirname, int heads, int secs,
836 Error **errp)
837{
838 bootsector_t* bootsector;
839 mapping_t* mapping;
840 unsigned int i;
841 unsigned int cluster;
842
843 memset(&(s->first_sectors[0]),0,0x40*0x200);
844
845 s->cluster_size=s->sectors_per_cluster*0x200;
846 s->cluster_buffer=g_malloc(s->cluster_size);
847
848
849
850
851
852
853
854
855 i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
856 s->sectors_per_fat=(s->sector_count+i)/i;
857
858 array_init(&(s->mapping),sizeof(mapping_t));
859 array_init(&(s->directory),sizeof(direntry_t));
860
861
862 {
863 direntry_t* entry=array_get_next(&(s->directory));
864 entry->attributes=0x28;
865 memcpy(entry->name, s->volume_label, sizeof(entry->name));
866 }
867
868
869 init_fat(s);
870
871 s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
872 s->cluster_count=sector2cluster(s, s->sector_count);
873
874 mapping = array_get_next(&(s->mapping));
875 mapping->begin = 0;
876 mapping->dir_index = 0;
877 mapping->info.dir.parent_mapping_index = -1;
878 mapping->first_mapping_index = -1;
879 mapping->path = g_strdup(dirname);
880 i = strlen(mapping->path);
881 if (i > 0 && mapping->path[i - 1] == '/')
882 mapping->path[i - 1] = '\0';
883 mapping->mode = MODE_DIRECTORY;
884 mapping->read_only = 0;
885 s->path = mapping->path;
886
887 for (i = 0, cluster = 0; i < s->mapping.next; i++) {
888
889
890
891 int fix_fat = (i != 0);
892 mapping = array_get(&(s->mapping), i);
893
894 if (mapping->mode & MODE_DIRECTORY) {
895 mapping->begin = cluster;
896 if(read_directory(s, i)) {
897 error_setg(errp, "Could not read directory %s",
898 mapping->path);
899 return -1;
900 }
901 mapping = array_get(&(s->mapping), i);
902 } else {
903 assert(mapping->mode == MODE_UNDEFINED);
904 mapping->mode=MODE_NORMAL;
905 mapping->begin = cluster;
906 if (mapping->end > 0) {
907 direntry_t* direntry = array_get(&(s->directory),
908 mapping->dir_index);
909
910 mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
911 set_begin_of_direntry(direntry, mapping->begin);
912 } else {
913 mapping->end = cluster + 1;
914 fix_fat = 0;
915 }
916 }
917
918 assert(mapping->begin < mapping->end);
919
920
921 cluster = mapping->end;
922
923 if(cluster > s->cluster_count) {
924 error_setg(errp,
925 "Directory does not fit in FAT%d (capacity %.2f MB)",
926 s->fat_type, s->sector_count / 2000.0);
927 return -1;
928 }
929
930
931 if (fix_fat) {
932 int j;
933 for(j = mapping->begin; j < mapping->end - 1; j++)
934 fat_set(s, j, j+1);
935 fat_set(s, mapping->end - 1, s->max_fat_value);
936 }
937 }
938
939 mapping = array_get(&(s->mapping), 0);
940 s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
941 s->last_cluster_of_root_directory = mapping->end;
942
943
944 fat_set(s,0,s->max_fat_value);
945 fat_set(s,1,s->max_fat_value);
946
947 s->current_mapping = NULL;
948
949 bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
950 bootsector->jump[0]=0xeb;
951 bootsector->jump[1]=0x3e;
952 bootsector->jump[2]=0x90;
953 memcpy(bootsector->name,"QEMU ",8);
954 bootsector->sector_size=cpu_to_le16(0x200);
955 bootsector->sectors_per_cluster=s->sectors_per_cluster;
956 bootsector->reserved_sectors=cpu_to_le16(1);
957 bootsector->number_of_fats=0x2;
958 bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
959 bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
960 bootsector->media_type=(s->first_sectors_number>1?0xf8:0xf0);
961 s->fat.pointer[0] = bootsector->media_type;
962 bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
963 bootsector->sectors_per_track = cpu_to_le16(secs);
964 bootsector->number_of_heads = cpu_to_le16(heads);
965 bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
966 bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
967
968
969 bootsector->u.fat16.drive_number=s->first_sectors_number==1?0:0x80;
970 bootsector->u.fat16.current_head=0;
971 bootsector->u.fat16.signature=0x29;
972 bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
973
974 memcpy(bootsector->u.fat16.volume_label, s->volume_label,
975 sizeof(bootsector->u.fat16.volume_label));
976 memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8);
977 bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
978
979 return 0;
980}
981
982#ifdef DEBUG
983static BDRVVVFATState *vvv = NULL;
984#endif
985
986static int enable_write_target(BDRVVVFATState *s, Error **errp);
987static int is_consistent(BDRVVVFATState *s);
988
989static QemuOptsList runtime_opts = {
990 .name = "vvfat",
991 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
992 .desc = {
993 {
994 .name = "dir",
995 .type = QEMU_OPT_STRING,
996 .help = "Host directory to map to the vvfat device",
997 },
998 {
999 .name = "fat-type",
1000 .type = QEMU_OPT_NUMBER,
1001 .help = "FAT type (12, 16 or 32)",
1002 },
1003 {
1004 .name = "floppy",
1005 .type = QEMU_OPT_BOOL,
1006 .help = "Create a floppy rather than a hard disk image",
1007 },
1008 {
1009 .name = "label",
1010 .type = QEMU_OPT_STRING,
1011 .help = "Use a volume label other than QEMU VVFAT",
1012 },
1013 {
1014 .name = "rw",
1015 .type = QEMU_OPT_BOOL,
1016 .help = "Make the image writable",
1017 },
1018 { }
1019 },
1020};
1021
1022static void vvfat_parse_filename(const char *filename, QDict *options,
1023 Error **errp)
1024{
1025 int fat_type = 0;
1026 bool floppy = false;
1027 bool rw = false;
1028 int i;
1029
1030 if (!strstart(filename, "fat:", NULL)) {
1031 error_setg(errp, "File name string must start with 'fat:'");
1032 return;
1033 }
1034
1035
1036 if (strstr(filename, ":32:")) {
1037 fat_type = 32;
1038 } else if (strstr(filename, ":16:")) {
1039 fat_type = 16;
1040 } else if (strstr(filename, ":12:")) {
1041 fat_type = 12;
1042 }
1043
1044 if (strstr(filename, ":floppy:")) {
1045 floppy = true;
1046 }
1047
1048 if (strstr(filename, ":rw:")) {
1049 rw = true;
1050 }
1051
1052
1053 i = strrchr(filename, ':') - filename;
1054 assert(i >= 3);
1055 if (filename[i - 2] == ':' && qemu_isalpha(filename[i - 1])) {
1056
1057 filename += i - 1;
1058 } else {
1059 filename += i + 1;
1060 }
1061
1062
1063 qdict_put(options, "dir", qstring_from_str(filename));
1064 qdict_put(options, "fat-type", qint_from_int(fat_type));
1065 qdict_put(options, "floppy", qbool_from_bool(floppy));
1066 qdict_put(options, "rw", qbool_from_bool(rw));
1067}
1068
1069static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
1070 Error **errp)
1071{
1072 BDRVVVFATState *s = bs->opaque;
1073 int cyls, heads, secs;
1074 bool floppy;
1075 const char *dirname, *label;
1076 QemuOpts *opts;
1077 Error *local_err = NULL;
1078 int ret;
1079
1080#ifdef DEBUG
1081 vvv = s;
1082#endif
1083
1084 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
1085 qemu_opts_absorb_qdict(opts, options, &local_err);
1086 if (local_err) {
1087 error_propagate(errp, local_err);
1088 ret = -EINVAL;
1089 goto fail;
1090 }
1091
1092 dirname = qemu_opt_get(opts, "dir");
1093 if (!dirname) {
1094 error_setg(errp, "vvfat block driver requires a 'dir' option");
1095 ret = -EINVAL;
1096 goto fail;
1097 }
1098
1099 s->fat_type = qemu_opt_get_number(opts, "fat-type", 0);
1100 floppy = qemu_opt_get_bool(opts, "floppy", false);
1101
1102 memset(s->volume_label, ' ', sizeof(s->volume_label));
1103 label = qemu_opt_get(opts, "label");
1104 if (label) {
1105 size_t label_length = strlen(label);
1106 if (label_length > 11) {
1107 error_setg(errp, "vvfat label cannot be longer than 11 bytes");
1108 ret = -EINVAL;
1109 goto fail;
1110 }
1111 memcpy(s->volume_label, label, label_length);
1112 }
1113
1114 if (floppy) {
1115
1116 if (!s->fat_type) {
1117 s->fat_type = 12;
1118 secs = 36;
1119 s->sectors_per_cluster = 2;
1120 } else {
1121 secs = s->fat_type == 12 ? 18 : 36;
1122 s->sectors_per_cluster = 1;
1123 }
1124 s->first_sectors_number = 1;
1125 cyls = 80;
1126 heads = 2;
1127 } else {
1128
1129 if (!s->fat_type) {
1130 s->fat_type = 16;
1131 }
1132 s->first_sectors_number = 0x40;
1133 cyls = s->fat_type == 12 ? 64 : 1024;
1134 heads = 16;
1135 secs = 63;
1136 }
1137
1138 switch (s->fat_type) {
1139 case 32:
1140 fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. "
1141 "You are welcome to do so!\n");
1142 break;
1143 case 16:
1144 case 12:
1145 break;
1146 default:
1147 error_setg(errp, "Valid FAT types are only 12, 16 and 32");
1148 ret = -EINVAL;
1149 goto fail;
1150 }
1151
1152
1153 s->bs = bs;
1154
1155
1156 s->sectors_per_cluster=0x10;
1157
1158 s->current_cluster=0xffffffff;
1159
1160
1161 bs->read_only = 1;
1162 s->qcow = s->write_target = NULL;
1163 s->qcow_filename = NULL;
1164 s->fat2 = NULL;
1165 s->downcase_short_names = 1;
1166
1167 fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
1168 dirname, cyls, heads, secs);
1169
1170 s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
1171
1172 if (qemu_opt_get_bool(opts, "rw", false)) {
1173 ret = enable_write_target(s, errp);
1174 if (ret < 0) {
1175 goto fail;
1176 }
1177 bs->read_only = 0;
1178 }
1179
1180 bs->total_sectors = cyls * heads * secs;
1181
1182 if (init_directories(s, dirname, heads, secs, errp)) {
1183 ret = -EIO;
1184 goto fail;
1185 }
1186
1187 s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
1188
1189 if (s->first_sectors_number == 0x40) {
1190 init_mbr(s, cyls, heads, secs);
1191 }
1192
1193
1194 qemu_co_mutex_init(&s->lock);
1195
1196
1197 if (s->qcow) {
1198 error_setg(&s->migration_blocker,
1199 "The vvfat (rw) format used by node '%s' "
1200 "does not support live migration",
1201 bdrv_get_device_or_node_name(bs));
1202 migrate_add_blocker(s->migration_blocker);
1203 }
1204
1205 ret = 0;
1206fail:
1207 qemu_opts_del(opts);
1208 return ret;
1209}
1210
1211static inline void vvfat_close_current_file(BDRVVVFATState *s)
1212{
1213 if(s->current_mapping) {
1214 s->current_mapping = NULL;
1215 if (s->current_fd) {
1216 qemu_close(s->current_fd);
1217 s->current_fd = 0;
1218 }
1219 }
1220 s->current_cluster = -1;
1221}
1222
1223
1224
1225
1226static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
1227{
1228 while(1) {
1229 int index3;
1230 mapping_t* mapping;
1231 index3=(index1+index2)/2;
1232 mapping=array_get(&(s->mapping),index3);
1233 assert(mapping->begin < mapping->end);
1234 if(mapping->begin>=cluster_num) {
1235 assert(index2!=index3 || index2==0);
1236 if(index2==index3)
1237 return index1;
1238 index2=index3;
1239 } else {
1240 if(index1==index3)
1241 return mapping->end<=cluster_num ? index2 : index1;
1242 index1=index3;
1243 }
1244 assert(index1<=index2);
1245 DLOG(mapping=array_get(&(s->mapping),index1);
1246 assert(mapping->begin<=cluster_num);
1247 assert(index2 >= s->mapping.next ||
1248 ((mapping = array_get(&(s->mapping),index2)) &&
1249 mapping->end>cluster_num)));
1250 }
1251}
1252
1253static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
1254{
1255 int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
1256 mapping_t* mapping;
1257 if(index>=s->mapping.next)
1258 return NULL;
1259 mapping=array_get(&(s->mapping),index);
1260 if(mapping->begin>cluster_num)
1261 return NULL;
1262 assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
1263 return mapping;
1264}
1265
1266static int open_file(BDRVVVFATState* s,mapping_t* mapping)
1267{
1268 if(!mapping)
1269 return -1;
1270 if(!s->current_mapping ||
1271 strcmp(s->current_mapping->path,mapping->path)) {
1272
1273 int fd = qemu_open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
1274 if(fd<0)
1275 return -1;
1276 vvfat_close_current_file(s);
1277 s->current_fd = fd;
1278 s->current_mapping = mapping;
1279 }
1280 return 0;
1281}
1282
1283static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
1284{
1285 if(s->current_cluster != cluster_num) {
1286 int result=0;
1287 off_t offset;
1288 assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
1289 if(!s->current_mapping
1290 || s->current_mapping->begin>cluster_num
1291 || s->current_mapping->end<=cluster_num) {
1292
1293 mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
1294
1295 assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
1296
1297 if (mapping && mapping->mode & MODE_DIRECTORY) {
1298 vvfat_close_current_file(s);
1299 s->current_mapping = mapping;
1300read_cluster_directory:
1301 offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
1302 s->cluster = (unsigned char*)s->directory.pointer+offset
1303 + 0x20*s->current_mapping->info.dir.first_dir_index;
1304 assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
1305 assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
1306 s->current_cluster = cluster_num;
1307 return 0;
1308 }
1309
1310 if(open_file(s,mapping))
1311 return -2;
1312 } else if (s->current_mapping->mode & MODE_DIRECTORY)
1313 goto read_cluster_directory;
1314
1315 assert(s->current_fd);
1316
1317 offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
1318 if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
1319 return -3;
1320 s->cluster=s->cluster_buffer;
1321 result=read(s->current_fd,s->cluster,s->cluster_size);
1322 if(result<0) {
1323 s->current_cluster = -1;
1324 return -1;
1325 }
1326 s->current_cluster = cluster_num;
1327 }
1328 return 0;
1329}
1330
1331#ifdef DEBUG
1332static void print_direntry(const direntry_t* direntry)
1333{
1334 int j = 0;
1335 char buffer[1024];
1336
1337 fprintf(stderr, "direntry %p: ", direntry);
1338 if(!direntry)
1339 return;
1340 if(is_long_name(direntry)) {
1341 unsigned char* c=(unsigned char*)direntry;
1342 int i;
1343 for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
1344#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;}
1345 ADD_CHAR(c[i]);
1346 for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
1347 ADD_CHAR(c[i]);
1348 for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
1349 ADD_CHAR(c[i]);
1350 buffer[j] = 0;
1351 fprintf(stderr, "%s\n", buffer);
1352 } else {
1353 int i;
1354 for(i=0;i<11;i++)
1355 ADD_CHAR(direntry->name[i]);
1356 buffer[j] = 0;
1357 fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
1358 buffer,
1359 direntry->attributes,
1360 begin_of_direntry(direntry),le32_to_cpu(direntry->size));
1361 }
1362}
1363
1364static void print_mapping(const mapping_t* mapping)
1365{
1366 fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, "
1367 "first_mapping_index = %d, name = %s, mode = 0x%x, " ,
1368 mapping, mapping->begin, mapping->end, mapping->dir_index,
1369 mapping->first_mapping_index, mapping->path, mapping->mode);
1370
1371 if (mapping->mode & MODE_DIRECTORY)
1372 fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
1373 else
1374 fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
1375}
1376#endif
1377
1378static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
1379 uint8_t *buf, int nb_sectors)
1380{
1381 BDRVVVFATState *s = bs->opaque;
1382 int i;
1383
1384 for(i=0;i<nb_sectors;i++,sector_num++) {
1385 if (sector_num >= bs->total_sectors)
1386 return -1;
1387 if (s->qcow) {
1388 int n;
1389 if (bdrv_is_allocated(s->qcow, sector_num, nb_sectors-i, &n)) {
1390DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
1391 if (bdrv_read(s->qcow, sector_num, buf + i*0x200, n)) {
1392 return -1;
1393 }
1394 i += n - 1;
1395 sector_num += n - 1;
1396 continue;
1397 }
1398DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
1399 }
1400 if(sector_num<s->faked_sectors) {
1401 if(sector_num<s->first_sectors_number)
1402 memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
1403 else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
1404 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
1405 else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
1406 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
1407 } else {
1408 uint32_t sector=sector_num-s->faked_sectors,
1409 sector_offset_in_cluster=(sector%s->sectors_per_cluster),
1410 cluster_num=sector/s->sectors_per_cluster;
1411 if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) {
1412
1413 memset(buf+i*0x200,0,0x200);
1414 continue;
1415 }
1416 memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
1417 }
1418 }
1419 return 0;
1420}
1421
1422static coroutine_fn int vvfat_co_read(BlockDriverState *bs, int64_t sector_num,
1423 uint8_t *buf, int nb_sectors)
1424{
1425 int ret;
1426 BDRVVVFATState *s = bs->opaque;
1427 qemu_co_mutex_lock(&s->lock);
1428 ret = vvfat_read(bs, sector_num, buf, nb_sectors);
1429 qemu_co_mutex_unlock(&s->lock);
1430 return ret;
1431}
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455typedef struct commit_t {
1456 char* path;
1457 union {
1458 struct { uint32_t cluster; } rename;
1459 struct { int dir_index; uint32_t modified_offset; } writeout;
1460 struct { uint32_t first_cluster; } new_file;
1461 struct { uint32_t cluster; } mkdir;
1462 } param;
1463
1464 enum {
1465 ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
1466 } action;
1467} commit_t;
1468
1469static void clear_commits(BDRVVVFATState* s)
1470{
1471 int i;
1472DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
1473 for (i = 0; i < s->commits.next; i++) {
1474 commit_t* commit = array_get(&(s->commits), i);
1475 assert(commit->path || commit->action == ACTION_WRITEOUT);
1476 if (commit->action != ACTION_WRITEOUT) {
1477 assert(commit->path);
1478 g_free(commit->path);
1479 } else
1480 assert(commit->path == NULL);
1481 }
1482 s->commits.next = 0;
1483}
1484
1485static void schedule_rename(BDRVVVFATState* s,
1486 uint32_t cluster, char* new_path)
1487{
1488 commit_t* commit = array_get_next(&(s->commits));
1489 commit->path = new_path;
1490 commit->param.rename.cluster = cluster;
1491 commit->action = ACTION_RENAME;
1492}
1493
1494static void schedule_writeout(BDRVVVFATState* s,
1495 int dir_index, uint32_t modified_offset)
1496{
1497 commit_t* commit = array_get_next(&(s->commits));
1498 commit->path = NULL;
1499 commit->param.writeout.dir_index = dir_index;
1500 commit->param.writeout.modified_offset = modified_offset;
1501 commit->action = ACTION_WRITEOUT;
1502}
1503
1504static void schedule_new_file(BDRVVVFATState* s,
1505 char* path, uint32_t first_cluster)
1506{
1507 commit_t* commit = array_get_next(&(s->commits));
1508 commit->path = path;
1509 commit->param.new_file.first_cluster = first_cluster;
1510 commit->action = ACTION_NEW_FILE;
1511}
1512
1513static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
1514{
1515 commit_t* commit = array_get_next(&(s->commits));
1516 commit->path = path;
1517 commit->param.mkdir.cluster = cluster;
1518 commit->action = ACTION_MKDIR;
1519}
1520
1521typedef struct {
1522
1523
1524
1525
1526
1527 unsigned char name[0x3f * 13 + 1];
1528 int checksum, len;
1529 int sequence_number;
1530} long_file_name;
1531
1532static void lfn_init(long_file_name* lfn)
1533{
1534 lfn->sequence_number = lfn->len = 0;
1535 lfn->checksum = 0x100;
1536}
1537
1538
1539static int parse_long_name(long_file_name* lfn,
1540 const direntry_t* direntry)
1541{
1542 int i, j, offset;
1543 const unsigned char* pointer = (const unsigned char*)direntry;
1544
1545 if (!is_long_name(direntry))
1546 return 1;
1547
1548 if (pointer[0] & 0x40) {
1549 lfn->sequence_number = pointer[0] & 0x3f;
1550 lfn->checksum = pointer[13];
1551 lfn->name[0] = 0;
1552 lfn->name[lfn->sequence_number * 13] = 0;
1553 } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
1554 return -1;
1555 else if (pointer[13] != lfn->checksum)
1556 return -2;
1557 else if (pointer[12] || pointer[26] || pointer[27])
1558 return -3;
1559
1560 offset = 13 * (lfn->sequence_number - 1);
1561 for (i = 0, j = 1; i < 13; i++, j+=2) {
1562 if (j == 11)
1563 j = 14;
1564 else if (j == 26)
1565 j = 28;
1566
1567 if (pointer[j+1] == 0)
1568 lfn->name[offset + i] = pointer[j];
1569 else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
1570 return -4;
1571 else
1572 lfn->name[offset + i] = 0;
1573 }
1574
1575 if (pointer[0] & 0x40)
1576 lfn->len = offset + strlen((char*)lfn->name + offset);
1577
1578 return 0;
1579}
1580
1581
1582static int parse_short_name(BDRVVVFATState* s,
1583 long_file_name* lfn, direntry_t* direntry)
1584{
1585 int i, j;
1586
1587 if (!is_short_name(direntry))
1588 return 1;
1589
1590 for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
1591 for (i = 0; i <= j; i++) {
1592 if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
1593 return -1;
1594 else if (s->downcase_short_names)
1595 lfn->name[i] = qemu_tolower(direntry->name[i]);
1596 else
1597 lfn->name[i] = direntry->name[i];
1598 }
1599
1600 for (j = 2; j >= 0 && direntry->name[8 + j] == ' '; j--) {
1601 }
1602 if (j >= 0) {
1603 lfn->name[i++] = '.';
1604 lfn->name[i + j + 1] = '\0';
1605 for (;j >= 0; j--) {
1606 uint8_t c = direntry->name[8 + j];
1607 if (c <= ' ' || c > 0x7f) {
1608 return -2;
1609 } else if (s->downcase_short_names) {
1610 lfn->name[i + j] = qemu_tolower(c);
1611 } else {
1612 lfn->name[i + j] = c;
1613 }
1614 }
1615 } else
1616 lfn->name[i + j + 1] = '\0';
1617
1618 lfn->len = strlen((char*)lfn->name);
1619
1620 return 0;
1621}
1622
1623static inline uint32_t modified_fat_get(BDRVVVFATState* s,
1624 unsigned int cluster)
1625{
1626 if (cluster < s->last_cluster_of_root_directory) {
1627 if (cluster + 1 == s->last_cluster_of_root_directory)
1628 return s->max_fat_value;
1629 else
1630 return cluster + 1;
1631 }
1632
1633 if (s->fat_type==32) {
1634 uint32_t* entry=((uint32_t*)s->fat2)+cluster;
1635 return le32_to_cpu(*entry);
1636 } else if (s->fat_type==16) {
1637 uint16_t* entry=((uint16_t*)s->fat2)+cluster;
1638 return le16_to_cpu(*entry);
1639 } else {
1640 const uint8_t* x=s->fat2+cluster*3/2;
1641 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
1642 }
1643}
1644
1645static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
1646{
1647 int was_modified = 0;
1648 int i, dummy;
1649
1650 if (s->qcow == NULL)
1651 return 0;
1652
1653 for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
1654 was_modified = bdrv_is_allocated(s->qcow,
1655 cluster2sector(s, cluster_num) + i, 1, &dummy);
1656
1657 return was_modified;
1658}
1659
1660static const char* get_basename(const char* path)
1661{
1662 char* basename = strrchr(path, '/');
1663 if (basename == NULL)
1664 return path;
1665 else
1666 return basename + 1;
1667}
1668
1669
1670
1671
1672
1673
1674
1675
1676typedef enum {
1677 USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
1678} used_t;
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
1691 direntry_t* direntry, const char* path)
1692{
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710 int copy_it = 0;
1711 int was_modified = 0;
1712 int32_t ret = 0;
1713
1714 uint32_t cluster_num = begin_of_direntry(direntry);
1715 uint32_t offset = 0;
1716 int first_mapping_index = -1;
1717 mapping_t* mapping = NULL;
1718 const char* basename2 = NULL;
1719
1720 vvfat_close_current_file(s);
1721
1722
1723 if (cluster_num == 0)
1724 return 0;
1725
1726
1727 if (s->qcow) {
1728 basename2 = get_basename(path);
1729
1730 mapping = find_mapping_for_cluster(s, cluster_num);
1731
1732 if (mapping) {
1733 const char* basename;
1734
1735 assert(mapping->mode & MODE_DELETED);
1736 mapping->mode &= ~MODE_DELETED;
1737
1738 basename = get_basename(mapping->path);
1739
1740 assert(mapping->mode & MODE_NORMAL);
1741
1742
1743 if (strcmp(basename, basename2))
1744 schedule_rename(s, cluster_num, g_strdup(path));
1745 } else if (is_file(direntry))
1746
1747 schedule_new_file(s, g_strdup(path), cluster_num);
1748 else {
1749 abort();
1750 return 0;
1751 }
1752 }
1753
1754 while(1) {
1755 if (s->qcow) {
1756 if (!copy_it && cluster_was_modified(s, cluster_num)) {
1757 if (mapping == NULL ||
1758 mapping->begin > cluster_num ||
1759 mapping->end <= cluster_num)
1760 mapping = find_mapping_for_cluster(s, cluster_num);
1761
1762
1763 if (mapping &&
1764 (mapping->mode & MODE_DIRECTORY) == 0) {
1765
1766
1767 if (offset != mapping->info.file.offset + s->cluster_size
1768 * (cluster_num - mapping->begin)) {
1769
1770 abort();
1771 copy_it = 1;
1772 } else if (offset == 0) {
1773 const char* basename = get_basename(mapping->path);
1774
1775 if (strcmp(basename, basename2))
1776 copy_it = 1;
1777 first_mapping_index = array_index(&(s->mapping), mapping);
1778 }
1779
1780 if (mapping->first_mapping_index != first_mapping_index
1781 && mapping->info.file.offset > 0) {
1782 abort();
1783 copy_it = 1;
1784 }
1785
1786
1787 if (!was_modified && is_file(direntry)) {
1788 was_modified = 1;
1789 schedule_writeout(s, mapping->dir_index, offset);
1790 }
1791 }
1792 }
1793
1794 if (copy_it) {
1795 int i, dummy;
1796
1797
1798
1799
1800 int64_t offset = cluster2sector(s, cluster_num);
1801
1802 vvfat_close_current_file(s);
1803 for (i = 0; i < s->sectors_per_cluster; i++) {
1804 if (!bdrv_is_allocated(s->qcow, offset + i, 1, &dummy)) {
1805 if (vvfat_read(s->bs, offset, s->cluster_buffer, 1)) {
1806 return -1;
1807 }
1808 if (bdrv_write(s->qcow, offset, s->cluster_buffer, 1)) {
1809 return -2;
1810 }
1811 }
1812 }
1813 }
1814 }
1815
1816 ret++;
1817 if (s->used_clusters[cluster_num] & USED_ANY)
1818 return 0;
1819 s->used_clusters[cluster_num] = USED_FILE;
1820
1821 cluster_num = modified_fat_get(s, cluster_num);
1822
1823 if (fat_eof(s, cluster_num))
1824 return ret;
1825 else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
1826 return -1;
1827
1828 offset += s->cluster_size;
1829 }
1830}
1831
1832
1833
1834
1835
1836
1837static int check_directory_consistency(BDRVVVFATState *s,
1838 int cluster_num, const char* path)
1839{
1840 int ret = 0;
1841 unsigned char* cluster = g_malloc(s->cluster_size);
1842 direntry_t* direntries = (direntry_t*)cluster;
1843 mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
1844
1845 long_file_name lfn;
1846 int path_len = strlen(path);
1847 char path2[PATH_MAX + 1];
1848
1849 assert(path_len < PATH_MAX);
1850 pstrcpy(path2, sizeof(path2), path);
1851 path2[path_len] = '/';
1852 path2[path_len + 1] = '\0';
1853
1854 if (mapping) {
1855 const char* basename = get_basename(mapping->path);
1856 const char* basename2 = get_basename(path);
1857
1858 assert(mapping->mode & MODE_DIRECTORY);
1859
1860 assert(mapping->mode & MODE_DELETED);
1861 mapping->mode &= ~MODE_DELETED;
1862
1863 if (strcmp(basename, basename2))
1864 schedule_rename(s, cluster_num, g_strdup(path));
1865 } else
1866
1867 schedule_mkdir(s, cluster_num, g_strdup(path));
1868
1869 lfn_init(&lfn);
1870 do {
1871 int i;
1872 int subret = 0;
1873
1874 ret++;
1875
1876 if (s->used_clusters[cluster_num] & USED_ANY) {
1877 fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
1878 goto fail;
1879 }
1880 s->used_clusters[cluster_num] = USED_DIRECTORY;
1881
1882DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
1883 subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
1884 s->sectors_per_cluster);
1885 if (subret) {
1886 fprintf(stderr, "Error fetching direntries\n");
1887 fail:
1888 g_free(cluster);
1889 return 0;
1890 }
1891
1892 for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
1893 int cluster_count = 0;
1894
1895DLOG(fprintf(stderr, "check direntry %d:\n", i); print_direntry(direntries + i));
1896 if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
1897 is_free(direntries + i))
1898 continue;
1899
1900 subret = parse_long_name(&lfn, direntries + i);
1901 if (subret < 0) {
1902 fprintf(stderr, "Error in long name\n");
1903 goto fail;
1904 }
1905 if (subret == 0 || is_free(direntries + i))
1906 continue;
1907
1908 if (fat_chksum(direntries+i) != lfn.checksum) {
1909 subret = parse_short_name(s, &lfn, direntries + i);
1910 if (subret < 0) {
1911 fprintf(stderr, "Error in short name (%d)\n", subret);
1912 goto fail;
1913 }
1914 if (subret > 0 || !strcmp((char*)lfn.name, ".")
1915 || !strcmp((char*)lfn.name, ".."))
1916 continue;
1917 }
1918 lfn.checksum = 0x100;
1919
1920 if (path_len + 1 + lfn.len >= PATH_MAX) {
1921 fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
1922 goto fail;
1923 }
1924 pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
1925 (char*)lfn.name);
1926
1927 if (is_directory(direntries + i)) {
1928 if (begin_of_direntry(direntries + i) == 0) {
1929 DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
1930 goto fail;
1931 }
1932 cluster_count = check_directory_consistency(s,
1933 begin_of_direntry(direntries + i), path2);
1934 if (cluster_count == 0) {
1935 DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
1936 goto fail;
1937 }
1938 } else if (is_file(direntries + i)) {
1939
1940 cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
1941 if (cluster_count !=
1942 (le32_to_cpu(direntries[i].size) + s->cluster_size
1943 - 1) / s->cluster_size) {
1944 DLOG(fprintf(stderr, "Cluster count mismatch\n"));
1945 goto fail;
1946 }
1947 } else
1948 abort();
1949
1950 ret += cluster_count;
1951 }
1952
1953 cluster_num = modified_fat_get(s, cluster_num);
1954 } while(!fat_eof(s, cluster_num));
1955
1956 g_free(cluster);
1957 return ret;
1958}
1959
1960
1961static int is_consistent(BDRVVVFATState* s)
1962{
1963 int i, check;
1964 int used_clusters_count = 0;
1965
1966DLOG(checkpoint());
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980 if (s->fat2 == NULL) {
1981 int size = 0x200 * s->sectors_per_fat;
1982 s->fat2 = g_malloc(size);
1983 memcpy(s->fat2, s->fat.pointer, size);
1984 }
1985 check = vvfat_read(s->bs,
1986 s->first_sectors_number, s->fat2, s->sectors_per_fat);
1987 if (check) {
1988 fprintf(stderr, "Could not copy fat\n");
1989 return 0;
1990 }
1991 assert (s->used_clusters);
1992 for (i = 0; i < sector2cluster(s, s->sector_count); i++)
1993 s->used_clusters[i] &= ~USED_ANY;
1994
1995 clear_commits(s);
1996
1997
1998
1999 if (s->qcow)
2000 for (i = 0; i < s->mapping.next; i++) {
2001 mapping_t* mapping = array_get(&(s->mapping), i);
2002 if (mapping->first_mapping_index < 0)
2003 mapping->mode |= MODE_DELETED;
2004 }
2005
2006 used_clusters_count = check_directory_consistency(s, 0, s->path);
2007 if (used_clusters_count <= 0) {
2008 DLOG(fprintf(stderr, "problem in directory\n"));
2009 return 0;
2010 }
2011
2012 check = s->last_cluster_of_root_directory;
2013 for (i = check; i < sector2cluster(s, s->sector_count); i++) {
2014 if (modified_fat_get(s, i)) {
2015 if(!s->used_clusters[i]) {
2016 DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
2017 return 0;
2018 }
2019 check++;
2020 }
2021
2022 if (s->used_clusters[i] == USED_ALLOCATED) {
2023
2024 DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
2025 return 0;
2026 }
2027 }
2028
2029 if (check != used_clusters_count)
2030 return 0;
2031
2032 return used_clusters_count;
2033}
2034
2035static inline void adjust_mapping_indices(BDRVVVFATState* s,
2036 int offset, int adjust)
2037{
2038 int i;
2039
2040 for (i = 0; i < s->mapping.next; i++) {
2041 mapping_t* mapping = array_get(&(s->mapping), i);
2042
2043#define ADJUST_MAPPING_INDEX(name) \
2044 if (mapping->name >= offset) \
2045 mapping->name += adjust
2046
2047 ADJUST_MAPPING_INDEX(first_mapping_index);
2048 if (mapping->mode & MODE_DIRECTORY)
2049 ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
2050 }
2051}
2052
2053
2054static mapping_t* insert_mapping(BDRVVVFATState* s,
2055 uint32_t begin, uint32_t end)
2056{
2057
2058
2059
2060
2061
2062
2063
2064 int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
2065 mapping_t* mapping = NULL;
2066 mapping_t* first_mapping = array_get(&(s->mapping), 0);
2067
2068 if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
2069 && mapping->begin < begin) {
2070 mapping->end = begin;
2071 index++;
2072 mapping = array_get(&(s->mapping), index);
2073 }
2074 if (index >= s->mapping.next || mapping->begin > begin) {
2075 mapping = array_insert(&(s->mapping), index, 1);
2076 mapping->path = NULL;
2077 adjust_mapping_indices(s, index, +1);
2078 }
2079
2080 mapping->begin = begin;
2081 mapping->end = end;
2082
2083DLOG(mapping_t* next_mapping;
2084assert(index + 1 >= s->mapping.next ||
2085((next_mapping = array_get(&(s->mapping), index + 1)) &&
2086 next_mapping->begin >= end)));
2087
2088 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
2089 s->current_mapping = array_get(&(s->mapping),
2090 s->current_mapping - first_mapping);
2091
2092 return mapping;
2093}
2094
2095static int remove_mapping(BDRVVVFATState* s, int mapping_index)
2096{
2097 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
2098 mapping_t* first_mapping = array_get(&(s->mapping), 0);
2099
2100
2101 if (mapping->first_mapping_index < 0) {
2102 g_free(mapping->path);
2103 }
2104
2105
2106 array_remove(&(s->mapping), mapping_index);
2107
2108
2109 adjust_mapping_indices(s, mapping_index, -1);
2110
2111 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
2112 s->current_mapping = array_get(&(s->mapping),
2113 s->current_mapping - first_mapping);
2114
2115 return 0;
2116}
2117
2118static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
2119{
2120 int i;
2121 for (i = 0; i < s->mapping.next; i++) {
2122 mapping_t* mapping = array_get(&(s->mapping), i);
2123 if (mapping->dir_index >= offset)
2124 mapping->dir_index += adjust;
2125 if ((mapping->mode & MODE_DIRECTORY) &&
2126 mapping->info.dir.first_dir_index >= offset)
2127 mapping->info.dir.first_dir_index += adjust;
2128 }
2129}
2130
2131static direntry_t* insert_direntries(BDRVVVFATState* s,
2132 int dir_index, int count)
2133{
2134
2135
2136
2137
2138 direntry_t* result = array_insert(&(s->directory), dir_index, count);
2139 if (result == NULL)
2140 return NULL;
2141 adjust_dirindices(s, dir_index, count);
2142 return result;
2143}
2144
2145static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
2146{
2147 int ret = array_remove_slice(&(s->directory), dir_index, count);
2148 if (ret)
2149 return ret;
2150 adjust_dirindices(s, dir_index, -count);
2151 return 0;
2152}
2153
2154
2155
2156
2157
2158
2159
2160static int commit_mappings(BDRVVVFATState* s,
2161 uint32_t first_cluster, int dir_index)
2162{
2163 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2164 direntry_t* direntry = array_get(&(s->directory), dir_index);
2165 uint32_t cluster = first_cluster;
2166
2167 vvfat_close_current_file(s);
2168
2169 assert(mapping);
2170 assert(mapping->begin == first_cluster);
2171 mapping->first_mapping_index = -1;
2172 mapping->dir_index = dir_index;
2173 mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
2174 MODE_DIRECTORY : MODE_NORMAL;
2175
2176 while (!fat_eof(s, cluster)) {
2177 uint32_t c, c1;
2178
2179 for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
2180 c = c1, c1 = modified_fat_get(s, c1));
2181
2182 c++;
2183 if (c > mapping->end) {
2184 int index = array_index(&(s->mapping), mapping);
2185 int i, max_i = s->mapping.next - index;
2186 for (i = 1; i < max_i && mapping[i].begin < c; i++);
2187 while (--i > 0)
2188 remove_mapping(s, index + 1);
2189 }
2190 assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
2191 || mapping[1].begin >= c);
2192 mapping->end = c;
2193
2194 if (!fat_eof(s, c1)) {
2195 int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
2196 mapping_t* next_mapping = i >= s->mapping.next ? NULL :
2197 array_get(&(s->mapping), i);
2198
2199 if (next_mapping == NULL || next_mapping->begin > c1) {
2200 int i1 = array_index(&(s->mapping), mapping);
2201
2202 next_mapping = insert_mapping(s, c1, c1+1);
2203
2204 if (c1 < c)
2205 i1++;
2206 mapping = array_get(&(s->mapping), i1);
2207 }
2208
2209 next_mapping->dir_index = mapping->dir_index;
2210 next_mapping->first_mapping_index =
2211 mapping->first_mapping_index < 0 ?
2212 array_index(&(s->mapping), mapping) :
2213 mapping->first_mapping_index;
2214 next_mapping->path = mapping->path;
2215 next_mapping->mode = mapping->mode;
2216 next_mapping->read_only = mapping->read_only;
2217 if (mapping->mode & MODE_DIRECTORY) {
2218 next_mapping->info.dir.parent_mapping_index =
2219 mapping->info.dir.parent_mapping_index;
2220 next_mapping->info.dir.first_dir_index =
2221 mapping->info.dir.first_dir_index +
2222 0x10 * s->sectors_per_cluster *
2223 (mapping->end - mapping->begin);
2224 } else
2225 next_mapping->info.file.offset = mapping->info.file.offset +
2226 mapping->end - mapping->begin;
2227
2228 mapping = next_mapping;
2229 }
2230
2231 cluster = c1;
2232 }
2233
2234 return 0;
2235}
2236
2237static int commit_direntries(BDRVVVFATState* s,
2238 int dir_index, int parent_mapping_index)
2239{
2240 direntry_t* direntry = array_get(&(s->directory), dir_index);
2241 uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
2242 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2243
2244 int factor = 0x10 * s->sectors_per_cluster;
2245 int old_cluster_count, new_cluster_count;
2246 int current_dir_index = mapping->info.dir.first_dir_index;
2247 int first_dir_index = current_dir_index;
2248 int ret, i;
2249 uint32_t c;
2250
2251DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
2252
2253 assert(direntry);
2254 assert(mapping);
2255 assert(mapping->begin == first_cluster);
2256 assert(mapping->info.dir.first_dir_index < s->directory.next);
2257 assert(mapping->mode & MODE_DIRECTORY);
2258 assert(dir_index == 0 || is_directory(direntry));
2259
2260 mapping->info.dir.parent_mapping_index = parent_mapping_index;
2261
2262 if (first_cluster == 0) {
2263 old_cluster_count = new_cluster_count =
2264 s->last_cluster_of_root_directory;
2265 } else {
2266 for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2267 c = fat_get(s, c))
2268 old_cluster_count++;
2269
2270 for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2271 c = modified_fat_get(s, c))
2272 new_cluster_count++;
2273 }
2274
2275 if (new_cluster_count > old_cluster_count) {
2276 if (insert_direntries(s,
2277 current_dir_index + factor * old_cluster_count,
2278 factor * (new_cluster_count - old_cluster_count)) == NULL)
2279 return -1;
2280 } else if (new_cluster_count < old_cluster_count)
2281 remove_direntries(s,
2282 current_dir_index + factor * new_cluster_count,
2283 factor * (old_cluster_count - new_cluster_count));
2284
2285 for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
2286 void* direntry = array_get(&(s->directory), current_dir_index);
2287 int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
2288 s->sectors_per_cluster);
2289 if (ret)
2290 return ret;
2291 assert(!strncmp(s->directory.pointer, "QEMU", 4));
2292 current_dir_index += factor;
2293 }
2294
2295 ret = commit_mappings(s, first_cluster, dir_index);
2296 if (ret)
2297 return ret;
2298
2299
2300 for (i = 0; i < factor * new_cluster_count; i++) {
2301 direntry = array_get(&(s->directory), first_dir_index + i);
2302 if (is_directory(direntry) && !is_dot(direntry)) {
2303 mapping = find_mapping_for_cluster(s, first_cluster);
2304 assert(mapping->mode & MODE_DIRECTORY);
2305 ret = commit_direntries(s, first_dir_index + i,
2306 array_index(&(s->mapping), mapping));
2307 if (ret)
2308 return ret;
2309 }
2310 }
2311
2312 return 0;
2313}
2314
2315
2316
2317static int commit_one_file(BDRVVVFATState* s,
2318 int dir_index, uint32_t offset)
2319{
2320 direntry_t* direntry = array_get(&(s->directory), dir_index);
2321 uint32_t c = begin_of_direntry(direntry);
2322 uint32_t first_cluster = c;
2323 mapping_t* mapping = find_mapping_for_cluster(s, c);
2324 uint32_t size = filesize_of_direntry(direntry);
2325 char* cluster = g_malloc(s->cluster_size);
2326 uint32_t i;
2327 int fd = 0;
2328
2329 assert(offset < size);
2330 assert((offset % s->cluster_size) == 0);
2331
2332 for (i = s->cluster_size; i < offset; i += s->cluster_size)
2333 c = modified_fat_get(s, c);
2334
2335 fd = qemu_open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
2336 if (fd < 0) {
2337 fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
2338 strerror(errno), errno);
2339 g_free(cluster);
2340 return fd;
2341 }
2342 if (offset > 0) {
2343 if (lseek(fd, offset, SEEK_SET) != offset) {
2344 qemu_close(fd);
2345 g_free(cluster);
2346 return -3;
2347 }
2348 }
2349
2350 while (offset < size) {
2351 uint32_t c1;
2352 int rest_size = (size - offset > s->cluster_size ?
2353 s->cluster_size : size - offset);
2354 int ret;
2355
2356 c1 = modified_fat_get(s, c);
2357
2358 assert((size - offset == 0 && fat_eof(s, c)) ||
2359 (size > offset && c >=2 && !fat_eof(s, c)));
2360
2361 ret = vvfat_read(s->bs, cluster2sector(s, c),
2362 (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
2363
2364 if (ret < 0) {
2365 qemu_close(fd);
2366 g_free(cluster);
2367 return ret;
2368 }
2369
2370 if (write(fd, cluster, rest_size) < 0) {
2371 qemu_close(fd);
2372 g_free(cluster);
2373 return -2;
2374 }
2375
2376 offset += rest_size;
2377 c = c1;
2378 }
2379
2380 if (ftruncate(fd, size)) {
2381 perror("ftruncate()");
2382 qemu_close(fd);
2383 g_free(cluster);
2384 return -4;
2385 }
2386 qemu_close(fd);
2387 g_free(cluster);
2388
2389 return commit_mappings(s, first_cluster, dir_index);
2390}
2391
2392#ifdef DEBUG
2393
2394static void check1(BDRVVVFATState* s)
2395{
2396 int i;
2397 for (i = 0; i < s->mapping.next; i++) {
2398 mapping_t* mapping = array_get(&(s->mapping), i);
2399 if (mapping->mode & MODE_DELETED) {
2400 fprintf(stderr, "deleted\n");
2401 continue;
2402 }
2403 assert(mapping->dir_index < s->directory.next);
2404 direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
2405 assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
2406 if (mapping->mode & MODE_DIRECTORY) {
2407 assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
2408 assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
2409 }
2410 }
2411}
2412
2413
2414static void check2(BDRVVVFATState* s)
2415{
2416 int i;
2417 int first_mapping = -1;
2418
2419 for (i = 0; i < s->directory.next; i++) {
2420 direntry_t* direntry = array_get(&(s->directory), i);
2421
2422 if (is_short_name(direntry) && begin_of_direntry(direntry)) {
2423 mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
2424 assert(mapping);
2425 assert(mapping->dir_index == i || is_dot(direntry));
2426 assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
2427 }
2428
2429 if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
2430
2431 int j, count = 0;
2432
2433 for (j = 0; j < s->mapping.next; j++) {
2434 mapping_t* mapping = array_get(&(s->mapping), j);
2435 if (mapping->mode & MODE_DELETED)
2436 continue;
2437 if (mapping->mode & MODE_DIRECTORY) {
2438 if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
2439 assert(++count == 1);
2440 if (mapping->first_mapping_index == -1)
2441 first_mapping = array_index(&(s->mapping), mapping);
2442 else
2443 assert(first_mapping == mapping->first_mapping_index);
2444 if (mapping->info.dir.parent_mapping_index < 0)
2445 assert(j == 0);
2446 else {
2447 mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
2448 assert(parent->mode & MODE_DIRECTORY);
2449 assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
2450 }
2451 }
2452 }
2453 }
2454 if (count == 0)
2455 first_mapping = -1;
2456 }
2457 }
2458}
2459#endif
2460
2461static int handle_renames_and_mkdirs(BDRVVVFATState* s)
2462{
2463 int i;
2464
2465#ifdef DEBUG
2466 fprintf(stderr, "handle_renames\n");
2467 for (i = 0; i < s->commits.next; i++) {
2468 commit_t* commit = array_get(&(s->commits), i);
2469 fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
2470 }
2471#endif
2472
2473 for (i = 0; i < s->commits.next;) {
2474 commit_t* commit = array_get(&(s->commits), i);
2475 if (commit->action == ACTION_RENAME) {
2476 mapping_t* mapping = find_mapping_for_cluster(s,
2477 commit->param.rename.cluster);
2478 char* old_path = mapping->path;
2479
2480 assert(commit->path);
2481 mapping->path = commit->path;
2482 if (rename(old_path, mapping->path))
2483 return -2;
2484
2485 if (mapping->mode & MODE_DIRECTORY) {
2486 int l1 = strlen(mapping->path);
2487 int l2 = strlen(old_path);
2488 int diff = l1 - l2;
2489 direntry_t* direntry = array_get(&(s->directory),
2490 mapping->info.dir.first_dir_index);
2491 uint32_t c = mapping->begin;
2492 int i = 0;
2493
2494
2495 while (!fat_eof(s, c)) {
2496 do {
2497 direntry_t* d = direntry + i;
2498
2499 if (is_file(d) || (is_directory(d) && !is_dot(d))) {
2500 mapping_t* m = find_mapping_for_cluster(s,
2501 begin_of_direntry(d));
2502 int l = strlen(m->path);
2503 char* new_path = g_malloc(l + diff + 1);
2504
2505 assert(!strncmp(m->path, mapping->path, l2));
2506
2507 pstrcpy(new_path, l + diff + 1, mapping->path);
2508 pstrcpy(new_path + l1, l + diff + 1 - l1,
2509 m->path + l2);
2510
2511 schedule_rename(s, m->begin, new_path);
2512 }
2513 i++;
2514 } while((i % (0x10 * s->sectors_per_cluster)) != 0);
2515 c = fat_get(s, c);
2516 }
2517 }
2518
2519 g_free(old_path);
2520 array_remove(&(s->commits), i);
2521 continue;
2522 } else if (commit->action == ACTION_MKDIR) {
2523 mapping_t* mapping;
2524 int j, parent_path_len;
2525
2526#ifdef __MINGW32__
2527 if (mkdir(commit->path))
2528 return -5;
2529#else
2530 if (mkdir(commit->path, 0755))
2531 return -5;
2532#endif
2533
2534 mapping = insert_mapping(s, commit->param.mkdir.cluster,
2535 commit->param.mkdir.cluster + 1);
2536 if (mapping == NULL)
2537 return -6;
2538
2539 mapping->mode = MODE_DIRECTORY;
2540 mapping->read_only = 0;
2541 mapping->path = commit->path;
2542 j = s->directory.next;
2543 assert(j);
2544 insert_direntries(s, s->directory.next,
2545 0x10 * s->sectors_per_cluster);
2546 mapping->info.dir.first_dir_index = j;
2547
2548 parent_path_len = strlen(commit->path)
2549 - strlen(get_basename(commit->path)) - 1;
2550 for (j = 0; j < s->mapping.next; j++) {
2551 mapping_t* m = array_get(&(s->mapping), j);
2552 if (m->first_mapping_index < 0 && m != mapping &&
2553 !strncmp(m->path, mapping->path, parent_path_len) &&
2554 strlen(m->path) == parent_path_len)
2555 break;
2556 }
2557 assert(j < s->mapping.next);
2558 mapping->info.dir.parent_mapping_index = j;
2559
2560 array_remove(&(s->commits), i);
2561 continue;
2562 }
2563
2564 i++;
2565 }
2566 return 0;
2567}
2568
2569
2570
2571
2572static int handle_commits(BDRVVVFATState* s)
2573{
2574 int i, fail = 0;
2575
2576 vvfat_close_current_file(s);
2577
2578 for (i = 0; !fail && i < s->commits.next; i++) {
2579 commit_t* commit = array_get(&(s->commits), i);
2580 switch(commit->action) {
2581 case ACTION_RENAME: case ACTION_MKDIR:
2582 abort();
2583 fail = -2;
2584 break;
2585 case ACTION_WRITEOUT: {
2586#ifndef NDEBUG
2587
2588 direntry_t* entry = array_get(&(s->directory),
2589 commit->param.writeout.dir_index);
2590 uint32_t begin = begin_of_direntry(entry);
2591 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2592#endif
2593
2594 assert(mapping);
2595 assert(mapping->begin == begin);
2596 assert(commit->path == NULL);
2597
2598 if (commit_one_file(s, commit->param.writeout.dir_index,
2599 commit->param.writeout.modified_offset))
2600 fail = -3;
2601
2602 break;
2603 }
2604 case ACTION_NEW_FILE: {
2605 int begin = commit->param.new_file.first_cluster;
2606 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2607 direntry_t* entry;
2608 int i;
2609
2610
2611 for (i = 0; i < s->directory.next; i++) {
2612 entry = array_get(&(s->directory), i);
2613 if (is_file(entry) && begin_of_direntry(entry) == begin)
2614 break;
2615 }
2616
2617 if (i >= s->directory.next) {
2618 fail = -6;
2619 continue;
2620 }
2621
2622
2623 if (mapping && mapping->begin != begin) {
2624 mapping->end = begin;
2625 mapping = NULL;
2626 }
2627 if (mapping == NULL) {
2628 mapping = insert_mapping(s, begin, begin+1);
2629 }
2630
2631 assert(commit->path);
2632 mapping->path = commit->path;
2633 mapping->read_only = 0;
2634 mapping->mode = MODE_NORMAL;
2635 mapping->info.file.offset = 0;
2636
2637 if (commit_one_file(s, i, 0))
2638 fail = -7;
2639
2640 break;
2641 }
2642 default:
2643 abort();
2644 }
2645 }
2646 if (i > 0 && array_remove_slice(&(s->commits), 0, i))
2647 return -1;
2648 return fail;
2649}
2650
2651static int handle_deletes(BDRVVVFATState* s)
2652{
2653 int i, deferred = 1, deleted = 1;
2654
2655
2656
2657 while (deferred && deleted) {
2658 deferred = 0;
2659 deleted = 0;
2660
2661 for (i = 1; i < s->mapping.next; i++) {
2662 mapping_t* mapping = array_get(&(s->mapping), i);
2663 if (mapping->mode & MODE_DELETED) {
2664 direntry_t* entry = array_get(&(s->directory),
2665 mapping->dir_index);
2666
2667 if (is_free(entry)) {
2668
2669 if (mapping->mode & MODE_DIRECTORY) {
2670 int j, next_dir_index = s->directory.next,
2671 first_dir_index = mapping->info.dir.first_dir_index;
2672
2673 if (rmdir(mapping->path) < 0) {
2674 if (errno == ENOTEMPTY) {
2675 deferred++;
2676 continue;
2677 } else
2678 return -5;
2679 }
2680
2681 for (j = 1; j < s->mapping.next; j++) {
2682 mapping_t* m = array_get(&(s->mapping), j);
2683 if (m->mode & MODE_DIRECTORY &&
2684 m->info.dir.first_dir_index >
2685 first_dir_index &&
2686 m->info.dir.first_dir_index <
2687 next_dir_index)
2688 next_dir_index =
2689 m->info.dir.first_dir_index;
2690 }
2691 remove_direntries(s, first_dir_index,
2692 next_dir_index - first_dir_index);
2693
2694 deleted++;
2695 }
2696 } else {
2697 if (unlink(mapping->path))
2698 return -4;
2699 deleted++;
2700 }
2701 DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
2702 remove_mapping(s, i);
2703 }
2704 }
2705 }
2706
2707 return 0;
2708}
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718static int do_commit(BDRVVVFATState* s)
2719{
2720 int ret = 0;
2721
2722
2723 if (s->commits.next == 0)
2724 return 0;
2725
2726 vvfat_close_current_file(s);
2727
2728 ret = handle_renames_and_mkdirs(s);
2729 if (ret) {
2730 fprintf(stderr, "Error handling renames (%d)\n", ret);
2731 abort();
2732 return ret;
2733 }
2734
2735
2736 memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
2737
2738
2739 ret = commit_direntries(s, 0, -1);
2740 if (ret) {
2741 fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
2742 abort();
2743 return ret;
2744 }
2745
2746 ret = handle_commits(s);
2747 if (ret) {
2748 fprintf(stderr, "Error handling commits (%d)\n", ret);
2749 abort();
2750 return ret;
2751 }
2752
2753 ret = handle_deletes(s);
2754 if (ret) {
2755 fprintf(stderr, "Error deleting\n");
2756 abort();
2757 return ret;
2758 }
2759
2760 if (s->qcow->drv->bdrv_make_empty) {
2761 s->qcow->drv->bdrv_make_empty(s->qcow);
2762 }
2763
2764 memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
2765
2766DLOG(checkpoint());
2767 return 0;
2768}
2769
2770static int try_commit(BDRVVVFATState* s)
2771{
2772 vvfat_close_current_file(s);
2773DLOG(checkpoint());
2774 if(!is_consistent(s))
2775 return -1;
2776 return do_commit(s);
2777}
2778
2779static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
2780 const uint8_t *buf, int nb_sectors)
2781{
2782 BDRVVVFATState *s = bs->opaque;
2783 int i, ret;
2784
2785DLOG(checkpoint());
2786
2787
2788 if (s->qcow == NULL) {
2789 return -EACCES;
2790 }
2791
2792 vvfat_close_current_file(s);
2793
2794
2795
2796
2797
2798
2799
2800 if (sector_num < s->first_sectors_number)
2801 return -1;
2802
2803 for (i = sector2cluster(s, sector_num);
2804 i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
2805 mapping_t* mapping = find_mapping_for_cluster(s, i);
2806 if (mapping) {
2807 if (mapping->read_only) {
2808 fprintf(stderr, "Tried to write to write-protected file %s\n",
2809 mapping->path);
2810 return -1;
2811 }
2812
2813 if (mapping->mode & MODE_DIRECTORY) {
2814 int begin = cluster2sector(s, i);
2815 int end = begin + s->sectors_per_cluster, k;
2816 int dir_index;
2817 const direntry_t* direntries;
2818 long_file_name lfn;
2819
2820 lfn_init(&lfn);
2821
2822 if (begin < sector_num)
2823 begin = sector_num;
2824 if (end > sector_num + nb_sectors)
2825 end = sector_num + nb_sectors;
2826 dir_index = mapping->dir_index +
2827 0x10 * (begin - mapping->begin * s->sectors_per_cluster);
2828 direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
2829
2830 for (k = 0; k < (end - begin) * 0x10; k++) {
2831
2832 if (parse_long_name(&lfn, direntries + k) < 0) {
2833 fprintf(stderr, "Warning: non-ASCII filename\n");
2834 return -1;
2835 }
2836
2837 else if (is_short_name(direntries+k) &&
2838 (direntries[k].attributes & 1)) {
2839 if (memcmp(direntries + k,
2840 array_get(&(s->directory), dir_index + k),
2841 sizeof(direntry_t))) {
2842 fprintf(stderr, "Warning: tried to write to write-protected file\n");
2843 return -1;
2844 }
2845 }
2846 }
2847 }
2848 i = mapping->end;
2849 } else
2850 i++;
2851 }
2852
2853
2854
2855
2856DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
2857 ret = bdrv_write(s->qcow, sector_num, buf, nb_sectors);
2858 if (ret < 0) {
2859 fprintf(stderr, "Error writing to qcow backend\n");
2860 return ret;
2861 }
2862
2863 for (i = sector2cluster(s, sector_num);
2864 i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
2865 if (i >= 0)
2866 s->used_clusters[i] |= USED_ALLOCATED;
2867
2868DLOG(checkpoint());
2869
2870 try_commit(s);
2871
2872DLOG(checkpoint());
2873 return 0;
2874}
2875
2876static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num,
2877 const uint8_t *buf, int nb_sectors)
2878{
2879 int ret;
2880 BDRVVVFATState *s = bs->opaque;
2881 qemu_co_mutex_lock(&s->lock);
2882 ret = vvfat_write(bs, sector_num, buf, nb_sectors);
2883 qemu_co_mutex_unlock(&s->lock);
2884 return ret;
2885}
2886
2887static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs,
2888 int64_t sector_num, int nb_sectors, int *n, BlockDriverState **file)
2889{
2890 BDRVVVFATState* s = bs->opaque;
2891 *n = s->sector_count - sector_num;
2892 if (*n > nb_sectors) {
2893 *n = nb_sectors;
2894 } else if (*n < 0) {
2895 return 0;
2896 }
2897 return BDRV_BLOCK_DATA;
2898}
2899
2900static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
2901 const uint8_t* buffer, int nb_sectors) {
2902 BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
2903 return try_commit(s);
2904}
2905
2906static void write_target_close(BlockDriverState *bs) {
2907 BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
2908 bdrv_unref(s->qcow);
2909 g_free(s->qcow_filename);
2910}
2911
2912static BlockDriver vvfat_write_target = {
2913 .format_name = "vvfat_write_target",
2914 .bdrv_write = write_target_commit,
2915 .bdrv_close = write_target_close,
2916};
2917
2918static int enable_write_target(BDRVVVFATState *s, Error **errp)
2919{
2920 BlockDriver *bdrv_qcow = NULL;
2921 BlockDriverState *backing;
2922 QemuOpts *opts = NULL;
2923 int ret;
2924 int size = sector2cluster(s, s->sector_count);
2925 QDict *options;
2926
2927 s->used_clusters = calloc(size, 1);
2928
2929 array_init(&(s->commits), sizeof(commit_t));
2930
2931 s->qcow_filename = g_malloc(PATH_MAX);
2932 ret = get_tmp_filename(s->qcow_filename, PATH_MAX);
2933 if (ret < 0) {
2934 error_setg_errno(errp, -ret, "can't create temporary file");
2935 goto err;
2936 }
2937
2938 bdrv_qcow = bdrv_find_format("qcow");
2939 if (!bdrv_qcow) {
2940 error_setg(errp, "Failed to locate qcow driver");
2941 ret = -ENOENT;
2942 goto err;
2943 }
2944
2945 opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort);
2946 qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s->sector_count * 512,
2947 &error_abort);
2948 qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort);
2949
2950 ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
2951 qemu_opts_del(opts);
2952 if (ret < 0) {
2953 goto err;
2954 }
2955
2956 s->qcow = NULL;
2957 options = qdict_new();
2958 qdict_put(options, "driver", qstring_from_str("qcow"));
2959 ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, options,
2960 BDRV_O_RDWR | BDRV_O_NO_FLUSH, errp);
2961 if (ret < 0) {
2962 goto err;
2963 }
2964
2965#ifndef _WIN32
2966 unlink(s->qcow_filename);
2967#endif
2968
2969 backing = bdrv_new();
2970 bdrv_set_backing_hd(s->bs, backing);
2971 bdrv_unref(backing);
2972
2973 s->bs->backing->bs->drv = &vvfat_write_target;
2974 s->bs->backing->bs->opaque = g_new(void *, 1);
2975 *(void**)s->bs->backing->bs->opaque = s;
2976
2977 return 0;
2978
2979err:
2980 g_free(s->qcow_filename);
2981 s->qcow_filename = NULL;
2982 return ret;
2983}
2984
2985static void vvfat_close(BlockDriverState *bs)
2986{
2987 BDRVVVFATState *s = bs->opaque;
2988
2989 vvfat_close_current_file(s);
2990 array_free(&(s->fat));
2991 array_free(&(s->directory));
2992 array_free(&(s->mapping));
2993 g_free(s->cluster_buffer);
2994
2995 if (s->qcow) {
2996 migrate_del_blocker(s->migration_blocker);
2997 error_free(s->migration_blocker);
2998 }
2999}
3000
3001static BlockDriver bdrv_vvfat = {
3002 .format_name = "vvfat",
3003 .protocol_name = "fat",
3004 .instance_size = sizeof(BDRVVVFATState),
3005
3006 .bdrv_parse_filename = vvfat_parse_filename,
3007 .bdrv_file_open = vvfat_open,
3008 .bdrv_close = vvfat_close,
3009
3010 .bdrv_read = vvfat_co_read,
3011 .bdrv_write = vvfat_co_write,
3012 .bdrv_co_get_block_status = vvfat_co_get_block_status,
3013};
3014
3015static void bdrv_vvfat_init(void)
3016{
3017 bdrv_register(&bdrv_vvfat);
3018}
3019
3020block_init(bdrv_vvfat_init);
3021
3022#ifdef DEBUG
3023static void checkpoint(void) {
3024 assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
3025 check1(vvv);
3026 check2(vvv);
3027 assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
3028#if 0
3029 if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
3030 fprintf(stderr, "Nonono!\n");
3031 mapping_t* mapping;
3032 direntry_t* direntry;
3033 assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
3034 assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
3035 if (vvv->mapping.next<47)
3036 return;
3037 assert((mapping = array_get(&(vvv->mapping), 47)));
3038 assert(mapping->dir_index < vvv->directory.next);
3039 direntry = array_get(&(vvv->directory), mapping->dir_index);
3040 assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0);
3041#endif
3042}
3043#endif
3044