Statistics
| Branch: | Revision:

root / block-vvfat.c @ dfae6487

History | View | Annotate | Download (53.2 kB)

1
/*
2
 * QEMU Block driver for virtual VFAT (shadows a local directory)
3
 * 
4
 * Copyright (c) 2004 Johannes E. Schindelin
5
 * 
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
#include <sys/stat.h>
25
#include <dirent.h>
26
#include <assert.h>
27
#include "vl.h"
28
#include "block_int.h"
29

    
30
// TODO: new file
31
// TODO: delete file
32
// TODO: make root directory larger
33
// TODO: make directory clusters connected, so they are reserved anyway... add a member which tells how many clusters are reserved after a directory
34
// TODO: introduce another member in mapping_t which says where the directory resides in s->directory (for mkdir and rmdir) 
35
// in _read and _write, before treating direntries or file contents, get_mapping to know what it is.
36
// TODO: mkdir
37
// TODO: rmdir 
38

    
39
// TODO: when commit_data'ing a direntry and is_consistent, commit_remove
40
// TODO: reset MODE_MODIFIED when commit_remove'ing
41

    
42
#define DEBUG
43

    
44
/* dynamic array functions */
45
typedef struct array_t {
46
    char* pointer;
47
    unsigned int size,next,item_size;
48
} array_t;
49

    
50
static inline void array_init(array_t* array,unsigned int item_size)
51
{
52
    array->pointer=0;
53
    array->size=0;
54
    array->next=0;
55
    array->item_size=item_size;
56
}
57

    
58
static inline void array_free(array_t* array)
59
{
60
    if(array->pointer)
61
        free(array->pointer);
62
    array->size=array->next=0;
63
}
64

    
65
/* make sure that memory is reserved at pointer[index*item_size] */
66
static inline void* array_get(array_t* array,unsigned int index) {
67
    if((index+1)*array->item_size>array->size) {
68
        int new_size=(index+32)*array->item_size;
69
        array->pointer=realloc(array->pointer,new_size);
70
        if(!array->pointer)
71
            return 0;
72
        array->size=new_size;
73
        array->next=index+1;
74
    }
75
    return array->pointer+index*array->item_size;
76
}
77

    
78
static inline void* array_get_next(array_t* array) {
79
    unsigned int next=array->next;
80
    void* result=array_get(array,next);
81
    array->next=next+1;
82
    return result;
83
}
84

    
85
static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
86
    if((array->next+count)*array->item_size>array->size) {
87
        int increment=count*array->item_size;
88
        array->pointer=realloc(array->pointer,array->size+increment);
89
        if(!array->pointer)
90
            return 0;
91
        array->size+=increment;
92
    }
93
    memmove(array->pointer+(index+count)*array->item_size,
94
                array->pointer+index*array->item_size,
95
                (array->next-index)*array->item_size);
96
    array->next+=count;
97
    return array->pointer+index*array->item_size;
98
}
99

    
100
/* this performs a "roll", so that the element which was at index_from becomes
101
 * index_to, but the order of all other elements is preserved. */
102
static inline int array_roll(array_t* array,int index_to,int index_from,int count)
103
{
104
    char* buf;
105
    char* from;
106
    char* to;
107
    int is;
108

    
109
    if(!array ||
110
            index_to<0 || index_to>=array->next ||
111
            index_from<0 || index_from>=array->next)
112
        return -1;
113
    
114
    if(index_to==index_from)
115
        return 0;
116

    
117
    is=array->item_size;
118
    from=array->pointer+index_from*is;
119
    to=array->pointer+index_to*is;
120
    buf=malloc(is*count);
121
    memcpy(buf,from,is*count);
122

    
123
    if(index_to<index_from)
124
        memmove(to+is*count,to,from-to);
125
    else
126
        memmove(from,from+is*count,to-from);
127
    
128
    memcpy(to,buf,is*count);
129

    
130
    free(buf);
131

    
132
    return 0;
133
}
134

    
135
int array_remove(array_t* array,int index)
136
{
137
    if(array_roll(array,array->next-1,index,1))
138
        return -1;
139
    array->next--;
140
    return 0;
141
}
142

    
143
/* These structures are used to fake a disk and the VFAT filesystem.
144
 * For this reason we need to use __attribute__((packed)). */
145

    
146
typedef struct bootsector_t {
147
    uint8_t jump[3];
148
    uint8_t name[8];
149
    uint16_t sector_size;
150
    uint8_t sectors_per_cluster;
151
    uint16_t reserved_sectors;
152
    uint8_t number_of_fats;
153
    uint16_t root_entries;
154
    uint16_t zero;
155
    uint8_t media_type;
156
    uint16_t sectors_per_fat;
157
    uint16_t sectors_per_track;
158
    uint16_t number_of_heads;
159
    uint32_t hidden_sectors;
160
    uint32_t total_sectors;
161
    union {
162
        struct {
163
            uint8_t drive_number;
164
            uint8_t current_head;
165
            uint8_t signature;
166
            uint32_t id;
167
            uint8_t volume_label[11];
168
        } __attribute__((packed)) fat16;
169
        struct {
170
            uint32_t sectors_per_fat;
171
            uint16_t flags;
172
            uint8_t major,minor;
173
            uint32_t first_cluster_of_root_directory;
174
            uint16_t info_sector;
175
            uint16_t backup_boot_sector;
176
            uint16_t ignored;
177
        } __attribute__((packed)) fat32;
178
    } u;
179
    uint8_t fat_type[8];
180
    uint8_t ignored[0x1c0];
181
    uint8_t magic[2];
182
} __attribute__((packed)) bootsector_t;
183

    
184
typedef struct partition_t {
185
    uint8_t attributes; /* 0x80 = bootable */
186
    uint8_t start_head;
187
    uint8_t start_sector;
188
    uint8_t start_cylinder;
189
    uint8_t fs_type; /* 0x6 = FAT16, 0xb = FAT32 */
190
    uint8_t end_head;
191
    uint8_t end_sector;
192
    uint8_t end_cylinder;
193
    uint32_t start_sector_long;
194
    uint32_t end_sector_long;
195
} __attribute__((packed)) partition_t;
196

    
197
typedef struct mbr_t {
198
    uint8_t ignored[0x1be];
199
    partition_t partition[4];
200
    uint8_t magic[2];
201
} __attribute__((packed)) mbr_t;
202

    
203
typedef struct direntry_t {
204
    uint8_t name[8];
205
    uint8_t extension[3];
206
    uint8_t attributes;
207
    uint8_t reserved[2];
208
    uint16_t ctime;
209
    uint16_t cdate;
210
    uint16_t adate;
211
    uint16_t begin_hi;
212
    uint16_t mtime;
213
    uint16_t mdate;
214
    uint16_t begin;
215
    uint32_t size;
216
} __attribute__((packed)) direntry_t;
217

    
218
/* this structure are used to transparently access the files */
219

    
220
typedef struct mapping_t {
221
    /* begin is the first cluster, end is the last+1,
222
     * offset is the offset in the file in clusters of this slice */
223
    off_t begin,end,offset;
224
    char* filename;
225

    
226
    /* as s->directory is growable, no pointer may be used here */
227
    unsigned int dir_index;
228
    enum { MODE_NORMAL,MODE_UNDEFINED,MODE_MODIFIED,MODE_DELETED,MODE_DIRECTORY } mode;
229
} mapping_t;
230

    
231
/* this structure is used to hold sectors which need to be written, but it's
232
 * not known yet where to write them. */
