source: trunk/packages/xen-3.1/xen-3.1/tools/ioemu/block-vvfat.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 77.7 KB
Line 
1/* vim:set shiftwidth=4 ts=8: */
2/*
3 * QEMU Block driver for virtual VFAT (shadows a local directory)
4 *
5 * Copyright (c) 2004,2005 Johannes E. Schindelin
6 *
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:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
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.
24 */
25#include <sys/stat.h>
26#include <dirent.h>
27#include <assert.h>
28#include "vl.h"
29#include "block_int.h"
30
31#ifndef S_IWGRP
32#define S_IWGRP 0
33#endif
34#ifndef S_IWOTH
35#define S_IWOTH 0
36#endif
37
38/* TODO: add ":bootsector=blabla.img:" */
39/* LATER TODO: add automatic boot sector generation from
40    BOOTEASY.ASM and Ranish Partition Manager
41    Note that DOS assumes the system files to be the first files in the
42    file system (test if the boot sector still relies on that fact)! */
43/* MAYBE TODO: write block-visofs.c */
44/* TODO: call try_commit() only after a timeout */
45
46/* #define DEBUG */
47
48#ifdef DEBUG
49
50#define DLOG(a) a
51
52#undef stderr
53#define stderr STDERR
54FILE* stderr = NULL;
55
56static void checkpoint();
57
58#ifdef __MINGW32__
59void nonono(const char* file, int line, const char* msg) {
60    fprintf(stderr, "Nonono! %s:%d %s\n", file, line, msg);
61    exit(-5);
62}
63#undef assert
64#define assert(a) if (!(a)) nonono(__FILE__, __LINE__, #a)
65#endif
66
67#else
68
69#define DLOG(a)
70
71#endif
72
73/* dynamic array functions */
74typedef struct array_t {
75    char* pointer;
76    unsigned int size,next,item_size;
77} array_t;
78
79static inline void array_init(array_t* array,unsigned int item_size)
80{
81    array->pointer=0;
82    array->size=0;
83    array->next=0;
84    array->item_size=item_size;
85}
86
87static inline void array_free(array_t* array)
88{
89    if(array->pointer)
90        free(array->pointer);
91    array->size=array->next=0;
92}
93
94/* does not automatically grow */
95static inline void* array_get(array_t* array,unsigned int index) {
96    assert(index >= 0);
97    assert(index < array->next);
98    return array->pointer + index * array->item_size;
99}
100
101static inline int array_ensure_allocated(array_t* array, int index)
102{
103    if((index + 1) * array->item_size > array->size) {
104        int new_size = (index + 32) * array->item_size;
105        array->pointer = realloc(array->pointer, new_size);
106        if (!array->pointer)
107            return -1;
108        array->size = new_size;
109        array->next = index + 1;
110    }
111
112    return 0;
113}
114
115static inline void* array_get_next(array_t* array) {
116    unsigned int next = array->next;
117    void* result;
118
119    if (array_ensure_allocated(array, next) < 0)
120        return NULL;
121
122    array->next = next + 1;
123    result = array_get(array, next);
124
125    return result;
126}
127
128static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
129    if((array->next+count)*array->item_size>array->size) {
130        int increment=count*array->item_size;
131        array->pointer=realloc(array->pointer,array->size+increment);
132        if(!array->pointer)
133            return 0;
134        array->size+=increment;
135    }
136    memmove(array->pointer+(index+count)*array->item_size,
137                array->pointer+index*array->item_size,
138                (array->next-index)*array->item_size);
139    array->next+=count;
140    return array->pointer+index*array->item_size;
141}
142
143/* this performs a "roll", so that the element which was at index_from becomes
144 * index_to, but the order of all other elements is preserved. */
145static inline int array_roll(array_t* array,int index_to,int index_from,int count)
146{
147    char* buf;
148    char* from;
149    char* to;
150    int is;
151
152    if(!array ||
153            index_to<0 || index_to>=array->next ||
154            index_from<0 || index_from>=array->next)
155        return -1;
156   
157    if(index_to==index_from)
158        return 0;
159
160    is=array->item_size;
161    from=array->pointer+index_from*is;
162    to=array->pointer+index_to*is;
163    buf=malloc(is*count);
164    memcpy(buf,from,is*count);
165
166    if(index_to<index_from)
167        memmove(to+is*count,to,from-to);
168    else
169        memmove(from,from+is*count,to-from);
170   
171    memcpy(to,buf,is*count);
172
173    free(buf);
174
175    return 0;
176}
177
178inline int array_remove_slice(array_t* array,int index, int count)
179{
180    assert(index >=0);
181    assert(count > 0);
182    assert(index + count <= array->next);
183    if(array_roll(array,array->next-1,index,count))
184        return -1;
185    array->next -= count;
186    return 0;
187}
188
189int array_remove(array_t* array,int index)
190{
191    return array_remove_slice(array, index, 1);
192}
193
194/* return the index for a given member */
195int array_index(array_t* array, void* pointer)
196{
197    size_t offset = (char*)pointer - array->pointer;
198    assert(offset >= 0);
199    assert((offset % array->item_size) == 0);
200    assert(offset/array->item_size < array->next);
201    return offset/array->item_size;
202}
203
204/* These structures are used to fake a disk and the VFAT filesystem.
205 * For this reason we need to use __attribute__((packed)). */
206
207typedef struct bootsector_t {
208    uint8_t jump[3];
209    uint8_t name[8];
210    uint16_t sector_size;
211    uint8_t sectors_per_cluster;
212    uint16_t reserved_sectors;
213    uint8_t number_of_fats;
214    uint16_t root_entries;
215    uint16_t total_sectors16;
216    uint8_t media_type;
217    uint16_t sectors_per_fat;
218    uint16_t sectors_per_track;
219    uint16_t number_of_heads;
220    uint32_t hidden_sectors;
221    uint32_t total_sectors;
222    union {
223        struct {
224            uint8_t drive_number;
225            uint8_t current_head;
226            uint8_t signature;
227            uint32_t id;
228            uint8_t volume_label[11];
229        } __attribute__((packed)) fat16;
230        struct {
231            uint32_t sectors_per_fat;
232            uint16_t flags;
233            uint8_t major,minor;
234            uint32_t first_cluster_of_root_directory;
235            uint16_t info_sector;
236            uint16_t backup_boot_sector;
237            uint16_t ignored;
238        } __attribute__((packed)) fat32;
239    } u;
240    uint8_t fat_type[8];
241    uint8_t ignored[0x1c0];
242    uint8_t magic[2];
243} __attribute__((packed)) bootsector_t;
244
245typedef struct partition_t {
246    uint8_t attributes; /* 0x80 = bootable */
247    uint8_t start_head;
248    uint8_t start_sector;
249    uint8_t start_cylinder;
250    uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xb = FAT32 */
251    uint8_t end_head;
252    uint8_t end_sector;
253    uint8_t end_cylinder;
254    uint32_t start_sector_long;
255    uint32_t end_sector_long;
256} __attribute__((packed)) partition_t;
257
258typedef struct mbr_t {
259    uint8_t ignored[0x1be];
260    partition_t partition[4];
261    uint8_t magic[2];
262} __attribute__((packed)) mbr_t;
263
264typedef struct direntry_t {
265    uint8_t name[8];
266    uint8_t extension[3];
267    uint8_t attributes;
268    uint8_t reserved[2];
269    uint16_t ctime;
270    uint16_t cdate;
271    uint16_t adate;
272    uint16_t begin_hi;
273    uint16_t mtime;
274    uint16_t mdate;
275    uint16_t begin;
276    uint32_t size;
277} __attribute__((packed)) direntry_t;
278
279/* this structure are used to transparently access the files */
280
281typedef struct mapping_t {
282    /* begin is the first cluster, end is the last+1 */
283    uint32_t begin,end;
284    /* as s->directory is growable, no pointer may be used here */
285    unsigned int dir_index;
286    /* the clusters of a file may be in any order; this points to the first */
287    int first_mapping_index;
288    union {
289        /* offset is
290         * - the offset in the file (in clusters) for a file, or
291         * - the next cluster of the directory for a directory, and
292         * - the address of the buffer for a faked entry
293         */
294        struct {
295            uint32_t offset;
296        } file;
297        struct {
298            int parent_mapping_index;
299            int first_dir_index;
300        } dir;
301    } info;
302    /* path contains the full path, i.e. it always starts with s->path */
303    char* path;
304
305    enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2,
306        MODE_DIRECTORY = 4, MODE_FAKED = 8,
307        MODE_DELETED = 16, MODE_RENAMED = 32 } mode;
308    int read_only;
309} mapping_t;
310
311#ifdef DEBUG
312static void print_direntry(const struct direntry_t*);
313static void print_mapping(const struct mapping_t* mapping);
314#endif
315
316/* here begins the real VVFAT driver */
317
318typedef struct BDRVVVFATState {
319    BlockDriverState* bs; /* pointer to parent */
320    unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
321    unsigned char first_sectors[0x40*0x200];
322   
323    int fat_type; /* 16 or 32 */
324    array_t fat,directory,mapping;
325   
326    unsigned int cluster_size;
327    unsigned int sectors_per_cluster;
328    unsigned int sectors_per_fat;
329    unsigned int sectors_of_root_directory;
330    uint32_t last_cluster_of_root_directory;
331    unsigned int faked_sectors; /* how many sectors are faked before file data */
332    uint32_t sector_count; /* total number of sectors of the partition */
333    uint32_t cluster_count; /* total number of clusters of this partition */
334    uint32_t max_fat_value;
335   
336    int current_fd;
337    mapping_t* current_mapping;
338    unsigned char* cluster; /* points to current cluster */
339    unsigned char* cluster_buffer; /* points to a buffer to hold temp data */
340    unsigned int current_cluster;
341
342    /* write support */
343    BlockDriverState* write_target;
344    char* qcow_filename;
345    BlockDriverState* qcow;
346    void* fat2;
347    char* used_clusters;
348    array_t commits;
349    const char* path;
350    int downcase_short_names;
351} BDRVVVFATState;
352
353
354static int vvfat_probe(const uint8_t *buf, int buf_size, const char *filename)
355{
356    if (strstart(filename, "fat:", NULL))
357        return 100;
358    return 0;
359}
360
361static void init_mbr(BDRVVVFATState* s)
362{
363    /* TODO: if the files mbr.img and bootsect.img exist, use them */
364    mbr_t* real_mbr=(mbr_t*)s->first_sectors;
365    partition_t* partition=&(real_mbr->partition[0]);
366
367    memset(s->first_sectors,0,512);
368   
369    partition->attributes=0x80; /* bootable */
370    partition->start_head=1;
371    partition->start_sector=1;
372    partition->start_cylinder=0;
373    /* FAT12/FAT16/FAT32 */
374    partition->fs_type=(s->fat_type==12?0x1:s->fat_type==16?0x6:0xb);
375    partition->end_head=s->bs->heads-1;
376    partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */;
377    partition->end_cylinder=0xff; /* lower 8 bits of end cylinder */;
378    partition->start_sector_long=cpu_to_le32(s->bs->secs);
379    partition->end_sector_long=cpu_to_le32(s->sector_count);
380
381    real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
382}
383
384/* direntry functions */
385
386/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
387static inline int short2long_name(unsigned char* dest,const char* src)
388{
389    int i;
390    for(i=0;i<129 && src[i];i++) {
391        dest[2*i]=src[i];
392        dest[2*i+1]=0;
393    }
394    dest[2*i]=dest[2*i+1]=0;
395    for(i=2*i+2;(i%26);i++)
396        dest[i]=0xff;
397    return i;
398}
399
400static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
401{
402    char buffer[258];
403    int length=short2long_name(buffer,filename),
404        number_of_entries=(length+25)/26,i;
405    direntry_t* entry;
406
407    for(i=0;i<number_of_entries;i++) {
408        entry=array_get_next(&(s->directory));
409        entry->attributes=0xf;
410        entry->reserved[0]=0;
411        entry->begin=0;
412        entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
413    }
414    for(i=0;i<length;i++) {
415        int offset=(i%26);
416        if(offset<10) offset=1+offset;
417        else if(offset<22) offset=14+offset-10;
418        else offset=28+offset-22;
419        entry=array_get(&(s->directory),s->directory.next-1-(i/26));
420        entry->name[offset]=buffer[i];
421    }
422    return array_get(&(s->directory),s->directory.next-number_of_entries);
423}
424
425static char is_free(const direntry_t* direntry)
426{
427    /* return direntry->name[0]==0 ; */
428    return direntry->attributes == 0 || direntry->name[0]==0xe5;
429}
430
431static char is_volume_label(const direntry_t* direntry)
432{
433    return direntry->attributes == 0x28;
434}
435
436static char is_long_name(const direntry_t* direntry)
437{
438    return direntry->attributes == 0xf;
439}
440
441static char is_short_name(const direntry_t* direntry)
442{
443    return !is_volume_label(direntry) && !is_long_name(direntry)
444        && !is_free(direntry);
445}
446
447static char is_directory(const direntry_t* direntry)
448{
449    return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
450}
451
452static inline char is_dot(const direntry_t* direntry)
453{
454    return is_short_name(direntry) && direntry->name[0] == '.';
455}
456
457static char is_file(const direntry_t* direntry)
458{
459    return is_short_name(direntry) && !is_directory(direntry);
460}
461
462static inline uint32_t begin_of_direntry(const direntry_t* direntry)
463{
464    return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
465}
466
467static inline uint32_t filesize_of_direntry(const direntry_t* direntry)
468{
469    return le32_to_cpu(direntry->size);
470}
471
472static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
473{
474    direntry->begin = cpu_to_le16(begin & 0xffff);
475    direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
476}
477
478/* fat functions */
479
480static inline uint8_t fat_chksum(const direntry_t* entry)
481{
482    uint8_t chksum=0;
483    int i;
484
485    for(i=0;i<11;i++)
486        chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0))
487            +(unsigned char)entry->name[i];
488   
489    return chksum;
490}
491
492/* if return_time==0, this returns the fat_date, else the fat_time */
493static uint16_t fat_datetime(time_t time,int return_time) {
494    struct tm* t;
495#ifdef _WIN32
496    t=localtime(&time); /* this is not thread safe */
497#else
498    struct tm t1;
499    t=&t1;
500    localtime_r(&time,t);
501#endif
502    if(return_time)
503        return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
504    return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
505}
506
507static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
508{
509    if(s->fat_type==32) {
510        uint32_t* entry=array_get(&(s->fat),cluster);
511        *entry=cpu_to_le32(value);
512    } else if(s->fat_type==16) {
513        uint16_t* entry=array_get(&(s->fat),cluster);
514        *entry=cpu_to_le16(value&0xffff);
515    } else {
516        int offset = (cluster*3/2);
517        unsigned char* p = array_get(&(s->fat), offset);
518        switch (cluster&1) {
519        case 0:
520                p[0] = value&0xff;
521                p[1] = (p[1]&0xf0) | ((value>>8)&0xf);
522                break;
523        case 1:
524                p[0] = (p[0]&0xf) | ((value&0xf)<<4);
525                p[1] = (value>>4);
526                break;
527        }
528    }
529}
530
531static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
532{
533    if(s->fat_type==32) {
534        uint32_t* entry=array_get(&(s->fat),cluster);
535        return le32_to_cpu(*entry);
536    } else if(s->fat_type==16) {
537        uint16_t* entry=array_get(&(s->fat),cluster);
538        return le16_to_cpu(*entry);
539    } else {
540        const uint8_t* x=s->fat.pointer+cluster*3/2;
541        return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
542    }
543}
544
545static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
546{
547    if(fat_entry>s->max_fat_value-8)
548        return -1;
549    return 0;
550}
551
552static inline void init_fat(BDRVVVFATState* s)
553{
554    if (s->fat_type == 12) {
555        array_init(&(s->fat),1);
556        array_ensure_allocated(&(s->fat),
557                s->sectors_per_fat * 0x200 * 3 / 2 - 1);
558    } else {
559        array_init(&(s->fat),(s->fat_type==32?4:2));
560        array_ensure_allocated(&(s->fat),
561                s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
562    }
563    memset(s->fat.pointer,0,s->fat.size);
564   
565    switch(s->fat_type) {
566        case 12: s->max_fat_value=0xfff; break;
567        case 16: s->max_fat_value=0xffff; break;
568        case 32: s->max_fat_value=0x0fffffff; break;
569        default: s->max_fat_value=0; /* error... */
570    }
571
572}
573
574/* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */
575/* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */
576static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
577        unsigned int directory_start, const char* filename, int is_dot)
578{
579    int i,j,long_index=s->directory.next;
580    direntry_t* entry=0;
581    direntry_t* entry_long=0;
582
583    if(is_dot) {
584        entry=array_get_next(&(s->directory));
585        memset(entry->name,0x20,11);
586        memcpy(entry->name,filename,strlen(filename));
587        return entry;
588    }
589   
590    entry_long=create_long_filename(s,filename);
591 
592    i = strlen(filename); 
593    for(j = i - 1; j>0  && filename[j]!='.';j--);
594    if (j > 0)
595        i = (j > 8 ? 8 : j);
596    else if (i > 8)
597        i = 8;
598
599    entry=array_get_next(&(s->directory));
600    memset(entry->name,0x20,11);
601    strncpy(entry->name,filename,i);
602   
603    if(j > 0)
604        for (i = 0; i < 3 && filename[j+1+i]; i++)
605            entry->extension[i] = filename[j+1+i];
606
607    /* upcase & remove unwanted characters */
608    for(i=10;i>=0;i--) {
609        if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--);
610        if(entry->name[i]<=' ' || entry->name[i]>0x7f
611                || strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
612            entry->name[i]='_';
613        else if(entry->name[i]>='a' && entry->name[i]<='z')
614            entry->name[i]+='A'-'a';
615    }
616
617    /* mangle duplicates */
618    while(1) {
619        direntry_t* entry1=array_get(&(s->directory),directory_start);
620        int j;
621
622        for(;entry1<entry;entry1++)
623            if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11))
624                break; /* found dupe */
625        if(entry1==entry) /* no dupe found */
626            break;
627
628        /* use all 8 characters of name */     
629        if(entry->name[7]==' ') {
630            int j;
631            for(j=6;j>0 && entry->name[j]==' ';j--)
632                entry->name[j]='~';
633        }
634
635        /* increment number */
636        for(j=7;j>0 && entry->name[j]=='9';j--)
637            entry->name[j]='0';
638        if(j>0) {
639            if(entry->name[j]<'0' || entry->name[j]>'9')
640                entry->name[j]='0';
641            else
642                entry->name[j]++;
643        }
644    }
645
646    /* calculate checksum; propagate to long name */
647    if(entry_long) {
648        uint8_t chksum=fat_chksum(entry);
649
650        /* calculate anew, because realloc could have taken place */
651        entry_long=array_get(&(s->directory),long_index);
652        while(entry_long<entry && is_long_name(entry_long)) {
653            entry_long->reserved[1]=chksum;
654            entry_long++;
655        }
656    }
657
658    return entry;
659}
660
661/*
662 * Read a directory. (the index of the corresponding mapping must be passed).
663 */
664static int read_directory(BDRVVVFATState* s, int mapping_index)
665{
666    mapping_t* mapping = array_get(&(s->mapping), mapping_index);
667    direntry_t* direntry;
668    const char* dirname = mapping->path;
669    int first_cluster = mapping->begin;
670    int parent_index = mapping->info.dir.parent_mapping_index;
671    mapping_t* parent_mapping = (mapping_t*)
672        (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : 0);
673    int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
674
675    DIR* dir=opendir(dirname);
676    struct dirent* entry;
677    int i;
678
679    assert(mapping->mode & MODE_DIRECTORY);
680
681    if(!dir) {
682        mapping->end = mapping->begin;
683        return -1;
684    }
685   
686    i = mapping->info.dir.first_dir_index =
687            first_cluster == 0 ? 0 : s->directory.next;
688
689    /* actually read the directory, and allocate the mappings */ 
690    while((entry=readdir(dir))) {
691        unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
692        char* buffer;
693        direntry_t* direntry;
694        struct stat st;
695        int is_dot=!strcmp(entry->d_name,".");
696        int is_dotdot=!strcmp(entry->d_name,"..");
697
698        if(first_cluster == 0 && (is_dotdot || is_dot))
699            continue;
700       
701        buffer=(char*)malloc(length);
702        assert(buffer);
703        snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
704
705        if(stat(buffer,&st)<0) {
706            free(buffer);
707            continue;
708        }
709
710        /* create directory entry for this file */
711        direntry=create_short_and_long_name(s, i, entry->d_name,
712                is_dot || is_dotdot);
713        direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
714        direntry->reserved[0]=direntry->reserved[1]=0;
715        direntry->ctime=fat_datetime(st.st_ctime,1);
716        direntry->cdate=fat_datetime(st.st_ctime,0);
717        direntry->adate=fat_datetime(st.st_atime,0);
718        direntry->begin_hi=0;
719        direntry->mtime=fat_datetime(st.st_mtime,1);
720        direntry->mdate=fat_datetime(st.st_mtime,0);
721        if(is_dotdot)
722            set_begin_of_direntry(direntry, first_cluster_of_parent);
723        else if(is_dot)
724            set_begin_of_direntry(direntry, first_cluster);
725        else
726            direntry->begin=0; /* do that later */
727        if (st.st_size > 0x7fffffff) {
728            fprintf(stderr, "File %s is larger than 2GB\n", buffer);
729            free(buffer);
730            return -2;
731        }
732        direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
733
734        /* create mapping for this file */
735        if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
736            s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
737            s->current_mapping->begin=0;
738            s->current_mapping->end=st.st_size;
739            /*
740             * we get the direntry of the most recent direntry, which
741             * contains the short name and all the relevant information.
742             */
743            s->current_mapping->dir_index=s->directory.next-1;
744            s->current_mapping->first_mapping_index = -1;
745            if (S_ISDIR(st.st_mode)) {
746                s->current_mapping->mode = MODE_DIRECTORY;
747                s->current_mapping->info.dir.parent_mapping_index =
748                    mapping_index;
749            } else {
750                s->current_mapping->mode = MODE_UNDEFINED;
751                s->current_mapping->info.file.offset = 0;
752            }
753            s->current_mapping->path=buffer;
754            s->current_mapping->read_only =
755                (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
756        }
757    }
758    closedir(dir);
759
760    /* fill with zeroes up to the end of the cluster */
761    while(s->directory.next%(0x10*s->sectors_per_cluster)) {
762        direntry_t* direntry=array_get_next(&(s->directory));
763        memset(direntry,0,sizeof(direntry_t));
764    }
765
766/* TODO: if there are more entries, bootsector has to be adjusted! */
767#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster)
768    if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) {
769        /* root directory */
770        int cur = s->directory.next;
771        array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
772        memset(array_get(&(s->directory), cur), 0,
773                (ROOT_ENTRIES - cur) * sizeof(direntry_t));
774    }
775       
776     /* reget the mapping, since s->mapping was possibly realloc()ed */
777    mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
778    first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
779        * 0x20 / s->cluster_size;
780    mapping->end = first_cluster;
781
782    direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
783    set_begin_of_direntry(direntry, mapping->begin);
784   
785    return 0;
786}
787
788static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
789{
790    return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
791}
792
793static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
794{
795    return s->faked_sectors + s->sectors_per_cluster * cluster_num;
796}
797
798static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num)
799{
800    return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster;
801}
802
803#ifdef DBG
804static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping)
805{
806    if(mapping->mode==MODE_UNDEFINED)
807        return 0;
808    return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index);
809}
810#endif
811
812static int init_directories(BDRVVVFATState* s,
813        const char* dirname)
814{
815    bootsector_t* bootsector;
816    mapping_t* mapping;
817    unsigned int i;
818    unsigned int cluster;
819
820    memset(&(s->first_sectors[0]),0,0x40*0x200);
821
822    s->cluster_size=s->sectors_per_cluster*0x200;
823    s->cluster_buffer=malloc(s->cluster_size);
824    assert(s->cluster_buffer);
825
826    /*
827     * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
828     * where sc is sector_count,
829     * spf is sectors_per_fat,
830     * spc is sectors_per_clusters, and
831     * fat_type = 12, 16 or 32.
832     */
833    i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
834    s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
835   
836    array_init(&(s->mapping),sizeof(mapping_t));
837    array_init(&(s->directory),sizeof(direntry_t));
838
839    /* add volume label */
840    {
841        direntry_t* entry=array_get_next(&(s->directory));
842        entry->attributes=0x28; /* archive | volume label */
843        snprintf(entry->name,11,"QEMU VVFAT");
844    }
845
846    /* Now build FAT, and write back information into directory */
847    init_fat(s);
848
849    s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
850    s->cluster_count=sector2cluster(s, s->sector_count);
851
852    mapping = array_get_next(&(s->mapping));
853    mapping->begin = 0;
854    mapping->dir_index = 0;
855    mapping->info.dir.parent_mapping_index = -1;
856    mapping->first_mapping_index = -1;
857    mapping->path = strdup(dirname);
858    i = strlen(mapping->path);
859    if (i > 0 && mapping->path[i - 1] == '/')
860        mapping->path[i - 1] = '\0';
861    mapping->mode = MODE_DIRECTORY;
862    mapping->read_only = 0;
863    s->path = mapping->path;
864
865    for (i = 0, cluster = 0; i < s->mapping.next; i++) {
866        int j;
867        /* MS-DOS expects the FAT to be 0 for the root directory
868         * (except for the media byte). */
869        /* LATER TODO: still true for FAT32? */
870        int fix_fat = (i != 0);
871        mapping = array_get(&(s->mapping), i);
872
873        if (mapping->mode & MODE_DIRECTORY) {
874            mapping->begin = cluster;
875            if(read_directory(s, i)) {
876                fprintf(stderr, "Could not read directory %s\n",
877                        mapping->path);
878                return -1;
879            }
880            mapping = array_get(&(s->mapping), i);
881        } else {
882            assert(mapping->mode == MODE_UNDEFINED);
883            mapping->mode=MODE_NORMAL;
884            mapping->begin = cluster;
885            if (mapping->end > 0) {
886                direntry_t* direntry = array_get(&(s->directory),
887                        mapping->dir_index);
888
889                mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
890                set_begin_of_direntry(direntry, mapping->begin);
891            } else {
892                mapping->end = cluster + 1;
893                fix_fat = 0;
894            }
895        }
896
897        assert(mapping->begin < mapping->end);
898
899        /* fix fat for entry */
900        if (fix_fat) {
901            for(j = mapping->begin; j < mapping->end - 1; j++)
902                fat_set(s, j, j+1);
903            fat_set(s, mapping->end - 1, s->max_fat_value);
904        }
905
906        /* next free cluster */
907        cluster = mapping->end;
908
909        if(cluster > s->cluster_count) {
910            fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type);
911            return -1;
912        }
913    }
914
915    mapping = array_get(&(s->mapping), 0);
916    s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
917    s->last_cluster_of_root_directory = mapping->end;
918
919    /* the FAT signature */
920    fat_set(s,0,s->max_fat_value);
921    fat_set(s,1,s->max_fat_value);
922
923    s->current_mapping = NULL;
924
925    bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
926    bootsector->jump[0]=0xeb;
927    bootsector->jump[1]=0x3e;
928    bootsector->jump[2]=0x90;
929    memcpy(bootsector->name,"QEMU    ",8);
930    bootsector->sector_size=cpu_to_le16(0x200);
931    bootsector->sectors_per_cluster=s->sectors_per_cluster;
932    bootsector->reserved_sectors=cpu_to_le16(1);
933    bootsector->number_of_fats=0x2; /* number of FATs */
934    bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
935    bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
936    bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
937    s->fat.pointer[0] = bootsector->media_type;
938    bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
939    bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
940    bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
941    bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
942    bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
943
944    /* LATER TODO: if FAT32, this is wrong */
945    bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
946    bootsector->u.fat16.current_head=0;
947    bootsector->u.fat16.signature=0x29;
948    bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
949
950    memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
951    memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12   ":s->fat_type==16?"FAT16   ":"FAT32   "),8);
952    bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
953
954    return 0;
955}
956
957static BDRVVVFATState *vvv = NULL;
958
959static int enable_write_target(BDRVVVFATState *s);
960static int is_consistent(BDRVVVFATState *s);
961
962static int vvfat_open(BlockDriverState *bs, const char* dirname)
963{
964    BDRVVVFATState *s = bs->opaque;
965    int floppy = 0;
966    int i;
967
968    vvv = s;
969
970DLOG(if (stderr == NULL) {
971    stderr = fopen("vvfat.log", "a");
972    setbuf(stderr, NULL);
973})
974
975    s->bs = bs;
976
977    s->fat_type=16;
978    /* LATER TODO: if FAT32, adjust */
979    s->sector_count=0xec04f;
980    s->sectors_per_cluster=0x10;
981    /* LATER TODO: this could be wrong for FAT32 */
982    bs->cyls=1023; bs->heads=15; bs->secs=63;
983
984    s->current_cluster=0xffffffff;
985
986    s->first_sectors_number=0x40;
987    /* read only is the default for safety */
988    bs->read_only = 1;
989    s->qcow = s->write_target = NULL;
990    s->qcow_filename = NULL;
991    s->fat2 = NULL;
992    s->downcase_short_names = 1;
993   
994    if (!strstart(dirname, "fat:", NULL))
995        return -1;
996
997    if (strstr(dirname, ":rw:")) {
998        if (enable_write_target(s))
999            return -1;
1000        bs->read_only = 0;
1001    }
1002
1003    if (strstr(dirname, ":floppy:")) {
1004        floppy = 1;
1005        s->fat_type = 12;
1006        s->first_sectors_number = 1;
1007        s->sectors_per_cluster=2;
1008        bs->cyls = 80; bs->heads = 2; bs->secs = 36;
1009    }
1010
1011    if (strstr(dirname, ":32:")) {
1012        fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
1013        s->fat_type = 32;
1014    } else if (strstr(dirname, ":16:")) {
1015        s->fat_type = 16;
1016    } else if (strstr(dirname, ":12:")) {
1017        s->fat_type = 12;
1018        s->sector_count=2880;
1019    }
1020
1021    i = strrchr(dirname, ':') - dirname;
1022    assert(i >= 3);
1023    if (dirname[i-2] == ':' && isalpha(dirname[i-1]))
1024        /* workaround for DOS drive names */
1025        dirname += i-1;
1026    else
1027        dirname += i+1;
1028
1029    bs->total_sectors=bs->cyls*bs->heads*bs->secs;
1030    if (s->sector_count > bs->total_sectors)
1031        s->sector_count = bs->total_sectors;
1032    if(init_directories(s, dirname))
1033        return -1;
1034
1035    if(s->first_sectors_number==0x40)
1036        init_mbr(s);
1037
1038    /* for some reason or other, MS-DOS does not like to know about CHS... */
1039    if (floppy)
1040        bs->heads = bs->cyls = bs->secs = 0;
1041
1042    //    assert(is_consistent(s));
1043
1044    return 0;
1045}
1046
1047static inline void vvfat_close_current_file(BDRVVVFATState *s)
1048{
1049    if(s->current_mapping) {
1050        s->current_mapping = NULL;
1051        if (s->current_fd) {
1052                close(s->current_fd);
1053                s->current_fd = 0;
1054        }
1055    }
1056    s->current_cluster = -1;
1057}
1058
1059/* mappings between index1 and index2-1 are supposed to be ordered
1060 * return value is the index of the last mapping for which end>cluster_num
1061 */
1062static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
1063{
1064    int index3=index1+1;
1065    while(1) {
1066        mapping_t* mapping;
1067        index3=(index1+index2)/2;
1068        mapping=array_get(&(s->mapping),index3);
1069        assert(mapping->begin < mapping->end);
1070        if(mapping->begin>=cluster_num) {
1071            assert(index2!=index3 || index2==0);
1072            if(index2==index3)
1073                return index1;
1074            index2=index3;
1075        } else {
1076            if(index1==index3)
1077                return mapping->end<=cluster_num ? index2 : index1;
1078            index1=index3;
1079        }
1080        assert(index1<=index2);
1081        DLOG(mapping=array_get(&(s->mapping),index1);
1082        assert(mapping->begin<=cluster_num);
1083        assert(index2 >= s->mapping.next || 
1084                ((mapping = array_get(&(s->mapping),index2)) &&
1085                mapping->end>cluster_num)));
1086    }
1087}
1088
1089static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
1090{
1091    int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
1092    mapping_t* mapping;
1093    if(index>=s->mapping.next)
1094        return 0;
1095    mapping=array_get(&(s->mapping),index);
1096    if(mapping->begin>cluster_num)
1097        return 0;
1098    assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
1099    return mapping;
1100}
1101
1102/*
1103 * This function simply compares path == mapping->path. Since the mappings
1104 * are sorted by cluster, this is expensive: O(n).
1105 */
1106static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s,
1107        const char* path)
1108{
1109    int i;
1110
1111    for (i = 0; i < s->mapping.next; i++) {
1112        mapping_t* mapping = array_get(&(s->mapping), i);
1113        if (mapping->first_mapping_index < 0 &&
1114                !strcmp(path, mapping->path))
1115            return mapping;
1116    }
1117
1118    return NULL;
1119}
1120
1121static int open_file(BDRVVVFATState* s,mapping_t* mapping)
1122{
1123    if(!mapping)
1124        return -1;
1125    if(!s->current_mapping ||
1126            strcmp(s->current_mapping->path,mapping->path)) {
1127        /* open file */
1128        int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
1129        if(fd<0)
1130            return -1;
1131        vvfat_close_current_file(s);
1132        s->current_fd = fd;
1133        s->current_mapping = mapping;
1134    }
1135    return 0;
1136}
1137
1138static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
1139{
1140    if(s->current_cluster != cluster_num) {
1141        int result=0;
1142        off_t offset;
1143        assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
1144        if(!s->current_mapping
1145                || s->current_mapping->begin>cluster_num
1146                || s->current_mapping->end<=cluster_num) {
1147            /* binary search of mappings for file */
1148            mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
1149
1150            assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
1151
1152            if (mapping && mapping->mode & MODE_DIRECTORY) {
1153                vvfat_close_current_file(s);
1154                s->current_mapping = mapping;
1155read_cluster_directory:
1156                offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
1157                s->cluster = s->directory.pointer+offset
1158                        + 0x20*s->current_mapping->info.dir.first_dir_index;
1159                assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
1160                assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
1161                s->current_cluster = cluster_num;
1162                return 0;
1163            }
1164
1165            if(open_file(s,mapping))
1166                return -2;
1167        } else if (s->current_mapping->mode & MODE_DIRECTORY)
1168            goto read_cluster_directory;
1169
1170        assert(s->current_fd);
1171
1172        offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
1173        if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
1174            return -3;
1175        s->cluster=s->cluster_buffer;
1176        result=read(s->current_fd,s->cluster,s->cluster_size);
1177        if(result<0) {
1178            s->current_cluster = -1;
1179            return -1;
1180        }
1181        s->current_cluster = cluster_num;
1182    }
1183    return 0;
1184}
1185
1186#ifdef DEBUG
1187static void hexdump(const void* address, uint32_t len)
1188{
1189    const unsigned char* p = address;
1190    int i, j;
1191
1192    for (i = 0; i < len; i += 16) {
1193        for (j = 0; j < 16 && i + j < len; j++)
1194            fprintf(stderr, "%02x ", p[i + j]);
1195        for (; j < 16; j++)
1196            fprintf(stderr, "   ");
1197        fprintf(stderr, " ");
1198        for (j = 0; j < 16 && i + j < len; j++)
1199            fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
1200        fprintf(stderr, "\n");
1201    }
1202}
1203
1204static void print_direntry(const direntry_t* direntry)
1205{
1206    int j = 0;
1207    char buffer[1024];
1208
1209    fprintf(stderr, "direntry 0x%x: ", (int)direntry);
1210    if(!direntry)
1211        return;
1212    if(is_long_name(direntry)) {
1213        unsigned char* c=(unsigned char*)direntry;
1214        int i;
1215        for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
1216#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = '°'; j++;}
1217            ADD_CHAR(c[i]);
1218        for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
1219            ADD_CHAR(c[i]);
1220        for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
1221            ADD_CHAR(c[i]);
1222        buffer[j] = 0;
1223        fprintf(stderr, "%s\n", buffer);
1224    } else {
1225        int i;
1226        for(i=0;i<11;i++)
1227            ADD_CHAR(direntry->name[i]);
1228        buffer[j] = 0;
1229        fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
1230                buffer,
1231                direntry->attributes,
1232                begin_of_direntry(direntry),le32_to_cpu(direntry->size));
1233    }
1234}
1235
1236static void print_mapping(const mapping_t* mapping)
1237{
1238    fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode);
1239    if (mapping->mode & MODE_DIRECTORY)
1240        fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
1241    else
1242        fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
1243}
1244#endif
1245
1246static int vvfat_read(BlockDriverState *bs, int64_t sector_num, 
1247                    uint8_t *buf, int nb_sectors)
1248{
1249    BDRVVVFATState *s = bs->opaque;
1250    int i;
1251
1252    for(i=0;i<nb_sectors;i++,sector_num++) {
1253        if (sector_num >= s->sector_count)
1254           return -1;
1255        if (s->qcow) {
1256            int n;
1257            if (s->qcow->drv->bdrv_is_allocated(s->qcow,
1258                        sector_num, nb_sectors-i, &n)) {
1259DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
1260                if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
1261                    return -1;
1262                i += n - 1;
1263                sector_num += n - 1;
1264                continue;
1265            }
1266DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
1267        }
1268        if(sector_num<s->faked_sectors) {
1269            if(sector_num<s->first_sectors_number)
1270                memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
1271            else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
1272                memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
1273            else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
1274                memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
1275        } else {
1276            uint32_t sector=sector_num-s->faked_sectors,
1277            sector_offset_in_cluster=(sector%s->sectors_per_cluster),
1278            cluster_num=sector/s->sectors_per_cluster;
1279            if(read_cluster(s, cluster_num) != 0) {
1280                /* LATER TODO: strict: return -1; */
1281                memset(buf+i*0x200,0,0x200);
1282                continue;
1283            }
1284            memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
1285        }
1286    }
1287    return 0;
1288}
1289
1290/* LATER TODO: statify all functions */
1291
1292/*
1293 * Idea of the write support (use snapshot):
1294 *
1295 * 1. check if all data is consistent, recording renames, modifications,
1296 *    new files and directories (in s->commits).
1297 *
1298 * 2. if the data is not consistent, stop committing
1299 *
1300 * 3. handle renames, and create new files and directories (do not yet
1301 *    write their contents)
1302 *
1303 * 4. walk the directories, fixing the mapping and direntries, and marking
1304 *    the handled mappings as not deleted
1305 *
1306 * 5. commit the contents of the files
1307 *
1308 * 6. handle deleted files and directories
1309 *
1310 */
1311
1312typedef struct commit_t {
1313    char* path;
1314    union {
1315        struct { uint32_t cluster; } rename;
1316        struct { int dir_index; uint32_t modified_offset; } writeout;
1317        struct { uint32_t first_cluster; } new_file;
1318        struct { uint32_t cluster; } mkdir;
1319    } param;
1320    /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
1321    enum {
1322        ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
1323    } action;
1324} commit_t;
1325
1326static void clear_commits(BDRVVVFATState* s)
1327{
1328    int i;
1329DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
1330    for (i = 0; i < s->commits.next; i++) {
1331        commit_t* commit = array_get(&(s->commits), i);
1332        assert(commit->path || commit->action == ACTION_WRITEOUT);
1333        if (commit->action != ACTION_WRITEOUT) {
1334            assert(commit->path);
1335            free(commit->path);
1336        } else
1337            assert(commit->path == NULL);
1338    }
1339    s->commits.next = 0;
1340}
1341
1342static void schedule_rename(BDRVVVFATState* s,
1343        uint32_t cluster, char* new_path)
1344{
1345    commit_t* commit = array_get_next(&(s->commits));
1346    commit->path = new_path;
1347    commit->param.rename.cluster = cluster;
1348    commit->action = ACTION_RENAME;
1349}
1350
1351static void schedule_writeout(BDRVVVFATState* s,
1352        int dir_index, uint32_t modified_offset)
1353{
1354    commit_t* commit = array_get_next(&(s->commits));
1355    commit->path = NULL;
1356    commit->param.writeout.dir_index = dir_index;
1357    commit->param.writeout.modified_offset = modified_offset;
1358    commit->action = ACTION_WRITEOUT;
1359}
1360
1361static void schedule_new_file(BDRVVVFATState* s,
1362        char* path, uint32_t first_cluster)
1363{
1364    commit_t* commit = array_get_next(&(s->commits));
1365    commit->path = path;
1366    commit->param.new_file.first_cluster = first_cluster;
1367    commit->action = ACTION_NEW_FILE;
1368}
1369
1370static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
1371{
1372    commit_t* commit = array_get_next(&(s->commits));
1373    commit->path = path;
1374    commit->param.mkdir.cluster = cluster;
1375    commit->action = ACTION_MKDIR;
1376}
1377
1378typedef struct {
1379    unsigned char name[1024];
1380    int checksum, len;
1381    int sequence_number;
1382} long_file_name;
1383
1384static void lfn_init(long_file_name* lfn)
1385{
1386   lfn->sequence_number = lfn->len = 0;
1387   lfn->checksum = 0x100;
1388}
1389
1390/* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
1391static int parse_long_name(long_file_name* lfn,
1392        const direntry_t* direntry)
1393{
1394    int i, j, offset;
1395    const unsigned char* pointer = (const unsigned char*)direntry;
1396
1397    if (!is_long_name(direntry))
1398        return 1;
1399
1400    if (pointer[0] & 0x40) {
1401        lfn->sequence_number = pointer[0] & 0x3f;
1402        lfn->checksum = pointer[13];
1403        lfn->name[0] = 0;
1404    } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
1405        return -1;
1406    else if (pointer[13] != lfn->checksum)
1407        return -2;
1408    else if (pointer[12] || pointer[26] || pointer[27])
1409        return -3;
1410
1411    offset = 13 * (lfn->sequence_number - 1);
1412    for (i = 0, j = 1; i < 13; i++, j+=2) {
1413        if (j == 11)
1414            j = 14;
1415        else if (j == 26)
1416            j = 28;
1417
1418        if (pointer[j+1] == 0)
1419            lfn->name[offset + i] = pointer[j];
1420        else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
1421            return -4;
1422        else
1423            lfn->name[offset + i] = 0;
1424    }
1425
1426    if (pointer[0] & 0x40)
1427        lfn->len = offset + strlen(lfn->name + offset);
1428
1429    return 0;
1430}
1431
1432/* returns 0 if successful, >0 if no short_name, and <0 on error */
1433static int parse_short_name(BDRVVVFATState* s,
1434        long_file_name* lfn, direntry_t* direntry)
1435{
1436    int i, j;
1437
1438    if (!is_short_name(direntry))
1439        return 1;
1440
1441    for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
1442    for (i = 0; i <= j; i++) {
1443        if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
1444            return -1;
1445        else if (s->downcase_short_names)
1446            lfn->name[i] = tolower(direntry->name[i]);
1447        else
1448            lfn->name[i] = direntry->name[i];
1449    }
1450
1451    for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
1452    if (j >= 0) {
1453        lfn->name[i++] = '.';
1454        lfn->name[i + j + 1] = '\0';
1455        for (;j >= 0; j--) {
1456            if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
1457                return -2;
1458            else if (s->downcase_short_names)
1459                lfn->name[i + j] = tolower(direntry->extension[j]);
1460            else
1461                lfn->name[i + j] = direntry->extension[j];
1462        }
1463    } else
1464        lfn->name[i + j + 1] = '\0';
1465
1466    lfn->len = strlen(lfn->name);
1467
1468    return 0;
1469}
1470
1471static inline uint32_t modified_fat_get(BDRVVVFATState* s,
1472        unsigned int cluster)
1473{
1474    if (cluster < s->last_cluster_of_root_directory) {
1475        if (cluster + 1 == s->last_cluster_of_root_directory)
1476            return s->max_fat_value;
1477        else
1478            return cluster + 1;
1479    }
1480
1481    if (s->fat_type==32) {
1482        uint32_t* entry=((uint32_t*)s->fat2)+cluster;
1483        return le32_to_cpu(*entry);
1484    } else if (s->fat_type==16) {
1485        uint16_t* entry=((uint16_t*)s->fat2)+cluster;
1486        return le16_to_cpu(*entry);
1487    } else {
1488        const uint8_t* x=s->fat2+cluster*3/2;
1489        return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
1490    }
1491}
1492
1493static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
1494{
1495    int was_modified = 0;
1496    int i, dummy;
1497
1498    if (s->qcow == NULL)
1499        return 0;
1500
1501    for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
1502        was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
1503                cluster2sector(s, cluster_num) + i, 1, &dummy);
1504
1505    return was_modified;
1506}
1507
1508static const char* get_basename(const char* path)
1509{
1510    char* basename = strrchr(path, '/');
1511    if (basename == NULL)
1512        return path;
1513    else
1514        return basename + 1; /* strip '/' */
1515}
1516
1517/*
1518 * The array s->used_clusters holds the states of the clusters. If it is
1519 * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
1520 * was modified, bit 3 is set.
1521 * If any cluster is allocated, but not part of a file or directory, this
1522 * driver refuses to commit.
1523 */
1524typedef enum {
1525     USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
1526} used_t;
1527
1528/*
1529 * get_cluster_count_for_direntry() not only determines how many clusters
1530 * are occupied by direntry, but also if it was renamed or modified.
1531 *
1532 * A file is thought to be renamed *only* if there already was a file with
1533 * exactly the same first cluster, but a different name.
1534 *
1535 * Further, the files/directories handled by this function are
1536 * assumed to be *not* deleted (and *only* those).
1537 */
1538static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
1539        direntry_t* direntry, const char* path)
1540{
1541    /*
1542     * This is a little bit tricky:
1543     * IF the guest OS just inserts a cluster into the file chain,
1544     * and leaves the rest alone, (i.e. the original file had clusters
1545     * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
1546     *
1547     * - do_commit will write the cluster into the file at the given
1548     *   offset, but
1549     *
1550     * - the cluster which is overwritten should be moved to a later
1551     *   position in the file.
1552     *
1553     * I am not aware that any OS does something as braindead, but this
1554     * situation could happen anyway when not committing for a long time.
1555     * Just to be sure that this does not bite us, detect it, and copy the
1556     * contents of the clusters to-be-overwritten into the qcow.
1557     */
1558    int copy_it = 0;
1559    int was_modified = 0;
1560    int32_t ret = 0;
1561
1562    uint32_t cluster_num = begin_of_direntry(direntry);
1563    uint32_t offset = 0;
1564    int first_mapping_index = -1;
1565    mapping_t* mapping = NULL;
1566    const char* basename2 = NULL;
1567
1568    vvfat_close_current_file(s);
1569
1570    /* the root directory */
1571    if (cluster_num == 0)
1572        return 0;
1573
1574    /* write support */
1575    if (s->qcow) {
1576        basename2 = get_basename(path);
1577
1578        mapping = find_mapping_for_cluster(s, cluster_num);
1579
1580        if (mapping) {
1581            const char* basename;
1582
1583            assert(mapping->mode & MODE_DELETED);
1584            mapping->mode &= ~MODE_DELETED;
1585
1586            basename = get_basename(mapping->path);
1587
1588            assert(mapping->mode & MODE_NORMAL);
1589
1590            /* rename */
1591            if (strcmp(basename, basename2))
1592                schedule_rename(s, cluster_num, strdup(path));
1593        } else if (is_file(direntry))
1594            /* new file */
1595            schedule_new_file(s, strdup(path), cluster_num);
1596        else {
1597            assert(0);
1598            return 0;
1599        }
1600    }
1601
1602    while(1) {
1603        if (s->qcow) {
1604            if (!copy_it && cluster_was_modified(s, cluster_num)) {
1605                if (mapping == NULL ||
1606                        mapping->begin > cluster_num ||
1607                        mapping->end <= cluster_num)
1608                mapping = find_mapping_for_cluster(s, cluster_num);
1609
1610
1611                if (mapping &&
1612                        (mapping->mode & MODE_DIRECTORY) == 0) {
1613
1614                    /* was modified in qcow */
1615                    if (offset != mapping->info.file.offset + s->cluster_size
1616                            * (cluster_num - mapping->begin)) {
1617                        /* offset of this cluster in file chain has changed */
1618                        assert(0);
1619                        copy_it = 1;
1620                    } else if (offset == 0) {
1621                        const char* basename = get_basename(mapping->path);
1622
1623                        if (strcmp(basename, basename2))
1624                            copy_it = 1;
1625                        first_mapping_index = array_index(&(s->mapping), mapping);
1626                    }
1627
1628                    if (mapping->first_mapping_index != first_mapping_index
1629                            && mapping->info.file.offset > 0) {
1630                        assert(0);
1631                        copy_it = 1;
1632                    }
1633
1634                    /* need to write out? */
1635                    if (!was_modified && is_file(direntry)) {
1636                        was_modified = 1;
1637                        schedule_writeout(s, mapping->dir_index, offset);
1638                    }
1639                }
1640            }
1641
1642            if (copy_it) {
1643                int i, dummy;
1644                /*
1645                 * This is horribly inefficient, but that is okay, since
1646                 * it is rarely executed, if at all.
1647                 */
1648                int64_t offset = cluster2sector(s, cluster_num);
1649
1650                vvfat_close_current_file(s);
1651                for (i = 0; i < s->sectors_per_cluster; i++)
1652                    if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
1653                                offset + i, 1, &dummy)) {
1654                        if (vvfat_read(s->bs,
1655                                    offset, s->cluster_buffer, 1))
1656                            return -1;
1657                        if (s->qcow->drv->bdrv_write(s->qcow,
1658                                    offset, s->cluster_buffer, 1))
1659                            return -2;
1660                    }
1661            }
1662        }
1663
1664        ret++;
1665        if (s->used_clusters[cluster_num] & USED_ANY)
1666            return 0;
1667        s->used_clusters[cluster_num] = USED_FILE;
1668
1669        cluster_num = modified_fat_get(s, cluster_num);
1670
1671        if (fat_eof(s, cluster_num))
1672            return ret;
1673        else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
1674            return -1;
1675
1676        offset += s->cluster_size;
1677    }
1678}
1679
1680/*
1681 * This function looks at the modified data (qcow).
1682 * It returns 0 upon inconsistency or error, and the number of clusters
1683 * used by the directory, its subdirectories and their files.
1684 */
1685static int check_directory_consistency(BDRVVVFATState *s,
1686        int cluster_num, const char* path)
1687{
1688    int ret = 0;
1689    unsigned char* cluster = malloc(s->cluster_size);
1690    direntry_t* direntries = (direntry_t*)cluster;
1691    mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
1692
1693    long_file_name lfn;
1694    int path_len = strlen(path);
1695    char path2[PATH_MAX];
1696
1697    assert(path_len < PATH_MAX); /* len was tested before! */
1698    strcpy(path2, path);
1699    path2[path_len] = '/';
1700    path2[path_len + 1] = '\0';
1701
1702    if (mapping) {
1703        const char* basename = get_basename(mapping->path);
1704        const char* basename2 = get_basename(path);
1705
1706        assert(mapping->mode & MODE_DIRECTORY);
1707
1708        assert(mapping->mode & MODE_DELETED);
1709        mapping->mode &= ~MODE_DELETED;
1710
1711        if (strcmp(basename, basename2))
1712            schedule_rename(s, cluster_num, strdup(path));
1713    } else
1714        /* new directory */
1715        schedule_mkdir(s, cluster_num, strdup(path));
1716               
1717    lfn_init(&lfn);
1718    do {
1719        int i;
1720        int subret = 0;
1721
1722        ret++;
1723
1724        if (s->used_clusters[cluster_num] & USED_ANY) {
1725            fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
1726            return 0;
1727        }
1728        s->used_clusters[cluster_num] = USED_DIRECTORY;
1729
1730DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
1731        subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
1732                s->sectors_per_cluster);
1733        if (subret) {
1734            fprintf(stderr, "Error fetching direntries\n");
1735        fail:
1736            free(cluster);
1737            return 0;
1738        }
1739
1740        for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
1741            int cluster_count;
1742
1743DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i));
1744            if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
1745                    is_free(direntries + i))
1746                continue;
1747
1748            subret = parse_long_name(&lfn, direntries + i);
1749            if (subret < 0) {
1750                fprintf(stderr, "Error in long name\n");
1751                goto fail;
1752            }
1753            if (subret == 0 || is_free(direntries + i))
1754                continue;
1755
1756            if (fat_chksum(direntries+i) != lfn.checksum) {
1757                subret = parse_short_name(s, &lfn, direntries + i);
1758                if (subret < 0) {
1759                    fprintf(stderr, "Error in short name (%d)\n", subret);
1760                    goto fail;
1761                }
1762                if (subret > 0 || !strcmp(lfn.name, ".")
1763                        || !strcmp(lfn.name, ".."))
1764                    continue;
1765            }
1766            lfn.checksum = 0x100; /* cannot use long name twice */
1767
1768            if (path_len + 1 + lfn.len >= PATH_MAX) {
1769                fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
1770                goto fail;
1771            }
1772            strcpy(path2 + path_len + 1, lfn.name);
1773
1774            if (is_directory(direntries + i)) {
1775                if (begin_of_direntry(direntries + i) == 0) {
1776                    DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
1777                    goto fail;
1778                }
1779                cluster_count = check_directory_consistency(s,
1780                        begin_of_direntry(direntries + i), path2);
1781                if (cluster_count == 0) {
1782                    DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
1783                    goto fail;
1784                }
1785            } else if (is_file(direntries + i)) {
1786                /* check file size with FAT */
1787                cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
1788                if (cluster_count !=
1789                        (le32_to_cpu(direntries[i].size) + s->cluster_size
1790                         - 1) / s->cluster_size) {
1791                    DLOG(fprintf(stderr, "Cluster count mismatch\n"));
1792                    goto fail;
1793                }
1794            } else
1795                assert(0); /* cluster_count = 0; */
1796
1797            ret += cluster_count;
1798        }
1799
1800        cluster_num = modified_fat_get(s, cluster_num);
1801    } while(!fat_eof(s, cluster_num));
1802
1803    free(cluster);
1804    return ret;
1805}
1806
1807/* returns 1 on success */
1808static int is_consistent(BDRVVVFATState* s)
1809{
1810    int i, check;
1811    int used_clusters_count = 0;
1812
1813DLOG(checkpoint());
1814    /*
1815     * - get modified FAT
1816     * - compare the two FATs (TODO)
1817     * - get buffer for marking used clusters
1818     * - recurse direntries from root (using bs->bdrv_read to make
1819     *    sure to get the new data)
1820     *   - check that the FAT agrees with the size
1821     *   - count the number of clusters occupied by this directory and
1822     *     its files
1823     * - check that the cumulative used cluster count agrees with the
1824     *   FAT
1825     * - if all is fine, return number of used clusters
1826     */
1827    if (s->fat2 == NULL) {
1828        int size = 0x200 * s->sectors_per_fat;
1829        s->fat2 = malloc(size);
1830        memcpy(s->fat2, s->fat.pointer, size);
1831    }
1832    check = vvfat_read(s->bs,
1833            s->first_sectors_number, s->fat2, s->sectors_per_fat);
1834    if (check) {
1835        fprintf(stderr, "Could not copy fat\n");
1836        return 0;
1837    }
1838    assert (s->used_clusters);
1839    for (i = 0; i < sector2cluster(s, s->sector_count); i++)
1840        s->used_clusters[i] &= ~USED_ANY;
1841
1842    clear_commits(s);
1843
1844    /* mark every mapped file/directory as deleted.
1845     * (check_directory_consistency() will unmark those still present). */
1846    if (s->qcow)
1847        for (i = 0; i < s->mapping.next; i++) {
1848            mapping_t* mapping = array_get(&(s->mapping), i);
1849            if (mapping->first_mapping_index < 0)
1850                mapping->mode |= MODE_DELETED;
1851        }
1852
1853    used_clusters_count = check_directory_consistency(s, 0, s->path);
1854    if (used_clusters_count <= 0) {
1855        DLOG(fprintf(stderr, "problem in directory\n"));
1856        return 0;
1857    }
1858
1859    check = s->last_cluster_of_root_directory;
1860    for (i = check; i < sector2cluster(s, s->sector_count); i++) {
1861        if (modified_fat_get(s, i)) {
1862            if(!s->used_clusters[i]) {
1863                DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
1864                return 0;
1865            }
1866            check++;
1867        }
1868
1869        if (s->used_clusters[i] == USED_ALLOCATED) {
1870            /* allocated, but not used... */
1871            DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
1872            return 0;
1873        }
1874    }
1875
1876    if (check != used_clusters_count)
1877        return 0;
1878
1879    return used_clusters_count;
1880}
1881
1882static inline void adjust_mapping_indices(BDRVVVFATState* s,
1883        int offset, int adjust)
1884{
1885    int i;
1886
1887    for (i = 0; i < s->mapping.next; i++) {
1888        mapping_t* mapping = array_get(&(s->mapping), i);
1889
1890#define ADJUST_MAPPING_INDEX(name) \
1891        if (mapping->name >= offset) \
1892            mapping->name += adjust
1893
1894        ADJUST_MAPPING_INDEX(first_mapping_index);
1895        if (mapping->mode & MODE_DIRECTORY)
1896            ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
1897    }
1898}
1899
1900/* insert or update mapping */
1901static mapping_t* insert_mapping(BDRVVVFATState* s,
1902        uint32_t begin, uint32_t end)
1903{
1904    /*
1905     * - find mapping where mapping->begin >= begin,
1906     * - if mapping->begin > begin: insert
1907     *   - adjust all references to mappings!
1908     * - else: adjust
1909     * - replace name
1910     */
1911    int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
1912    mapping_t* mapping = NULL;
1913    mapping_t* first_mapping = array_get(&(s->mapping), 0);
1914
1915    if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
1916            && mapping->begin < begin) {
1917        mapping->end = begin;
1918        index++;
1919        mapping = array_get(&(s->mapping), index);
1920    }
1921    if (index >= s->mapping.next || mapping->begin > begin) {
1922        mapping = array_insert(&(s->mapping), index, 1);
1923        mapping->path = NULL;
1924        adjust_mapping_indices(s, index, +1);
1925    }
1926
1927    mapping->begin = begin;
1928    mapping->end = end;
1929
1930DLOG(mapping_t* next_mapping;
1931assert(index + 1 >= s->mapping.next ||
1932((next_mapping = array_get(&(s->mapping), index + 1)) &&
1933 next_mapping->begin >= end)));
1934
1935    if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1936        s->current_mapping = array_get(&(s->mapping),
1937                s->current_mapping - first_mapping);
1938
1939    return mapping;
1940}
1941
1942static int remove_mapping(BDRVVVFATState* s, int mapping_index)
1943{
1944    mapping_t* mapping = array_get(&(s->mapping), mapping_index);
1945    mapping_t* first_mapping = array_get(&(s->mapping), 0);
1946
1947    /* free mapping */
1948    if (mapping->first_mapping_index < 0)
1949        free(mapping->path);
1950
1951    /* remove from s->mapping */
1952    array_remove(&(s->mapping), mapping_index);
1953
1954    /* adjust all references to mappings */
1955    adjust_mapping_indices(s, mapping_index, -1);
1956
1957    if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1958        s->current_mapping = array_get(&(s->mapping),
1959                s->current_mapping - first_mapping);
1960
1961    return 0;
1962}
1963
1964static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
1965{
1966    int i;
1967    for (i = 0; i < s->mapping.next; i++) {
1968        mapping_t* mapping = array_get(&(s->mapping), i);
1969        if (mapping->dir_index >= offset)
1970            mapping->dir_index += adjust;
1971        if ((mapping->mode & MODE_DIRECTORY) &&
1972                mapping->info.dir.first_dir_index >= offset)
1973            mapping->info.dir.first_dir_index += adjust;
1974    }
1975}
1976
1977static direntry_t* insert_direntries(BDRVVVFATState* s,
1978        int dir_index, int count)
1979{
1980    /*
1981     * make room in s->directory,
1982     * adjust_dirindices
1983     */
1984    direntry_t* result = array_insert(&(s->directory), dir_index, count);
1985    if (result == NULL)
1986        return NULL;
1987    adjust_dirindices(s, dir_index, count);
1988    return result;
1989}
1990
1991static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
1992{
1993    int ret = array_remove_slice(&(s->directory), dir_index, count);
1994    if (ret)
1995        return ret;
1996    adjust_dirindices(s, dir_index, -count);
1997    return 0;
1998}
1999
2000/*
2001 * Adapt the mappings of the cluster chain starting at first cluster
2002 * (i.e. if a file starts at first_cluster, the chain is followed according
2003 * to the modified fat, and the corresponding entries in s->mapping are
2004 * adjusted)
2005 */
2006static int commit_mappings(BDRVVVFATState* s,
2007        uint32_t first_cluster, int dir_index)
2008{
2009    mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2010    direntry_t* direntry = array_get(&(s->directory), dir_index);
2011    uint32_t cluster = first_cluster;
2012
2013    vvfat_close_current_file(s);
2014
2015    assert(mapping);
2016    assert(mapping->begin == first_cluster);
2017    mapping->first_mapping_index = -1;
2018    mapping->dir_index = dir_index;
2019    mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
2020        MODE_DIRECTORY : MODE_NORMAL;
2021
2022    while (!fat_eof(s, cluster)) {
2023        uint32_t c, c1;
2024
2025        for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
2026                c = c1, c1 = modified_fat_get(s, c1));
2027
2028        c++;
2029        if (c > mapping->end) {
2030            int index = array_index(&(s->mapping), mapping);
2031            int i, max_i = s->mapping.next - index;
2032            for (i = 1; i < max_i && mapping[i].begin < c; i++);
2033            while (--i > 0)
2034                remove_mapping(s, index + 1);
2035        }
2036        assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
2037                || mapping[1].begin >= c);
2038        mapping->end = c;
2039
2040        if (!fat_eof(s, c1)) {
2041            int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
2042            mapping_t* next_mapping = i >= s->mapping.next ? NULL :
2043                array_get(&(s->mapping), i);
2044
2045            if (next_mapping == NULL || next_mapping->begin > c1) {
2046                int i1 = array_index(&(s->mapping), mapping);
2047
2048                next_mapping = insert_mapping(s, c1, c1+1);
2049
2050                if (c1 < c)
2051                    i1++;
2052                mapping = array_get(&(s->mapping), i1);
2053            }
2054
2055            next_mapping->dir_index = mapping->dir_index;
2056            next_mapping->first_mapping_index = 
2057                mapping->first_mapping_index < 0 ?
2058                array_index(&(s->mapping), mapping) :
2059                mapping->first_mapping_index;
2060            next_mapping->path = mapping->path;
2061            next_mapping->mode = mapping->mode;
2062            next_mapping->read_only = mapping->read_only;
2063            if (mapping->mode & MODE_DIRECTORY) {
2064                next_mapping->info.dir.parent_mapping_index =
2065                        mapping->info.dir.parent_mapping_index;
2066                next_mapping->info.dir.first_dir_index =
2067                        mapping->info.dir.first_dir_index +
2068                        0x10 * s->sectors_per_cluster *
2069                        (mapping->end - mapping->begin);
2070            } else
2071                next_mapping->info.file.offset = mapping->info.file.offset +
2072                        mapping->end - mapping->begin;
2073
2074            mapping = next_mapping;
2075        }
2076               
2077        cluster = c1;
2078    }
2079
2080    return 0;
2081}
2082
2083static int commit_direntries(BDRVVVFATState* s,
2084        int dir_index, int parent_mapping_index)
2085{
2086    direntry_t* direntry = array_get(&(s->directory), dir_index);
2087    uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
2088    mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2089
2090    int factor = 0x10 * s->sectors_per_cluster;
2091    int old_cluster_count, new_cluster_count;
2092    int current_dir_index = mapping->info.dir.first_dir_index;
2093    int first_dir_index = current_dir_index;
2094    int ret, i;
2095    uint32_t c;
2096
2097DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
2098
2099    assert(direntry);
2100    assert(mapping);
2101    assert(mapping->begin == first_cluster);
2102    assert(mapping->info.dir.first_dir_index < s->directory.next);
2103    assert(mapping->mode & MODE_DIRECTORY);
2104    assert(dir_index == 0 || is_directory(direntry));
2105
2106    mapping->info.dir.parent_mapping_index = parent_mapping_index;
2107
2108    if (first_cluster == 0) {
2109        old_cluster_count = new_cluster_count =
2110            s->last_cluster_of_root_directory;
2111    } else {
2112        for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2113                c = fat_get(s, c))
2114            old_cluster_count++;
2115
2116        for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2117                c = modified_fat_get(s, c))
2118            new_cluster_count++;
2119    }
2120
2121    if (new_cluster_count > old_cluster_count) {
2122        if (insert_direntries(s,
2123                current_dir_index + factor * old_cluster_count,
2124                factor * (new_cluster_count - old_cluster_count)) == NULL)
2125            return -1;
2126    } else if (new_cluster_count < old_cluster_count)
2127        remove_direntries(s,
2128                current_dir_index + factor * new_cluster_count,
2129                factor * (old_cluster_count - new_cluster_count));
2130
2131    for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
2132        void* direntry = array_get(&(s->directory), current_dir_index);
2133        int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
2134                s->sectors_per_cluster);
2135        if (ret)
2136            return ret;
2137        assert(!strncmp(s->directory.pointer, "QEMU", 4));
2138        current_dir_index += factor;
2139    }
2140
2141    ret = commit_mappings(s, first_cluster, dir_index);
2142    if (ret)
2143        return ret;
2144
2145    /* recurse */
2146    for (i = 0; i < factor * new_cluster_count; i++) {
2147        direntry = array_get(&(s->directory), first_dir_index + i);
2148        if (is_directory(direntry) && !is_dot(direntry)) {
2149            mapping = find_mapping_for_cluster(s, first_cluster);
2150            assert(mapping->mode & MODE_DIRECTORY);
2151            ret = commit_direntries(s, first_dir_index + i,
2152                array_index(&(s->mapping), mapping));
2153            if (ret)
2154                return ret;
2155        }
2156    }
2157
2158    return 0;
2159}
2160
2161/* commit one file (adjust contents, adjust mapping),
2162   return first_mapping_index */
2163static int commit_one_file(BDRVVVFATState* s,
2164        int dir_index, uint32_t offset)
2165{
2166    direntry_t* direntry = array_get(&(s->directory), dir_index);
2167    uint32_t c = begin_of_direntry(direntry);
2168    uint32_t first_cluster = c;
2169    mapping_t* mapping = find_mapping_for_cluster(s, c);
2170    uint32_t size = filesize_of_direntry(direntry);
2171    char* cluster = malloc(s->cluster_size);
2172    uint32_t i;
2173    int fd = 0;
2174
2175    assert(offset < size);
2176    assert((offset % s->cluster_size) == 0);
2177
2178    for (i = s->cluster_size; i < offset; i += s->cluster_size)
2179        c = modified_fat_get(s, c);
2180
2181    fd = open(mapping->path, O_RDWR | O_CREAT, 0666);
2182    if (fd < 0) {
2183        fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
2184                strerror(errno), errno);
2185        return fd;
2186    }
2187    if (offset > 0)
2188        if (lseek(fd, offset, SEEK_SET) != offset)
2189            return -3;
2190
2191    while (offset < size) {
2192        uint32_t c1;
2193        int rest_size = (size - offset > s->cluster_size ?
2194                s->cluster_size : size - offset);
2195        int ret;
2196
2197        c1 = modified_fat_get(s, c);
2198
2199        assert((size - offset == 0 && fat_eof(s, c)) ||
2200                (size > offset && c >=2 && !fat_eof(s, c)));
2201        assert(size >= 0);
2202
2203        ret = vvfat_read(s->bs, cluster2sector(s, c),
2204            cluster, (rest_size + 0x1ff) / 0x200);
2205
2206        if (ret < 0)
2207            return ret;
2208
2209        if (write(fd, cluster, rest_size) < 0)
2210            return -2;
2211
2212        offset += rest_size;
2213        c = c1;
2214    }
2215
2216    ftruncate(fd, size);
2217    close(fd);
2218
2219    return commit_mappings(s, first_cluster, dir_index);
2220}
2221
2222#ifdef DEBUG
2223/* test, if all mappings point to valid direntries */
2224static void check1(BDRVVVFATState* s)
2225{
2226    int i;
2227    for (i = 0; i < s->mapping.next; i++) {
2228        mapping_t* mapping = array_get(&(s->mapping), i);
2229        if (mapping->mode & MODE_DELETED) {
2230            fprintf(stderr, "deleted\n");
2231            continue;
2232        }
2233        assert(mapping->dir_index >= 0);
2234        assert(mapping->dir_index < s->directory.next);
2235        direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
2236        assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
2237        if (mapping->mode & MODE_DIRECTORY) {
2238            assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
2239            assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
2240        }
2241    }
2242}
2243
2244/* test, if all direntries have mappings */
2245static void check2(BDRVVVFATState* s)
2246{
2247    int i;
2248    int first_mapping = -1;
2249
2250    for (i = 0; i < s->directory.next; i++) {
2251        direntry_t* direntry = array_get(&(s->directory), i);
2252
2253        if (is_short_name(direntry) && begin_of_direntry(direntry)) {
2254            mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
2255            assert(mapping);
2256            assert(mapping->dir_index == i || is_dot(direntry));
2257            assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
2258        }
2259
2260        if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
2261            /* cluster start */
2262            int j, count = 0;
2263
2264            for (j = 0; j < s->mapping.next; j++) {
2265                mapping_t* mapping = array_get(&(s->mapping), j);
2266                if (mapping->mode & MODE_DELETED)
2267                    continue;
2268                if (mapping->mode & MODE_DIRECTORY) {
2269                    if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
2270                        assert(++count == 1);
2271                        if (mapping->first_mapping_index == -1)
2272                            first_mapping = array_index(&(s->mapping), mapping);
2273                        else
2274                            assert(first_mapping == mapping->first_mapping_index);
2275                        if (mapping->info.dir.parent_mapping_index < 0)
2276                            assert(j == 0);
2277                        else {
2278                            mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
2279                            assert(parent->mode & MODE_DIRECTORY);
2280                            assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
2281                        }
2282                    }
2283                }
2284            }
2285            if (count == 0)
2286                first_mapping = -1;
2287        }
2288    }
2289}
2290#endif
2291
2292static int handle_renames_and_mkdirs(BDRVVVFATState* s)
2293{
2294    int i;
2295
2296#ifdef DEBUG
2297    fprintf(stderr, "handle_renames\n");
2298    for (i = 0; i < s->commits.next; i++) {
2299        commit_t* commit = array_get(&(s->commits), i);
2300        fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
2301    }
2302#endif
2303
2304    for (i = 0; i < s->commits.next;) {
2305        commit_t* commit = array_get(&(s->commits), i);
2306        if (commit->action == ACTION_RENAME) {
2307            mapping_t* mapping = find_mapping_for_cluster(s,
2308                    commit->param.rename.cluster);
2309            char* old_path = mapping->path;
2310
2311            assert(commit->path);
2312            mapping->path = commit->path;
2313            if (rename(old_path, mapping->path))
2314                return -2;
2315
2316            if (mapping->mode & MODE_DIRECTORY) {
2317                int l1 = strlen(mapping->path);
2318                int l2 = strlen(old_path);
2319                int diff = l1 - l2;
2320                direntry_t* direntry = array_get(&(s->directory),
2321                        mapping->info.dir.first_dir_index);
2322                uint32_t c = mapping->begin;
2323                int i = 0;
2324
2325                /* recurse */
2326                while (!fat_eof(s, c)) {
2327                    do {
2328                        direntry_t* d = direntry + i;
2329
2330                        if (is_file(d) || (is_directory(d) && !is_dot(d))) {
2331                            mapping_t* m = find_mapping_for_cluster(s,
2332                                    begin_of_direntry(d));
2333                            int l = strlen(m->path);
2334                            char* new_path = malloc(l + diff + 1);
2335
2336                            assert(!strncmp(m->path, mapping->path, l2));
2337
2338                            strcpy(new_path, mapping->path);
2339                            strcpy(new_path + l1, m->path + l2);
2340
2341                            schedule_rename(s, m->begin, new_path);
2342                        }
2343                        i++;
2344                    } while((i % (0x10 * s->sectors_per_cluster)) != 0);
2345                    c = fat_get(s, c);
2346                }
2347            }
2348
2349            free(old_path);
2350            array_remove(&(s->commits), i);
2351            continue;
2352        } else if (commit->action == ACTION_MKDIR) {
2353            mapping_t* mapping;
2354            int j, parent_path_len;
2355
2356#ifdef __MINGW32__
2357            if (mkdir(commit->path))
2358                return -5;
2359#else
2360            if (mkdir(commit->path, 0755))
2361                return -5;
2362#endif
2363
2364            mapping = insert_mapping(s, commit->param.mkdir.cluster,
2365                    commit->param.mkdir.cluster + 1);
2366            if (mapping == NULL)
2367                return -6;
2368
2369            mapping->mode = MODE_DIRECTORY;
2370            mapping->read_only = 0;
2371            mapping->path = commit->path;
2372            j = s->directory.next;
2373            assert(j);
2374            insert_direntries(s, s->directory.next,
2375                    0x10 * s->sectors_per_cluster);
2376            mapping->info.dir.first_dir_index = j;
2377
2378            parent_path_len = strlen(commit->path)
2379                - strlen(get_basename(commit->path)) - 1;
2380            for (j = 0; j < s->mapping.next; j++) {
2381                mapping_t* m = array_get(&(s->mapping), j);
2382                if (m->first_mapping_index < 0 && m != mapping &&
2383                        !strncmp(m->path, mapping->path, parent_path_len) &&
2384                        strlen(m->path) == parent_path_len)
2385                    break;
2386            }
2387            assert(j < s->mapping.next);
2388            mapping->info.dir.parent_mapping_index = j;
2389
2390            array_remove(&(s->commits), i);
2391            continue;
2392        }
2393
2394        i++;
2395    }
2396    return 0;
2397}
2398
2399/*
2400 * TODO: make sure that the short name is not matching *another* file
2401 */
2402static int handle_commits(BDRVVVFATState* s)
2403{
2404    int i, fail = 0;
2405
2406    vvfat_close_current_file(s);
2407
2408    for (i = 0; !fail && i < s->commits.next; i++) {
2409        commit_t* commit = array_get(&(s->commits), i);
2410        switch(commit->action) {
2411        case ACTION_RENAME: case ACTION_MKDIR:
2412            assert(0);
2413            fail = -2;
2414            break;
2415        case ACTION_WRITEOUT: {
2416            direntry_t* entry = array_get(&(s->directory),
2417                    commit->param.writeout.dir_index);
2418            uint32_t begin = begin_of_direntry(entry);
2419            mapping_t* mapping = find_mapping_for_cluster(s, begin);
2420
2421            assert(mapping);
2422            assert(mapping->begin == begin);
2423            assert(commit->path == NULL);
2424
2425            if (commit_one_file(s, commit->param.writeout.dir_index,
2426                        commit->param.writeout.modified_offset))
2427                fail = -3;
2428
2429            break;
2430        }
2431        case ACTION_NEW_FILE: {
2432            int begin = commit->param.new_file.first_cluster;
2433            mapping_t* mapping = find_mapping_for_cluster(s, begin);
2434            direntry_t* entry;
2435            int i;
2436
2437            /* find direntry */
2438            for (i = 0; i < s->directory.next; i++) {
2439                entry = array_get(&(s->directory), i);
2440                if (is_file(entry) && begin_of_direntry(entry) == begin)
2441                    break;
2442            }
2443
2444            if (i >= s->directory.next) {
2445                fail = -6;
2446                continue;
2447            }
2448
2449            /* make sure there exists an initial mapping */
2450            if (mapping && mapping->begin != begin) {
2451                mapping->end = begin;
2452                mapping = NULL;
2453            }
2454            if (mapping == NULL) {
2455                mapping = insert_mapping(s, begin, begin+1);
2456            }
2457            /* most members will be fixed in commit_mappings() */
2458            assert(commit->path);
2459            mapping->path = commit->path;
2460            mapping->read_only = 0;
2461            mapping->mode = MODE_NORMAL;
2462            mapping->info.file.offset = 0;
2463
2464            if (commit_one_file(s, i, 0))
2465                fail = -7;
2466
2467            break;
2468        }
2469        default:
2470            assert(0);
2471        }
2472    }
2473    if (i > 0 && array_remove_slice(&(s->commits), 0, i))
2474        return -1;
2475    return fail;
2476}
2477
2478static int handle_deletes(BDRVVVFATState* s)
2479{
2480    int i, deferred = 1, deleted = 1;
2481
2482    /* delete files corresponding to mappings marked as deleted */
2483    /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
2484    while (deferred && deleted) {
2485        deferred = 0;
2486        deleted = 0;
2487
2488        for (i = 1; i < s->mapping.next; i++) {
2489            mapping_t* mapping = array_get(&(s->mapping), i);
2490            if (mapping->mode & MODE_DELETED) {
2491                direntry_t* entry = array_get(&(s->directory),
2492                        mapping->dir_index);
2493
2494                if (is_free(entry)) {
2495                    /* remove file/directory */
2496                    if (mapping->mode & MODE_DIRECTORY) {
2497                        int j, next_dir_index = s->directory.next,
2498                        first_dir_index = mapping->info.dir.first_dir_index;
2499
2500                        if (rmdir(mapping->path) < 0) {
2501                            if (errno == ENOTEMPTY) {
2502                                deferred++;
2503                                continue;
2504                            } else
2505                                return -5;
2506                        }
2507
2508                        for (j = 1; j < s->mapping.next; j++) {
2509                            mapping_t* m = array_get(&(s->mapping), j);
2510                            if (m->mode & MODE_DIRECTORY &&
2511                                    m->info.dir.first_dir_index >
2512                                    first_dir_index &&
2513                                    m->info.dir.first_dir_index <
2514                                    next_dir_index)
2515                                next_dir_index =
2516                                    m->info.dir.first_dir_index;
2517                        }
2518                        remove_direntries(s, first_dir_index,
2519                                next_dir_index - first_dir_index);
2520
2521                        deleted++;
2522                    }
2523                } else {
2524                    if (unlink(mapping->path))
2525                        return -4;
2526                    deleted++;
2527                }
2528                DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
2529                remove_mapping(s, i);
2530            }
2531        }
2532    }
2533
2534    return 0;
2535}
2536
2537/*
2538 * synchronize mapping with new state:
2539 *
2540 * - copy FAT (with bdrv_read)
2541 * - mark all filenames corresponding to mappings as deleted
2542 * - recurse direntries from root (using bs->bdrv_read)
2543 * - delete files corresponding to mappings marked as deleted
2544 */
2545static int do_commit(BDRVVVFATState* s)
2546{
2547    int ret = 0;
2548
2549    /* the real meat are the commits. Nothing to do? Move along! */
2550    if (s->commits.next == 0)
2551        return 0;
2552
2553    vvfat_close_current_file(s);
2554
2555    ret = handle_renames_and_mkdirs(s);
2556    if (ret) {
2557        fprintf(stderr, "Error handling renames (%d)\n", ret);
2558        assert(0);
2559        return ret;
2560    }
2561
2562    /* copy FAT (with bdrv_read) */ 
2563    memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
2564
2565    /* recurse direntries from root (using bs->bdrv_read) */
2566    ret = commit_direntries(s, 0, -1);
2567    if (ret) {
2568        fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
2569        assert(0);
2570        return ret;
2571    }
2572
2573    ret = handle_commits(s);
2574    if (ret) {
2575        fprintf(stderr, "Error handling commits (%d)\n", ret);
2576        assert(0);
2577        return ret;
2578    }
2579
2580    ret = handle_deletes(s);
2581    if (ret) {
2582        fprintf(stderr, "Error deleting\n");
2583        assert(0);
2584        return ret;
2585    }
2586
2587    s->qcow->drv->bdrv_make_empty(s->qcow);
2588
2589    memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
2590
2591DLOG(checkpoint());
2592    return 0;
2593}
2594
2595static int try_commit(BDRVVVFATState* s)
2596{
2597    vvfat_close_current_file(s);
2598DLOG(checkpoint());
2599    if(!is_consistent(s))
2600        return -1;
2601    return do_commit(s);
2602}
2603
2604static int vvfat_write(BlockDriverState *bs, int64_t sector_num, 
2605                    const uint8_t *buf, int nb_sectors)
2606{
2607    BDRVVVFATState *s = bs->opaque; 
2608    int i, ret;
2609
2610DLOG(checkpoint());
2611
2612    vvfat_close_current_file(s);
2613
2614    /*
2615     * Some sanity checks:
2616     * - do not allow writing to the boot sector
2617     * - do not allow to write non-ASCII filenames
2618     */
2619
2620    if (sector_num < s->first_sectors_number)
2621        return -1;
2622
2623    for (i = sector2cluster(s, sector_num);
2624            i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
2625        mapping_t* mapping = find_mapping_for_cluster(s, i);
2626        if (mapping) {
2627            if (mapping->read_only) {
2628                fprintf(stderr, "Tried to write to write-protected file %s\n",
2629                        mapping->path);
2630                return -1;
2631            }
2632
2633            if (mapping->mode & MODE_DIRECTORY) {
2634                int begin = cluster2sector(s, i);
2635                int end = begin + s->sectors_per_cluster, k;
2636                int dir_index;
2637                const direntry_t* direntries;
2638                long_file_name lfn;
2639
2640                lfn_init(&lfn);
2641
2642                if (begin < sector_num)
2643                    begin = sector_num;
2644                if (end > sector_num + nb_sectors)
2645                    end = sector_num + nb_sectors;
2646                dir_index  = mapping->dir_index + 
2647                    0x10 * (begin - mapping->begin * s->sectors_per_cluster);
2648                direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
2649
2650                for (k = 0; k < (end - begin) * 0x10; k++) {
2651                    /* do not allow non-ASCII filenames */
2652                    if (parse_long_name(&lfn, direntries + k) < 0) {
2653                        fprintf(stderr, "Warning: non-ASCII filename\n");
2654                        return -1;
2655                    }
2656                    /* no access to the direntry of a read-only file */
2657                    else if (is_short_name(direntries+k) &&
2658                            (direntries[k].attributes & 1)) {
2659                        if (memcmp(direntries + k,
2660                                    array_get(&(s->directory), dir_index + k),
2661                                    sizeof(direntry_t))) {
2662                            fprintf(stderr, "Warning: tried to write to write-protected file\n");
2663                            return -1;
2664                        }
2665                    }
2666                }
2667            }
2668            i = mapping->end;
2669        } else
2670            i++;
2671    }
2672
2673    /*
2674     * Use qcow backend. Commit later.
2675     */
2676DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
2677    ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
2678    if (ret < 0) {
2679        fprintf(stderr, "Error writing to qcow backend\n");
2680        return ret;
2681    }
2682
2683    for (i = sector2cluster(s, sector_num);
2684            i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
2685        if (i >= 0)
2686            s->used_clusters[i] |= USED_ALLOCATED;
2687
2688DLOG(checkpoint());
2689    /* TODO: add timeout */
2690    try_commit(s);
2691
2692DLOG(checkpoint());
2693    return 0;
2694}
2695
2696static int vvfat_is_allocated(BlockDriverState *bs,
2697        int64_t sector_num, int nb_sectors, int* n)
2698{
2699    BDRVVVFATState* s = bs->opaque;
2700    *n = s->sector_count - sector_num;
2701    if (*n > nb_sectors)
2702        *n = nb_sectors;
2703    else if (*n < 0)
2704        return 0;
2705    return 1;   
2706}
2707
2708static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
2709        const uint8_t* buffer, int nb_sectors) {
2710    BDRVVVFATState* s = bs->opaque;
2711    return try_commit(s);
2712}
2713
2714static void write_target_close(BlockDriverState *bs) {
2715    BDRVVVFATState* s = bs->opaque;
2716    bdrv_delete(s->qcow);
2717    free(s->qcow_filename);
2718}
2719
2720static BlockDriver vvfat_write_target = {
2721    "vvfat_write_target", 0, NULL, NULL, NULL,
2722    write_target_commit,
2723    write_target_close,
2724    NULL, NULL, NULL
2725};
2726
2727static int enable_write_target(BDRVVVFATState *s)
2728{
2729    int size = sector2cluster(s, s->sector_count);
2730    s->used_clusters = calloc(size, 1);
2731
2732    array_init(&(s->commits), sizeof(commit_t));
2733
2734    s->qcow_filename = malloc(1024);
2735    strcpy(s->qcow_filename, "/tmp/vl.XXXXXX");
2736    get_tmp_filename(s->qcow_filename, strlen(s->qcow_filename) + 1);
2737    if (bdrv_create(&bdrv_qcow,
2738                s->qcow_filename, s->sector_count, "fat:", 0) < 0)
2739        return -1;
2740    s->qcow = bdrv_new("");
2741    if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0)
2742        return -1;
2743
2744#ifndef _WIN32
2745    unlink(s->qcow_filename);
2746#endif
2747
2748    s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
2749    s->bs->backing_hd->drv = &vvfat_write_target;
2750    s->bs->backing_hd->opaque = s;
2751
2752    return 0;
2753}
2754
2755static void vvfat_close(BlockDriverState *bs)
2756{
2757    BDRVVVFATState *s = bs->opaque;
2758
2759    vvfat_close_current_file(s);
2760    array_free(&(s->fat));
2761    array_free(&(s->directory));
2762    array_free(&(s->mapping));
2763    if(s->cluster_buffer)
2764        free(s->cluster_buffer);
2765}
2766
2767BlockDriver bdrv_vvfat = {
2768    "vvfat",
2769    sizeof(BDRVVVFATState),
2770    vvfat_probe,
2771    vvfat_open,
2772    vvfat_read,
2773    vvfat_write,
2774    vvfat_close,
2775    NULL, /* ??? Not sure if we can do any meaningful flushing.  */
2776    NULL,
2777    vvfat_is_allocated
2778};
2779
2780#ifdef DEBUG
2781static void checkpoint() {
2782    assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
2783    check1(vvv);
2784    check2(vvv);
2785    assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
2786#if 0
2787    if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
2788        fprintf(stderr, "Nonono!\n");
2789    mapping_t* mapping;
2790    direntry_t* direntry;
2791    assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
2792    assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
2793    if (vvv->mapping.next<47)
2794        return;
2795    assert((mapping = array_get(&(vvv->mapping), 47)));
2796    assert(mapping->dir_index < vvv->directory.next);
2797    direntry = array_get(&(vvv->directory), mapping->dir_index);
2798    assert(!memcmp(direntry->name, "USB     H  ", 11) || direntry->name[0]==0);
2799#endif
2800    return;
2801    /* avoid compiler warnings: */
2802    hexdump(NULL, 100);
2803    remove_mapping(vvv, NULL);
2804    print_mapping(NULL);
2805    print_direntry(NULL);
2806}
2807#endif
2808
Note: See TracBrowser for help on using the repository browser.