233

    
234
typedef struct commit_t {
235
    uint32_t cluster_num;
236
    uint8_t* buf;
237
} commit_t;
238

    
239
/* write support exists for fat, direntry and file contents */
240
typedef enum {
241
    WRITE_UNDEFINED,WRITE_FAT,WRITE_DIRENTRY,WRITE_DATA
242
} write_action_t;
243

    
244
/* here begins the real VVFAT driver */
245

    
246
typedef struct BDRVVVFATState {
247
    unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
248
    unsigned char first_sectors[0x40*0x200];
249
    
250
    int fat_type; /* 16 or 32 */
251
    array_t fat,directory,mapping;
252
   
253
    unsigned int cluster_size;
254
    unsigned int sectors_per_cluster;
255
    unsigned int sectors_per_fat;
256
    unsigned int sectors_of_root_directory;
257
    unsigned int sectors_for_directory;
258
    unsigned int faked_sectors; /* how many sectors are faked before file data */
259
    uint32_t sector_count; /* total number of sectors of the partition */
260
    uint32_t cluster_count; /* total number of clusters of this partition */
261
    unsigned int first_file_mapping; /* index of the first mapping which is not a directory, but a file */
262
    uint32_t max_fat_value;
263
   
264
    int current_fd;
265
    char current_fd_is_writable; /* =0 if read only, !=0 if read/writable */
266
    mapping_t* current_mapping;
267
    unsigned char* cluster;
268
    unsigned int current_cluster;
269

    
270
    /* write support */
271
    array_t commit;
272
    /* for each file, the file contents, the direntry, and the fat entries are
273
     * written, but not necessarily in that order */
274
    write_action_t action[3];
275
} BDRVVVFATState;
276

    
277

    
278
static int vvfat_probe(const uint8_t *buf, int buf_size, const char *filename)
279
{
280
    if (strstart(filename, "fat:", NULL) ||
281
        strstart(filename, "fatrw:", NULL))
282
        return 100;
283
    return 0;
284
}
285

    
286
static void init_mbr(BDRVVVFATState* s)
287
{
288
    /* TODO: if the files mbr.img and bootsect.img exist, use them */
289
    mbr_t* real_mbr=(mbr_t*)s->first_sectors;
290
    partition_t* partition=&(real_mbr->partition[0]);
291

    
292
    memset(s->first_sectors,0,512);
293
   
294
    partition->attributes=0x80; /* bootable */
295
    partition->start_head=1;
296
    partition->start_sector=1;
297
    partition->start_cylinder=0;
298
    partition->fs_type=(s->fat_type==16?0x6:0xb); /* FAT16/FAT32 */
299
    partition->end_head=0xf;
300
    partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */;
301
    partition->end_cylinder=0xff; /* lower 8 bits of end cylinder */;
302
    partition->start_sector_long=cpu_to_le32(0x3f);
303
    partition->end_sector_long=cpu_to_le32(s->sector_count);
304

    
305
    real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
306
}
307

    
308
/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
309
static inline int short2long_name(unsigned char* dest,const char* src)
310
{
311
    int i;
312
    for(i=0;i<129 && src[i];i++) {
313
        dest[2*i]=src[i];
314
        dest[2*i+1]=0;
315
    }
316
    dest[2*i]=dest[2*i+1]=0;
317
    for(i=2*i+2;(i%26);i++)
318
        dest[i]=0xff;
319
    return i;
320
}
321

    
322
static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
323
{
324
    char buffer[258];
325
    int length=short2long_name(buffer,filename),
326
        number_of_entries=(length+25)/26,i;
327
    direntry_t* entry;
328

    
329
    for(i=0;i<number_of_entries;i++) {
330
        entry=array_get_next(&(s->directory));
331
        entry->attributes=0xf;
332
        entry->reserved[0]=0;
333
        entry->begin=0;
334
        entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
335
    }
336
    for(i=0;i<length;i++) {
337
        int offset=(i%26);
338
        if(offset<10) offset=1+offset;
339
        else if(offset<22) offset=14+offset-10;
340
        else offset=28+offset-22;
341
        entry=array_get(&(s->directory),s->directory.next-1-(i/26));
342
        entry->name[offset]=buffer[i];
343
    }
344
    return array_get(&(s->directory),s->directory.next-number_of_entries);
345
}
346

    
347
/* fat functions */
348

    
349
static inline uint8_t fat_chksum(direntry_t* entry)
350
{
351
    uint8_t chksum=0;
352
    int i;
353

    
354
    for(i=0;i<11;i++)
355
        chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0))
356
            +(unsigned char)entry->name[i];
357
    
358
    return chksum;
359
}
360

    
361
/* if return_time==0, this returns the fat_date, else the fat_time */
362
static uint16_t fat_datetime(time_t time,int return_time) {
363
    struct tm* t;
364
#ifdef _WIN32
365
    t=localtime(&time); /* this is not thread safe */
366
#else
367
    struct tm t1;
368
    t=&t1;
369
    localtime_r(&time,t);
370
#endif
371
    if(return_time)
372
        return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
373
    return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
374
}
375

    
376
static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
377
{
378
    if(s->fat_type==12) {
379
        assert(0); /* TODO */
380
    } else if(s->fat_type==16) {
381
        uint16_t* entry=array_get(&(s->fat),cluster);
382
        *entry=cpu_to_le16(value&0xffff);
383
    } else {
384
        uint32_t* entry=array_get(&(s->fat),cluster);
385
        *entry=cpu_to_le32(value);
386
    }
387
}
388

    
389
static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
390
{
391
    //fprintf(stderr,"want to get fat for cluster %d\n",cluster);
392
    if(s->fat_type==12) {
393
        const uint8_t* x=s->fat.pointer+cluster*3/2;
394
        return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
395
    } else if(s->fat_type==16) {
396
        uint16_t* entry=array_get(&(s->fat),cluster);
397
        return le16_to_cpu(*entry);
398
    } else {
399
        uint32_t* entry=array_get(&(s->fat),cluster);
400
        return le32_to_cpu(*entry);
401
    }
402
}
403

    
404
static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
405
{
406
    if(fat_entry>s->max_fat_value-8)
407
        return -1;
408
    return 0;
409
}
410

    
411
static inline void init_fat(BDRVVVFATState* s)
412
{
413
    int i;
414
    
415
    array_init(&(s->fat),(s->fat_type==32?4:2));
416
    array_get(&(s->fat),s->sectors_per_fat*0x200/s->fat.item_size-1);
417
    memset(s->fat.pointer,0,s->fat.size);
418
    fat_set(s,0,0x7ffffff8);
419
    
420
    for(i=1;i<s->sectors_for_directory/s->sectors_per_cluster-1;i++)
421
        fat_set(s,i,i+1);
422
    fat_set(s,i,0x7fffffff);
423

    
424
    switch(s->fat_type) {
425
        case 12: s->max_fat_value=0xfff; break;
426
        case 16: s->max_fat_value=0xffff; break;
427
        case 32: s->max_fat_value=0xfffffff; break;
428
        default: s->max_fat_value=0; /* error... */
429
    }
430

    
431
}
432

    
433
static inline int long2unix_name(unsigned char* dest,int dest_size,direntry_t* direntry_short) {
434
    int i=-1,j;
435
    int chksum=fat_chksum(direntry_short);
436
    while(1) {
437
        char* buf=(char*)(direntry_short+i);
438
        if((buf[0]&0x3f)!=-i || direntry_short[i].reserved[1]!=chksum ||
439
                direntry_short[i].attributes!=0xf) {
440
            if(i<-1)
441
                return -3;
442
            /* take short name */
443
            for(j=7;j>0 && direntry_short->name[j]==' ';j--);
444
            if(j+1>dest_size)
445
                return -1;
446
            strncpy(dest,direntry_short->name,j+1);
447
            dest+=j+1; dest_size-=j+1;
448
            for(j=2;j>=0 && direntry_short->extension[j]==' ';j--);
449
            if(j>=0) {
450
                if(j+2>dest_size)
451
                    return -1;
452
                dest[0]='.';
453
                strncpy(dest+1,direntry_short->extension,j+1);
454
            }
455
            return 0;
456
        }
457
        for(j=0;j<13;j++) {
458
            dest_size--;
459
            if(dest_size<0)
460
                return -2;
461
            dest[0]=buf[2*j+((j<5)?1:(j<11)?4:6)];
462
            if(dest[0]==0 && (buf[0]&0x40)!=0)
463
                return 0;
464
            dest++;
465
        }
466
        /* last entry, but no trailing \0? */
467
        if(buf[0]&0x40)
468
            return -3;
469
        i--;
470
    }
471
}
472

    
473
static inline direntry_t* create_short_filename(BDRVVVFATState* s,unsigned int directory_start,const char* filename,int is_dot)
474
{
475
    int i,long_index=s->directory.next;
476
    direntry_t* entry=0;
477
    direntry_t* entry_long=0;
478

    
479
    if(is_dot) {
480
        entry=array_get_next(&(s->directory));
481
        memset(entry->name,0x20,11);
482
        memcpy(entry->name,filename,strlen(filename));
483
        return entry;
484
    }
485
    
486
    for(i=1;i<8 && filename[i] && filename[i]!='.';i++);
487

    
488
    entry_long=create_long_filename(s,filename);
489
   
490
    entry=array_get_next(&(s->directory));
491
    memset(entry->name,0x20,11);
492
    strncpy(entry->name,filename,i);
493
    
494
    if(filename[i]) {
495
        int len=strlen(filename);
496
        for(i=len;i>0 && filename[i-1]!='.';i--);
497
        if(i>0)
498
            memcpy(entry->extension,filename+i,(len-i>3?3:len-i));
499
    }
500

    
501
    /* upcase & remove unwanted characters */
502
    for(i=10;i>=0;i--) {
503
        if(i==10 || i==7) for(;i>1 && entry->name[i]==' ';i--);
504
        if(entry->name[i]<=' ' || entry->name[i]>0x7f
505
                || strchr("*?<>|\":/\\[];,+='",entry->name[i]))
506
            entry->name[i]='_';
507
        else if(entry->name[i]>='a' && entry->name[i]<='z')
508
            entry->name[i]+='A'-'a';
509
    }
510

    
511
    /* mangle duplicates */
512
    while(1) {
513
        direntry_t* entry1=array_get(&(s->directory),directory_start);
514
        int j;
515

    
516
        for(;entry1<entry;entry1++)
517
            if(!(entry1->attributes&0xf) && !memcmp(entry1->name,entry->name,11))
518
                break; /* found dupe */
519
        if(entry1==entry) /* no dupe found */
520
            break;
521

    
522
        /* use all 8 characters of name */        
523
        if(entry->name[7]==' ') {
524
            int j;
525
            for(j=6;j>0 && entry->name[j]==' ';j--)
526
                entry->name[j]='~';
527
        }
528

    
529
        /* increment number */
530
        for(j=7;j>0 && entry->name[j]=='9';j--)
531
            entry->name[j]='0';
532
        if(j>0) {
533
            if(entry->name[j]<'0' || entry->name[j]>'9')
534
                entry->name[j]='0';
535
            else
536
                entry->name[j]++;
537
        }
538
    }
539

    
540
    /* calculate checksum; propagate to long name */
541
    if(entry_long) {
542
        uint8_t chksum=fat_chksum(entry);
543

    
544
        /* calculate anew, because realloc could have taken place */
545
        entry_long=array_get(&(s->directory),long_index);
546
        while(entry_long<entry
547
                    && entry_long->attributes==0xf) {
548
            entry_long->reserved[1]=chksum;
549
            entry_long++;
550
        }
551
    }
552

    
553
    return entry;
554
}
555

    
556
static int read_directory(BDRVVVFATState* s,const char* dirname,
557
                int first_cluster_of_parent)
558
{
559

    
560
    DIR* dir=opendir(dirname);
561
    struct dirent* entry;
562
    struct stat st;
563
    unsigned int start_of_directory=s->directory.next;
564
    /* mappings before first_file_mapping are directories */
565
    unsigned int first_directory_mapping=s->first_file_mapping;
566
    unsigned int first_cluster=(start_of_directory/0x10/s->sectors_per_cluster);
567
    int i;
568

    
569
    if(!dir)
570
        return -1;
571
    
572
    while((entry=readdir(dir))) {
573
        unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
574
        char* buffer;
575
        direntry_t* direntry;
576
        int is_dot=!strcmp(entry->d_name,".");
577
        int is_dotdot=!strcmp(entry->d_name,"..");
578

    
579
        if(start_of_directory==1 && (is_dotdot || is_dot))
580
            continue;
581
        
582
        buffer=(char*)malloc(length);
583
        snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
584

    
585
        if(stat(buffer,&st)<0) {
586
            free(buffer);
587
            continue;
588
        }
589

    
590
        /* create directory entry for this file */
591
        //fprintf(stderr,"create direntry at %d (cluster %d) for %s\n",s->directory.next,s->directory.next/0x10/s->sectors_per_cluster,entry->d_name);
592
        direntry=create_short_filename(s,start_of_directory,entry->d_name,is_dot||is_dotdot);
593
        direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
594
        direntry->reserved[0]=direntry->reserved[1]=0;
595
        direntry->ctime=fat_datetime(st.st_ctime,1);
596
        direntry->cdate=fat_datetime(st.st_ctime,0);
597
        direntry->adate=fat_datetime(st.st_atime,0);
598
        direntry->begin_hi=0;
599
        direntry->mtime=fat_datetime(st.st_mtime,1);
600
        direntry->mdate=fat_datetime(st.st_mtime,0);
601
        if(is_dotdot)
602
            direntry->begin=cpu_to_le16(first_cluster_of_parent);
603
        else if(is_dot)
604
            direntry->begin=cpu_to_le16(first_cluster);
605
        else
606
            direntry->begin=cpu_to_le16(0); /* do that later */
607
        direntry->size=cpu_to_le32(st.st_size);
608

    
609
        /* create mapping for this file */
610
        if(!is_dot && !is_dotdot) {
611
            if(S_ISDIR(st.st_mode))
612
                s->current_mapping=(mapping_t*)array_insert(&(s->mapping),s->first_file_mapping++,1);
613
            else
614
                s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
615
            s->current_mapping->begin=0;
616
            s->current_mapping->end=st.st_size;
617
            s->current_mapping->offset=0;
618
            s->current_mapping->filename=buffer;
619
            s->current_mapping->dir_index=s->directory.next-1;
620
            s->current_mapping->mode=(S_ISDIR(st.st_mode)?MODE_DIRECTORY:MODE_UNDEFINED);
621
        }
622
    }
623
    closedir(dir);
624

    
625
    /* fill with zeroes up to the end of the cluster */
626
    while(s->directory.next%(0x10*s->sectors_per_cluster)) {
627
        direntry_t* direntry=array_get_next(&(s->directory));
628
        memset(direntry,0,sizeof(direntry_t));
629
    }
630

    
631
    /* reserve next cluster also (for new files) */
632
    for(i=0;i<0x10*s->sectors_per_cluster;i++) {
633
        direntry_t* direntry=array_get_next(&(s->directory));
634
        memset(direntry,0,sizeof(direntry_t));
635
    }
636

    
637
    /* was it the first directory? */
638
    if(start_of_directory==1) {
639
        mapping_t* mapping=array_insert(&(s->mapping),0,1);
640
        mapping->filename=strdup(dirname);
641
        mapping->mode=MODE_DIRECTORY;
642
        mapping->begin=0;
643
        mapping->end=1;
644
        mapping->offset=0;
645
        mapping->dir_index=0xffffffff;
646
        s->sectors_of_root_directory=s->directory.next/0x10;
647
    }
648

    
649
    /* recurse directories */
650
    {
651
        int i;
652

    
653
        //fprintf(stderr,"iterating subdirectories of %s (first cluster %d): %d to %d\n",dirname,first_cluster,first_directory_mapping,last_directory_mapping);
654
        for(i=first_directory_mapping;i<s->first_file_mapping;i++) {
655
            mapping_t* mapping=array_get(&(s->mapping),i);
656
            direntry_t* direntry=array_get(&(s->directory),mapping->dir_index);
657
            /* the directory to be read can add more subdirectories */
658
            int last_dir_mapping=s->first_file_mapping;
659
            
660
            assert(mapping->mode==MODE_DIRECTORY);
661
            /* first, tell the mapping where the directory will start */
662
            mapping->begin=s->directory.next/0x10/s->sectors_per_cluster;
663
            if(i>0) {
664
                mapping[-1].end=mapping->begin;
665
                assert(mapping[-1].begin<mapping->begin);
666
            }
667
            /* then tell the direntry */
668
            direntry->begin=cpu_to_le16(mapping->begin);
669
            //fprintf(stderr,"read directory %s (begin %d)\n",mapping->filename,(int)mapping->begin);
670
            /* then read it */
671
            if(read_directory(s,mapping->filename,first_cluster))
672
                return -1;
673

    
674
            if(last_dir_mapping!=s->first_file_mapping) {
675
                int diff=s->first_file_mapping-last_dir_mapping;
676
                assert(diff>0);
677

    
678
                if(last_dir_mapping!=i+1) {
679
                    int count=last_dir_mapping-i-1;
680
                    int to=s->first_file_mapping-count;
681

    
682
                    assert(count>0);
683
                    assert(to>i+1);
684
                    array_roll(&(s->mapping),to,i+1,count);
685
                    /* could have changed due to realloc */
686
                    mapping=array_get(&(s->mapping),i);
687
                    mapping->end=mapping[1].begin;
688
                }
689
                i+=diff;
690
            }
691
        }
692
    }
693

    
694
    return 0;
695
}
696

    
697
static int init_directory(BDRVVVFATState* s,const char* dirname)
698
{
699
    bootsector_t* bootsector=(bootsector_t*)&(s->first_sectors[(s->first_sectors_number-1)*0x200]);
700
    unsigned int i;
701
    unsigned int cluster;
702

    
703
    memset(&(s->first_sectors[0]),0,0x40*0x200);
704

    
705
    /* TODO: if FAT32, this is probably wrong */
706
    s->sectors_per_fat=0xfc;
707
    s->sectors_per_cluster=0x10;
708
    s->cluster_size=s->sectors_per_cluster*0x200;
709
    s->cluster=malloc(s->cluster_size);
710
    
711
    array_init(&(s->mapping),sizeof(mapping_t));
712
    array_init(&(s->directory),sizeof(direntry_t));
713
    array_init(&(s->commit),sizeof(commit_t));
714

    
715
    /* add volume label */
716
    {
717
        direntry_t* entry=array_get_next(&(s->directory));
718
        entry->attributes=0x28; /* archive | volume label */
719
        snprintf(entry->name,11,"QEMU VVFAT");
720
    }
721

    
722
    if(read_directory(s,dirname,0))
723
        return -1;
724

    
725
    /* make sure that the number of directory entries is multiple of 0x200/0x20 (to fit the last sector exactly) */
726
    s->sectors_for_directory=s->directory.next/0x10;
727

    
728
    s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2+s->sectors_for_directory;
729
    s->cluster_count=(s->sector_count-s->faked_sectors)/s->sectors_per_cluster;
730

    
731
    /* Now build FAT, and write back information into directory */
732
    init_fat(s);
733

    
734
    cluster=s->sectors_for_directory/s->sectors_per_cluster;
735
    assert(s->sectors_for_directory%s->sectors_per_cluster==0);
736

    
737
    /* set the end of the last read directory */
738
    if(s->first_file_mapping>0) {
739
        mapping_t* mapping=array_get(&(s->mapping),s->first_file_mapping-1);
740
        mapping->end=cluster;
741
    }
742

    
743
    for(i=1;i<s->mapping.next;i++) {
744
        mapping_t* mapping=array_get(&(s->mapping),i);
745
        direntry_t* direntry=array_get(&(s->directory),mapping->dir_index);
746
        if(mapping->mode==MODE_DIRECTORY) {
747
            /* directory */
748
            int i;
749
#ifdef DEBUG
750
            fprintf(stderr,"assert: %s %d < %d\n",mapping->filename,(int)mapping->begin,(int)mapping->end);
751
#endif
752
            assert(mapping->begin<mapping->end);
753
            for(i=mapping->begin;i<mapping->end-1;i++)
754
                fat_set(s,i,i+1);
755
            fat_set(s,i,0x7fffffff);
756
        } else {
757
            /* as the space is virtual, we can be sloppy about it */
758
            unsigned int end_cluster=cluster+mapping->end/s->cluster_size;
759

    
760
            if(end_cluster>=s->cluster_count) {
761
                fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type);
762
                return -1;
763
            }
764
            mapping->begin=cluster;
765
            mapping->mode=MODE_NORMAL;
766
            mapping->offset=0;
767
            direntry->size=cpu_to_le32(mapping->end);
768
            if(direntry->size==0) {
769
                direntry->begin=0;
770
                mapping->end=cluster;
771
                continue;
772
            }
773

    
774
            direntry->begin=cpu_to_le16(cluster);
775
            mapping->end=end_cluster+1;
776
            for(;cluster<end_cluster;cluster++)
777
                fat_set(s,cluster,cluster+1);
778
            fat_set(s,cluster,0x7fffffff);
779
            cluster++;
780
        }
781
    }
782

    
783
    s->current_mapping=0;
784

    
785
    bootsector->jump[0]=0xeb;
786
    bootsector->jump[1]=0x3e;
787
    bootsector->jump[2]=0x90;
788
    memcpy(bootsector->name,"QEMU    ",8);
789
    bootsector->sector_size=cpu_to_le16(0x200);
790
    bootsector->sectors_per_cluster=s->sectors_per_cluster;
791
    bootsector->reserved_sectors=cpu_to_le16(1);
792
    bootsector->number_of_fats=0x2; /* number of FATs */
793
    bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
794
    bootsector->zero=0;
795
    bootsector->media_type=(s->first_sectors_number==1?0xf0:0xf8); /* media descriptor */
796
    bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
797
    bootsector->sectors_per_track=cpu_to_le16(0x3f);
798
    bootsector->number_of_heads=cpu_to_le16(0x10);
799
    bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
800
    /* TODO: if FAT32, adjust */
801
    bootsector->total_sectors=cpu_to_le32(s->sector_count);
802

    
803
    /* TODO: if FAT32, this is wrong */
804
    bootsector->u.fat16.drive_number=0x80; /* assume this is hda (TODO) */
805
    bootsector->u.fat16.current_head=0;
806
    bootsector->u.fat16.signature=0x29;
807
    bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
808

    
809
    memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
810
    memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12   ":s->fat_type==16?"FAT16   ":"FAT32   "),8);
811
    bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
812

    
813
    return 0;
814
}
815

    
816
static int vvfat_open(BlockDriverState *bs, const char* dirname)
817
{
818
    BDRVVVFATState *s = bs->opaque;
819
    int i;
820

    
821
    /* TODO: automatically determine which FAT type */
822
    s->fat_type=16;
823
    s->sector_count=0xec04f;
824

    
825
    s->current_cluster=0xffffffff;
826
    s->first_file_mapping=0;
827

    
828
    /* TODO: if simulating a floppy, this is 1, because there is no partition table */
829
    s->first_sectors_number=0x40;
830
    
831
    if (strstart(dirname, "fat:", &dirname)) {
832
        /* read only is the default for safety */
833
        bs->read_only = 1;
834
    } else if (strstart(dirname, "fatrw:", &dirname)) {
835
        /* development only for now */
836
        bs->read_only = 0;
837
    } else {
838
        return -1;
839
    }
840
    if(init_directory(s,dirname))
841
        return -1;
842

    
843
    if(s->first_sectors_number==0x40)
844
        init_mbr(s);
845

    
846
    /* TODO: this could be wrong for FAT32 */
847
    bs->cyls=1023; bs->heads=15; bs->secs=63;
848
    bs->total_sectors=bs->cyls*bs->heads*bs->secs;
849

    
850
    /* write support */
851
    for(i=0;i<3;i++)
852
        s->action[i]=WRITE_UNDEFINED;
853
    return 0;
854
}
855

    
856
static inline void vvfat_close_current_file(BDRVVVFATState *s)
857
{
858
    if(s->current_mapping) {
859
        s->current_mapping = 0;
860
        close(s->current_fd);
861
    }
862
}
863

    
864
/* mappings between index1 and index2-1 are supposed to be ordered
865
 * return value is the index of the last mapping for which end>cluster_num
866
 */
867
static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
868
{
869
    int index3=index1+1;
870
    //fprintf(stderr,"find_aux: cluster_num=%d, index1=%d,index2=%d\n",cluster_num,index1,index2);
871
    while(1) {
872
        mapping_t* mapping;
873
        index3=(index1+index2)/2;
874
        mapping=array_get(&(s->mapping),index3);
875
        //fprintf(stderr,"index3: %d = (%d+%d)/2, end: %d\n",index3,index1,index2,(int)mapping->end);
876
        if(mapping->end>cluster_num) {
877
            assert(index2!=index3 || index2==0);
878
            if(index2==index3)
879
                return index2;
880
            index2=index3;
881
        } else {
882
            if(index1==index3)
883
                return index2;
884
            index1=index3;
885
        }
886
        assert(index1<=index2);
887
    }
888
}
889

    
890
static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
891
{
892
    int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
893
    mapping_t* mapping;
894
    if(index>=s->mapping.next)
895
        return 0;
896
    mapping=array_get(&(s->mapping),index);
897
    if(mapping->begin>cluster_num)
898
        return 0;
899
    return mapping;
900
}
901

    
902
static int open_file(BDRVVVFATState* s,mapping_t* mapping,int flags)
903
{
904
    if(!mapping)
905
        return -1;
906
    assert(flags==O_RDONLY || flags==O_RDWR);
907
    if(!s->current_mapping ||
908
            strcmp(s->current_mapping->filename,mapping->filename) ||
909
            (flags==O_RDWR && !s->current_fd_is_writable)) {
910
        /* open file */
911
        int fd = open(mapping->filename, flags | O_BINARY | O_LARGEFILE);
912
        if(fd<0)
913
            return -1;
914
        vvfat_close_current_file(s);
915
        s->current_fd = fd;
916
        s->current_fd_is_writable = (flags==O_RDWR?-1:0);
917
        s->current_mapping = mapping;
918
    }
919
    return 0;
920
}
921

    
922
static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
923
{
924
    if(s->current_cluster != cluster_num) {
925
        int result=0;
926
        off_t offset;
927
        if(!s->current_mapping
928
                || s->current_mapping->begin>cluster_num
929
                || s->current_mapping->end<=cluster_num) {
930
            /* binary search of mappings for file */
931
            mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
932
            if(open_file(s,mapping,O_RDONLY))
933
                return -2;
934
        }
935

    
936
        offset=s->cluster_size*(cluster_num-s->current_mapping->begin+s->current_mapping->offset);
937
        if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
938
            return -3;
939
        result=read(s->current_fd,s->cluster,s->cluster_size);
940
        if(result<0) {
941
            s->current_cluster = -1;
942
            return -1;
943
        }
944
        s->current_cluster = cluster_num;
945
    }
946
    return 0;
947
}
948

    
949
static int vvfat_read(BlockDriverState *bs, int64_t sector_num, 
950
                    uint8_t *buf, int nb_sectors)
951
{
952
    BDRVVVFATState *s = bs->opaque;
953
    int i;
954

    
955
    //    fprintf(stderr,"vvfat_read: sector %d+%d\n",(int)sector_num,nb_sectors);
956

    
957
    for(i=0;i<nb_sectors;i++,sector_num++) {
958
        if(sector_num<s->faked_sectors) {
959
                if(sector_num<s->first_sectors_number)
960
                    memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
961
                else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
962
                        memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
963
                else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
964
                        memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
965
                else if(sector_num-s->first_sectors_number-s->sectors_per_fat*2<s->sectors_for_directory)
966
                        memcpy(buf+i*0x200,&(s->directory.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat*2)*0x200]),0x200);
967
        } else {
968
            uint32_t sector=sector_num-s->first_sectors_number-s->sectors_per_fat*2,
969
                sector_offset_in_cluster=(sector%s->sectors_per_cluster),
970
                cluster_num=sector/s->sectors_per_cluster;
971
                if(read_cluster(s, cluster_num) != 0) {
972
                        //fprintf(stderr,"failed to read cluster %d\n",(int)cluster_num);
973
                        // TODO: strict: return -1;
974
                        memset(buf+i*0x200,0,0x200);
975
                        continue;
976
                }
977
                memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
978
        }
979
    }
980
    return 0;
981
}
982

    
983
static void print_direntry(direntry_t* direntry)
984
{
985
    if(!direntry)
986
        return;
987
    if(direntry->attributes==0xf) {
988
        unsigned char* c=(unsigned char*)direntry;
989
        int i;
990
        for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
991
            fputc(c[i],stderr);
992
        for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
993
            fputc(c[i],stderr);
994
        for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
995
            fputc(c[i],stderr);
996
        fputc('\n',stderr);
997
    } else {
998
        int i;
999
        for(i=0;i<11;i++)
1000
            fputc(direntry->name[i],stderr);
1001
        fprintf(stderr,"attributes=0x%02x begin=%d size=%d\n",
1002
                direntry->attributes,
1003
                direntry->begin,direntry->size);
1004
    }
1005
}
1006

    
1007
static void print_changed_sector(BlockDriverState *bs,int64_t sector_num,const uint8_t *buf)
1008
{
1009
    BDRVVVFATState *s = bs->opaque;
1010

    
1011
    if(sector_num<s->first_sectors_number)
1012
        return;
1013
    if(sector_num<s->first_sectors_number+s->sectors_per_fat*2) {
1014
        int first=((sector_num-s->first_sectors_number)%s->sectors_per_fat);
1015
        int first_fat_entry=first*0x200/2;
1016
        int i;
1017

    
1018
        fprintf(stderr, "fat:\n");
1019
        for(i=0;i<0x200;i+=2) {
1020
            uint16_t* f=array_get(&(s->fat),first_fat_entry+i/2);
1021
            if(memcmp(buf+i,f,2))
1022
                fprintf(stderr,"%d(%d->%d) ",first_fat_entry+i/2,*f,*(uint16_t*)(buf+i));
1023
        }
1024
        fprintf(stderr, "\n");
1025
    } else if(sector_num<s->faked_sectors) {
1026
        direntry_t* d=(direntry_t*)buf;
1027
        int i;
1028
        fprintf(stderr, "directory:\n");
1029
        for(i=0;i<0x200/sizeof(direntry_t);i++) {
1030
            direntry_t* d_old=(direntry_t*)(s->directory.pointer+0x200*(sector_num-s->first_sectors_number-s->sectors_per_fat*2)+i*sizeof(direntry_t));
1031
            if(memcmp(d+i,d_old,sizeof(direntry_t))) {
1032
                fprintf(stderr, "old: "); print_direntry(d_old);
1033
                fprintf(stderr, "new: "); print_direntry(d+i);
1034
                fprintf(stderr, "\n");
1035
            }
1036
        }
1037
    } else {
1038
        int sec=(sector_num-s->first_sectors_number-2*s->sectors_per_fat);
1039
        fprintf(stderr, "\tcluster: %d(+%d sectors)\n",sec/s->sectors_per_cluster,sec%s->sectors_per_cluster);
1040
    }
1041
}
1042

    
1043
char direntry_is_free(const direntry_t* direntry)
1044
{
1045
    return direntry->name[0]==0 || direntry->name[0]==0xe5;
1046
}
1047

    
1048
/* TODO: use this everywhere */
1049
static inline uint32_t begin_of_direntry(direntry_t* direntry)
1050
{
1051
    return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
1052
}
1053

    
1054
int consistency_check1(BDRVVVFATState *s) {
1055
    /* check all mappings */
1056
    int i;
1057
    for(i=0;i<s->mapping.next;i++) {
1058
        mapping_t* mapping=array_get(&(s->mapping),i);
1059
        int j;
1060
        for(j=mapping->begin;j<mapping->end-1;j++)
1061
            assert(fat_get(s,j)==j+1);
1062
        assert(fat_get(s,j)==(0x7fffffff&s->max_fat_value));
1063
    }
1064
    return 0;
1065
}
1066

    
1067
int consistency_check2(BDRVVVFATState *s) {
1068
    /* check fat entries: consecutive fat entries should be mapped in one mapping */
1069
    int i;
1070
    /* TODO: i=0 (mappings for direntries have to be sorted) */
1071
    for(i=s->sectors_for_directory/s->sectors_per_cluster;i<s->fat.next-1;i++) {
1072
        uint32_t j=fat_get(s,i);
1073
        if(j!=i+1 && j!=0 && !fat_eof(s,j)) {
1074
            mapping_t* mapping=find_mapping_for_cluster(s,i+1);
1075
            assert(mapping->begin==i+1);
1076
        }
1077
    }
1078
    return 0;
1079
}
1080

    
1081
int consistency_check3(BDRVVVFATState *s) {
1082
    /* check that for each file there is exactly one mapping per cluster */
1083
    int i,count_non_next=0;
1084
    for(i=0;i<s->mapping.next;i++) {
1085
        mapping_t* mapping=array_get(&(s->mapping),i);
1086
        /* TODO: when directories are correctly adapted, add them here */
1087
        assert(mapping->begin<mapping->end);
1088
        if(mapping->mode==MODE_NORMAL) {
1089
            int j,count=0,count_next=0;
1090
            for(j=0;j<s->mapping.next;j++) {
1091
                mapping_t* other=array_get(&(s->mapping),j);
1092
                if(mapping->begin<other->end&&mapping->end>other->begin)
1093
                    count++;
1094
                if(mapping->end==other->begin)
1095
                    count_next++;
1096
            }
1097
            assert(count==1); /* no overlapping mappings */
1098
            assert(count_next==1 || count_next==0); /* every mapping except the last one has a successor */
1099
            if(!count_next)
1100
                count_non_next++;
1101
        }
1102
    }
1103
    assert(count_non_next==1); /* only one last mapping */
1104
    return 0;
1105
}
1106

    
1107
static inline commit_t* commit_get_next(BDRVVVFATState* s)
1108
{
1109
    commit_t* commit=array_get_next(&(s->commit));
1110
    if((commit->buf=malloc(s->cluster_size))==0) {
1111
        /* out of memory */
1112
        s->commit.next--;
1113
        return 0;
1114
    }
1115
    return commit;
1116
}
1117

    
1118
int commit_remove(BDRVVVFATState* s,commit_t* commit)
1119
{
1120
    int index=commit-(commit_t*)s->commit.pointer;
1121
    free(commit->buf);
1122
    if(array_roll(&(s->commit),s->commit.next-1,index,1))
1123
        return -1;
1124
    s->commit.next--;
1125
    return 0;
1126
}
1127

    
1128
/* TODO: the plan for write support:
1129
 *
1130
 * it seems that the direntries are written first, then the data is committed
1131
 * to the free sectors, then fat 1 is updated, then fat2.
1132
 *
1133
 * Plan: when sectors are written, do the following:
1134
 *
1135
 * - if they are in a directory, check if the entry has changed. if yes,
1136
 *   look what has changed (different strategies for name, begin & size).
1137
 *
1138
 *   if it is new (old entry is only 0's or has E5 at the start), create it,
1139
 *   and also create mapping, but in a special mode "undefined" (TODO),
1140
 *   because we cannot know which clusters belong to it yet.
1141
 *
1142
 *   if it is zeroed, or has E5 at the start, look if has just moved. If yes,
1143
 *   copy the entry to the new position. If no, delete the file.
1144
 *
1145
 * - if they are in data, and the cluster is undefined, add it to the commit
1146
 *   list. if the cluster is defined (find_mapping), then write it into the
1147
 *   corresponding file.
1148
 *
1149
 *   If it is the last cluster (TODO: add a function
1150
 *   fat_get(s,cluster); ), make sure not to write a complete cluster_size.
1151
 *
1152
 *   If the data is in current_cluster, update s->cluster.
1153
 *
1154
 * - if they are in fat 1, update mappings, look in the commit list
1155
 *   (assertions!) and if the cluster is now known (or changed from undefined
1156
 *   state to defined state, like when begin or size changed in a direntry),
1157
 *   write it.
1158
 *
1159
 * - if they are in fat 2, make sure they match with current fat.
1160
 *
1161
 */
1162

    
1163
void mapping_modify_from_direntry(BDRVVVFATState* s,mapping_t* mapping,direntry_t* direntry)
1164
{
1165
    int begin=le16_to_cpu(direntry->begin),
1166
        end=begin+le32_to_cpu(direntry->size)/s->cluster_size+1,
1167
        i;
1168
    mapping->mode = MODE_MODIFIED;
1169
    /* TODO: what if begin==0 (size==0)? */
1170
    mapping->begin = begin;
1171
    /* TODO: why not just mapping->end = begin+1 ? */
1172
    for(i=begin+1;i<end && (fat_get(s,i)==0 || fat_get(s,i)==i+1);i++);
1173
    mapping->end = i;
1174
}
1175

    
1176
mapping_t* find_mapping_for_direntry(BDRVVVFATState* s,direntry_t* direntry)
1177
{
1178
    int i;
1179
    int dir_index=direntry-((direntry_t*)s->directory.pointer);
1180
    
1181
    /* TODO: support allocation for new clusters for directories (new/larger directory */
1182
    assert(dir_index<0x200/0x20*s->sectors_for_directory);
1183
    
1184
    for(i=0;i<s->mapping.next;i++) {
1185
        mapping_t* mapping=array_get(&(s->mapping),i);
1186
        if(mapping->dir_index==dir_index && mapping->offset==0 &&
1187
                mapping->mode!=MODE_UNDEFINED)
1188
            return mapping;
1189
    }
1190
    return 0;
1191
}
1192

    
1193
static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
1194
{
1195
    return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)/s->sectors_per_cluster;
1196
}
1197

    
1198
static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num)
1199
{
1200
    return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster;
1201
}
1202

    
1203
static commit_t* get_commit_for_cluster(BDRVVVFATState* s,uint32_t cluster_num)
1204
{
1205
    int i;
1206
    for(i=0;i<s->commit.next;i++) {
1207
        commit_t* commit=array_get(&(s->commit),i);
1208
        if(commit->cluster_num==cluster_num)
1209
            return commit;
1210
    }
1211
    return 0;
1212
}
1213

    
1214
static inline commit_t* create_or_get_commit_for_sector(BDRVVVFATState* s,off_t sector_num)
1215
{
1216
    int i;
1217
    commit_t* commit;
1218
    uint32_t cluster_num=sector2cluster(s,sector_num);
1219

    
1220
    for(i=0;i<s->commit.next;i++) {
1221
        commit=array_get(&(s->commit),i);
1222
        if(commit->cluster_num==cluster_num)
1223
            return commit;
1224
    }
1225

    
1226
    commit=commit_get_next(s);
1227
    commit->cluster_num=cluster_num;
1228
    /* we can ignore read errors here */
1229
    read_cluster(s,cluster_num);
1230
    memcpy(commit->buf,s->cluster,s->cluster_size);
1231
    return commit;
1232
}
1233

    
1234
static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping)
1235
{
1236
    if(mapping->mode==MODE_UNDEFINED)
1237
        return 0;
1238
    if(mapping->dir_index>=0x200/0x20*s->sectors_for_directory)
1239
        return 0;
1240
    return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index);
1241
}
1242

    
1243
static void print_mappings(BDRVVVFATState* s)
1244
{
1245
    int i;
1246
    fprintf(stderr,"mapping:\n");
1247
    for(i=0;i<s->mapping.next;i++) {
1248
        mapping_t* m=array_get(&(s->mapping),i);
1249
        direntry_t* d=get_direntry_for_mapping(s,m);
1250
        fprintf(stderr,"%02d %d-%d (%d) %s (dir: %d)",i,(int)m->begin,(int)m->end,(int)m->offset,m->filename,m->dir_index);
1251
        print_direntry(d);
1252
        fprintf(stderr,"\n");
1253
    }
1254
    fprintf(stderr,"mappings end.\n");
1255
}
1256

    
1257
/* TODO: statify all functions */
1258

    
1259
/* This function is only meant for file contents.
1260
 * It will return an error if used for other sectors. */
1261
static int write_cluster(BDRVVVFATState* s,uint32_t cluster_num,const uint8_t* buf)
1262
{
1263
    /* sector_offset is the sector_num relative to the first cluster */
1264
    mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
1265
    direntry_t* direntry;
1266
    int next_cluster,write_size,last_cluster;
1267
    off_t offset;
1268

    
1269
    /* if this cluster is free, return error */
1270
    next_cluster=fat_get(s,cluster_num);
1271
    if(next_cluster<2)
1272
        return -1;
1273
    
1274
    /* TODO: MODE_DIRECTORY */
1275
    if(!mapping || mapping->mode==MODE_UNDEFINED || mapping->mode==MODE_DIRECTORY)
1276
        return -1;
1277
    direntry=get_direntry_for_mapping(s,mapping);
1278
    if(!direntry)
1279
        return -2;
1280

    
1281
    /* get size to write */
1282
    last_cluster=fat_eof(s,next_cluster);
1283
    write_size=!last_cluster?s->cluster_size:
1284
        (le32_to_cpu(direntry->size)%s->cluster_size);
1285
    if(write_size<=0)
1286
        return 0;
1287
    //fprintf(stderr,"next_cluster: %d (%d), write_size: %d, %d, %d\n",next_cluster,s->max_fat_value-8,write_size,direntry->size,s->cluster_size);
1288

    
1289
    if(open_file(s,mapping,O_RDWR))
1290
        return -4;
1291
   
1292
    offset=(cluster_num-mapping->begin+mapping->offset)*s->cluster_size;
1293
    if(lseek(s->current_fd,offset,SEEK_SET)!=offset)
1294
        return -3;
1295
    if(write(s->current_fd,buf,write_size)!=write_size) {
1296
        lseek(s->current_fd,0,SEEK_END);
1297
        vvfat_close_current_file(s);
1298
        return -2;
1299
    }
1300

    
1301
    /* seek to end of file, so it doesn't get truncated */
1302
    if(!last_cluster)
1303
        lseek(s->current_fd,0,SEEK_END);
1304
    else {
1305
        ftruncate(s->current_fd,le32_to_cpu(direntry->size));
1306
        vvfat_close_current_file(s);
1307
    }
1308

    
1309
    /* update s->cluster if necessary */
1310
    if(cluster_num==s->current_cluster && s->cluster!=buf)
1311
        memcpy(s->cluster,buf,s->cluster_size);
1312

    
1313
    return 0;
1314
}
1315

    
1316
/* this function returns !=0 on error */
1317
int mapping_is_consistent(BDRVVVFATState* s,mapping_t* mapping)
1318
{
1319
    direntry_t* direntry=get_direntry_for_mapping(s,mapping);
1320
    uint32_t cluster_count=0;
1321
    int commit_count=0; /* number of commits for this file (we also write incomplete files; think "append") */
1322
    //fprintf(stderr,"check direntry for %s\n",mapping->filename);
1323
    while(mapping) {
1324
        int i;
1325
        assert(mapping->begin<mapping->end);
1326
        for(i=mapping->begin;i<mapping->end-1;i++) {
1327
            if(i<=0 || fat_get(s,i)!=i+1) {
1328
                /*fprintf(stderr,"the fat mapping of %d is not %d, but %d\n",
1329
                        i,i+1,fat_get(s,i));*/
1330
                return -1;
1331
            }
1332
            if(get_commit_for_cluster(s,i))
1333
                commit_count++;
1334
        }
1335
        if(get_commit_for_cluster(s,i))
1336
            commit_count++;
1337

    
1338
        cluster_count+=mapping->end-mapping->begin;
1339
        
1340
        i=fat_get(s,mapping->end-1);
1341
        if(fat_eof(s,i))
1342
            break;
1343

    
1344
        mapping=find_mapping_for_cluster(s,i);
1345
        if(!mapping) {
1346
            //fprintf(stderr,"No mapping found for %d\n",i);
1347
            print_mappings(s);
1348
            return -2;
1349
        }
1350
    }
1351

    
1352
    if(cluster_count!=(le32_to_cpu(direntry->size)+s->cluster_size-1)/s->cluster_size) {
1353
        //fprintf(stderr,"cluster_count is %d, but size is %d\n",cluster_count,le32_to_cpu(direntry->size));
1354
        return -3;
1355
    }
1356

    
1357
    if(commit_count==0)
1358
        return -4;
1359

    
1360
    //fprintf(stderr,"okay\n");
1361
    return 0;
1362
}
1363

    
1364
/* TODO: remember what comes third, and what's first in this OS:
1365
 * FAT, direntry or data.
1366
 * If the last written sector is either last in cluster or sector_num+nb_sectors-1,
1367
 *         - commit every cluster for this file if mapping_is_consistent()==0
1368
 *         - if the last written sector is first_action, and last_action=third_action, clear commit
1369
 */
1370

    
1371
static int commit_cluster_aux(BDRVVVFATState* s,commit_t* commit)
1372
{
1373
    int result=write_cluster(s,commit->cluster_num,commit->buf);
1374
    return result;
1375
}
1376

    
1377

    
1378
static int commit_cluster(BDRVVVFATState* s,uint32_t cluster_num)
1379
{
1380
    commit_t* commit;
1381

    
1382
    /* commit the sectors of this cluster */
1383
    commit=get_commit_for_cluster(s,cluster_num);
1384
    if(commit)
1385
        return commit_cluster_aux(s,commit);
1386
    return 0;
1387
}
1388

    
1389
/* this function checks the consistency for the direntry which belongs to
1390
 * the mapping. if everything is found consistent, the data is committed.
1391
 * this returns 0 if no error occurred (even if inconsistencies were found) */
1392
static inline int commit_data_if_consistent(BDRVVVFATState* s,mapping_t* mapping,write_action_t action)
1393
{
1394
    direntry_t* direntry;
1395
    
1396
    if(!mapping)
1397
        return 0;
1398

    
1399
    //fprintf(stderr,"7\n");
1400
#define d(x) fprintf(stderr,#x "\n")
1401
    direntry=get_direntry_for_mapping(s,mapping);
1402

    
1403
    //d(8);
1404

    
1405
    assert(action==WRITE_FAT || action==WRITE_DIRENTRY || action==WRITE_DATA);
1406

    
1407
    //d(9);
1408
    //fprintf(stderr,"mapping: 0x%x s=0x%x\n",(uint32_t)mapping,(uint32_t)s);
1409
    /*fprintf(stderr,"commit? file=%s, action=%s\n",
1410
            mapping->filename,action==WRITE_FAT?"fat":action==WRITE_DIRENTRY?"direntry":"data");*/
1411

    
1412
    //d(10);
1413
    if(s->action[2]==WRITE_UNDEFINED) {
1414
        int i;
1415
        for(i=2;i>0 && s->action[i-1]==WRITE_UNDEFINED;i--);
1416
        if(i>0 && action!=s->action[i-1])
1417
            s->action[i]=action;
1418
        assert(i<2 || s->action[0]!=s->action[2]);
1419
    }
1420
    //d(11);
1421
    
1422
    if(mapping_is_consistent(s,mapping)==0) {
1423
        uint32_t cluster_num=begin_of_direntry(direntry);
1424
        off_t remaining_bytes=le32_to_cpu(direntry->size);
1425
        //fprintf(stderr,"the data for %s was found consistent\n",mapping->filename);
1426
        while(remaining_bytes>0) {
1427
            commit_t* commit=get_commit_for_cluster(s,cluster_num);
1428
            if(!commit)
1429
                continue;
1430
                
1431
            //fprintf(stderr,"commit_cluster %d (%d), remaining: %d\n",cluster_num,s->max_fat_value-15,(int)remaining_bytes);
1432
            assert(cluster_num>1);
1433
            assert(cluster_num<s->max_fat_value-15);
1434
            if(commit_cluster(s,cluster_num)) {
1435
                fprintf(stderr,"error committing cluster %d\n",cluster_num);
1436
                return -1;
1437
            }
1438
            cluster_num=fat_get(s,cluster_num);
1439
            remaining_bytes-=s->cluster_size;
1440
            /* TODO: if(action==s->action[2]) {
1441
                commit_t* commit=get_commit_for_cluster(s,cluster_num);
1442
                commit_remove(s,commit);
1443
            } */
1444
        }
1445
    }
1446
    //print_mappings(s);
1447
    //fprintf(stderr,"finish vvfat_write\n");
1448
    return 0;
1449
}
1450

    
1451
static int vvfat_write(BlockDriverState *bs, int64_t sector_num, 
1452
                    const uint8_t *buf, int nb_sectors)
1453
{
1454
    BDRVVVFATState *s = bs->opaque;
1455
    int i;
1456

    
1457
    /* fprintf(stderr,"vvfat_write %d+%d (%s)\n",(int)sector_num,nb_sectors,
1458
                    (sector_num>=s->faked_sectors?"data":
1459
                     (sector_num>=s->first_sectors_number+2*s->sectors_per_fat?"directory":
1460
                      (sector_num>=s->first_sectors_number+s->sectors_per_fat?"fat 2":
1461
                       (sector_num>=s->first_sectors_number?"fat 1":"boot sector"))))); */
1462

    
1463
    for(i=0;i<nb_sectors;i++,sector_num++,buf+=0x200) {
1464
        print_changed_sector(bs,sector_num,buf);
1465

    
1466
        if(sector_num<s->first_sectors_number) {
1467
            /* change the bootsector or partition table? no! */
1468
            return -1;
1469
        } else if(sector_num<s->first_sectors_number+s->sectors_per_fat) {
1470
            /* FAT 1 */
1471
            int fat_entries_per_cluster=s->cluster_size*8/s->fat_type;
1472
            int first_cluster=(sector_num-s->first_sectors_number)*fat_entries_per_cluster,i;
1473
            mapping_t* mapping=0;
1474

    
1475
            /* write back */
1476
            memcpy(s->fat.pointer+0x200*(sector_num-s->first_sectors_number),
1477
                    buf,0x200);
1478

    
1479
            /* for each changed FAT entry, */
1480
            for(i=0;i<fat_entries_per_cluster;i++) {
1481
                int new_value;
1482
                
1483
                /* TODO: MODE_DIRENTRY */
1484
                if(first_cluster+i<s->sectors_for_directory/s->sectors_per_cluster)
1485
                    continue;
1486

    
1487
                new_value=fat_get(s,first_cluster+i);
1488

    
1489
                /* check the current fat entry */
1490
                if(new_value<2 || (new_value>=s->max_fat_value-0xf && !fat_eof(s,new_value))) {
1491
                    /* free, reserved or bad cluster */
1492
                    mapping=find_mapping_for_cluster(s,first_cluster+i);
1493
                    //assert(!mapping || mapping->mode==MODE_DELETED);
1494
                    if(mapping && mapping->mode==MODE_DELETED &&
1495
                            first_cluster+i+1==mapping->end)
1496
                        array_remove(&(s->mapping),mapping-(mapping_t*)s->mapping.pointer);
1497
                    mapping=0;
1498
                    continue;
1499
                }
1500

    
1501
                /* get the mapping for the current entry */
1502
                if(!mapping || mapping->begin>new_value || mapping->end<=new_value) {
1503
                    mapping=find_mapping_for_cluster(s,first_cluster+i);
1504
                }
1505

    
1506
                print_mappings(s);
1507
                fprintf(stderr,"fat_get(%d)=%d\n",first_cluster+i,new_value);
1508
                /* TODO: what if there's no mapping? this is valid. */
1509
                /* TODO: refactor the rest of this clause so it can be called when the direntry changes, too */
1510
                assert(mapping);
1511

    
1512
                if(new_value>1 && new_value<s->max_fat_value-0xf) {
1513
                    /* the cluster new_value points to is valid */
1514

    
1515
                    if(first_cluster+i+1==new_value) {
1516
                        /* consecutive cluster */
1517
                        if(mapping->end<=new_value)
1518
                            mapping->end=new_value+1;
1519
                    } else {
1520
                        mapping_t* next_mapping;
1521
                        
1522
                        /* the current mapping ends here */
1523
                        mapping->end=first_cluster+i+1;
1524
                        
1525
                        /* the next mapping */
1526
                        next_mapping=find_mapping_for_cluster(s,new_value);
1527
                        if(next_mapping) {
1528
                            assert(mapping!=next_mapping);
1529
                            /* assert next mapping's filename is the same */
1530
                            assert(next_mapping->filename==mapping->filename);
1531
                            assert(next_mapping->dir_index==mapping->dir_index);
1532
                            /* assert next mapping is MODIFIED or UNDEFINED */
1533
                            assert(next_mapping->mode==MODE_MODIFIED || next_mapping->mode==MODE_UNDEFINED);
1534
                        } else {
1535
                            int index=find_mapping_for_cluster_aux(s,new_value,0,s->mapping.next);
1536
                            next_mapping=array_insert(&(s->mapping),index,1);
1537
                            next_mapping->filename=mapping->filename;
1538
                            next_mapping->dir_index=mapping->dir_index;
1539
                            next_mapping->mode=MODE_MODIFIED;
1540
                            next_mapping->begin=0;
1541
                        }
1542
                        /* adjust offset of next mapping */
1543
                        next_mapping->offset=mapping->offset+mapping->end-mapping->begin;
1544
                        /* set begin and possible end */
1545
                        if(next_mapping->begin!=new_value) {
1546
                            next_mapping->begin=new_value;
1547
                            next_mapping->end=new_value+1;
1548
                        }
1549
                        if(commit_data_if_consistent(s,mapping,WRITE_FAT))
1550
                            return -4;
1551
                        mapping=0;
1552
                    }
1553
                } else if(fat_eof(s,new_value)) {
1554
                    /* the last cluster of the file */
1555
                    mapping->end=first_cluster+i+1;
1556
                    if(commit_data_if_consistent(s,mapping,WRITE_FAT))
1557
                        return -4;
1558
                    mapping=0;
1559
                }
1560
            }
1561
        } else if(sector_num<s->first_sectors_number+2*s->sectors_per_fat) {
1562
            /* FAT 2: check if it is the same as FAT 1 */
1563
            if(memcmp(array_get(&(s->fat),sector_num-s->first_sectors_number),buf,0x200))
1564
                return -1; /* mismatch */
1565
        } else if(sector_num<s->faked_sectors) {
1566
            /* direntry */
1567
            /* - if they are in a directory, check if the entry has changed.
1568
             *   if yes, look what has changed (different strategies for name,
1569
             *   begin & size).
1570
             *
1571
             *   if it is new (old entry is only 0's or has E5 at the start),
1572
             *   create it, and also create mapping, but in a special mode
1573
             *   "undefined", because we cannot know which clusters belong
1574
             *   to it yet.
1575
             *
1576
             *   if it is zeroed, or has E5 at the start, look if has just
1577
             *   moved. If yes, copy the entry to the new position. If no,
1578
             *   delete the file.
1579
             */
1580
            mapping_t* dir_mapping=find_mapping_for_cluster(s,sector2cluster(s,sector_num));
1581
            direntry_t *original=array_get(&(s->directory),sector_num-s->first_sectors_number-2*s->sectors_per_fat);
1582
            direntry_t *new_=(direntry_t*)buf;
1583
            int first_dir_index=(sector_num-s->first_sectors_number-2*s->sectors_per_fat)*0x200/0x20;
1584
            int j;
1585

    
1586
#if 0
1587
            fprintf(stderr,"direntry: consistency check\n");
1588

1589
            if(s->commit.next==0) {
1590
                consistency_check1(s);
1591
                consistency_check2(s);
1592
                consistency_check3(s);
1593
            }
1594
#endif
1595

    
1596
            assert(sizeof(direntry_t)==0x20);
1597

    
1598
            for(j=0;j<0x200/0x20;j++) {
1599
                //fprintf(stderr,"compare direntry %d: 0x%x,0x%x\n",j,(uint32_t)original+j,(uint32_t)new_+j);
1600
                if(memcmp(original+j,new_+j,sizeof(direntry_t))) {
1601
                    //fprintf(stderr,"different\n");
1602
                    /* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */
1603
                    if(direntry_is_free(original+j)) {
1604
                        mapping_t* mapping;
1605
                        char buffer[4096];
1606
                        int fd,i;
1607

    
1608
                        if(new_[j].attributes==0xf)
1609
                            continue; /* long entry */
1610

    
1611
                        print_mappings(s);
1612
                        //fprintf(stderr,"sector: %d cluster: %d\n",(int)sector_num,(int)sector2cluster(s,sector_num));
1613

    
1614
                        /* construct absolute path */
1615
                        strncpy(buffer,dir_mapping->filename,4096);
1616
                        i=strlen(buffer);
1617
                        if(i+2>=4096)
1618
                                return -1;
1619
                        buffer[i]='/';
1620
                        if(long2unix_name(buffer+i+1,4096-i-1,new_+j))
1621
                                return -2;
1622

    
1623
                        /* new file/directory */
1624
                        if(new_[j].attributes&0x10) {
1625
#ifdef _WIN32
1626
#define SEVENFIVEFIVE
1627
#else
1628
#define SEVENFIVEFIVE ,0755
1629
#endif
1630
                            if(mkdir(buffer SEVENFIVEFIVE))
1631
                                return -3;
1632
                            /* TODO: map direntry.begin as directory, together with new array_t direntries */
1633
                            assert(0);
1634
                        } else {
1635
                            fd=open(buffer,O_CREAT|O_EXCL,0644);
1636
                            if(!fd)
1637
                                return -3;
1638
                            close(fd);
1639
                        }
1640

    
1641
                        /* create mapping */
1642
                        i=find_mapping_for_cluster_aux(s,begin_of_direntry(new_+j),0,s->mapping.next);
1643
                        mapping=array_insert(&(s->mapping),i,1);
1644
                        mapping->filename=strdup(buffer);
1645
                        mapping->offset=0;
1646
                        /* back pointer to direntry */
1647
                        mapping->dir_index=first_dir_index+j;
1648
                        /* set mode to modified */
1649
                        mapping->mode=MODE_MODIFIED;
1650
                        /* set begin to direntry.begin */
1651
                        mapping->begin=begin_of_direntry(new_+j);
1652
                        /* set end to begin+1 */
1653
                        mapping->end=mapping->begin+1;
1654
                        /* commit file contents */
1655
                        if(commit_data_if_consistent(s,mapping,WRITE_DIRENTRY)) {
1656
                            fprintf(stderr,"error committing file contents for new file %s!\n",buffer);
1657
                            return -4;
1658
                        }
1659
                    } else if(direntry_is_free(new_+j)) {
1660
                        assert(0);
1661
                        /* TODO: delete file */
1662
                        /* TODO: write direntry */
1663
                        /* TODO: modify mapping: set mode=deleted */
1664
                    } else {
1665
                        /* modified file */
1666
                        mapping_t* mapping=0;
1667
                        /* if direntry.begin has changed,
1668
                         * set mode to modified,
1669
                         * adapt begin,
1670
                         * adapt end */
1671
                        /* TODO: handle rename */
1672
                        assert(!memcmp(new_[j].name,original[j].name,11));
1673
                        //fprintf(stderr,"1\n");
1674
                        if(new_[j].begin!=original[j].begin || new_[j].size/s->cluster_size!=original[j].size/s->cluster_size) {
1675
                        //fprintf(stderr,"2\n");
1676
                            mapping = find_mapping_for_direntry(s,original+j);
1677
                        //fprintf(stderr,"3\n");
1678
                            if(!mapping) /* this should never happen! */
1679
                                return -2;
1680
                            mapping_modify_from_direntry(s,mapping,new_+j);
1681
                            //fprintf(stderr,"4\n");
1682
                            if(commit_data_if_consistent(s,mapping,WRITE_DIRENTRY)) {
1683
                                fprintf(stderr,"big error\n");
1684
                                return -4;
1685
                            }
1686
                        }
1687
                        /* TODO: handle modified times and other attributes */
1688

    
1689
                        //fprintf(stderr,"5: mapping=0x%x, s=0x%x, s->mapping.pointer=0x%x\n",(uint32_t)mapping,(uint32_t)s,(uint32_t)s->mapping.pointer);
1690
                        //fprintf(stderr,"6\n");
1691
                    }
1692
                }
1693
            }
1694
            /* write back direntries */
1695
            memcpy(original,new_,0x200);
1696
        } else {
1697
            /* data */
1698
            off_t sector=sector_num-s->first_sectors_number-2*s->sectors_per_fat;
1699
            off_t cluster=sector/s->sectors_per_cluster;
1700
            mapping_t* mapping=find_mapping_for_cluster(s,cluster);
1701
            if(mapping && mapping->mode==MODE_DELETED)
1702
                return -3; /* this is an error: no writes to these clusters before committed */
1703
            {
1704
                /* as of yet, undefined: put into commits */
1705
                commit_t* commit=create_or_get_commit_for_sector(s,sector_num);
1706

    
1707
                if(!commit)
1708
                    return -1; /* out of memory */
1709
                memcpy(commit->buf+0x200*sector_offset_in_cluster(s,sector_num),buf,0x200);
1710

    
1711
                //fprintf(stderr,"mapping: 0x%x\n",(uint32_t)mapping);
1712
                if(commit_data_if_consistent(s,mapping,WRITE_DATA))
1713
                    return -4;
1714
            }
1715
        }
1716
    }
1717
    return 0;
1718
}
1719

    
1720
static void vvfat_close(BlockDriverState *bs)
1721
{
1722
    BDRVVVFATState *s = bs->opaque;
1723

    
1724
    vvfat_close_current_file(s);
1725
    array_free(&(s->fat));
1726
    array_free(&(s->directory));
1727
    array_free(&(s->mapping));
1728
    if(s->cluster)
1729
        free(s->cluster);
1730
}
1731

    
1732
BlockDriver bdrv_vvfat = {
1733
    "vvfat",
1734
    sizeof(BDRVVVFATState),
1735
    vvfat_probe,
1736
    vvfat_open,
1737
    vvfat_read,
1738
    vvfat_write,
1739
    vvfat_close,
1740
};
1741

    
1742