blob: 43004e53485894b0b33d61b0bf34a86401b9edbf [file] [log] [blame]
bellarda0464332005-12-18 18:29:50 +00001/* vim:set shiftwidth=4 ts=8: */
bellardde167e42005-04-28 21:15:08 +00002/*
3 * QEMU Block driver for virtual VFAT (shadows a local directory)
ths5fafdf22007-09-16 21:08:06 +00004 *
bellarda0464332005-12-18 18:29:50 +00005 * Copyright (c) 2004,2005 Johannes E. Schindelin
ths5fafdf22007-09-16 21:08:06 +00006 *
bellardde167e42005-04-28 21:15:08 +00007 * 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>
pbrookfaf07962007-11-11 02:51:17 +000028#include "qemu-common.h"
bellardde167e42005-04-28 21:15:08 +000029#include "block_int.h"
30
bellarda0464332005-12-18 18:29:50 +000031#ifndef S_IWGRP
32#define S_IWGRP 0
33#endif
34#ifndef S_IWOTH
35#define S_IWOTH 0
36#endif
bellardde167e42005-04-28 21:15:08 +000037
bellarda0464332005-12-18 18:29:50 +000038/* TODO: add ":bootsector=blabla.img:" */
39/* LATER TODO: add automatic boot sector generation from
40 BOOTEASY.ASM and Ranish Partition Manager
ths5fafdf22007-09-16 21:08:06 +000041 Note that DOS assumes the system files to be the first files in the
bellarda0464332005-12-18 18:29:50 +000042 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 */
bellardde167e42005-04-28 21:15:08 +000045
bellarda0464332005-12-18 18:29:50 +000046/* #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
bellard6bcb76c2006-09-09 12:03:20 +000064#define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0)
bellarda0464332005-12-18 18:29:50 +000065#endif
66
67#else
68
69#define DLOG(a)
70
71#endif
bellardde167e42005-04-28 21:15:08 +000072
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
bellarda0464332005-12-18 18:29:50 +000094/* does not automatically grow */
bellardde167e42005-04-28 21:15:08 +000095static inline void* array_get(array_t* array,unsigned int index) {
bellarda0464332005-12-18 18:29:50 +000096 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;
bellardde167e42005-04-28 21:15:08 +0000110 }
bellarda0464332005-12-18 18:29:50 +0000111
112 return 0;
bellardde167e42005-04-28 21:15:08 +0000113}
114
115static inline void* array_get_next(array_t* array) {
bellarda0464332005-12-18 18:29:50 +0000116 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
bellardde167e42005-04-28 21:15:08 +0000125 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;
ths3b46e622007-09-17 08:09:54 +0000156
bellardde167e42005-04-28 21:15:08 +0000157 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);
ths3b46e622007-09-17 08:09:54 +0000170
bellardde167e42005-04-28 21:15:08 +0000171 memcpy(to,buf,is*count);
172
173 free(buf);
174
175 return 0;
176}
177
bellarda0464332005-12-18 18:29:50 +0000178inline 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
bellardde167e42005-04-28 21:15:08 +0000189int array_remove(array_t* array,int index)
190{
bellarda0464332005-12-18 18:29:50 +0000191 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;
bellardde167e42005-04-28 21:15:08 +0000202}
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;
bellarda0464332005-12-18 18:29:50 +0000215 uint16_t total_sectors16;
bellardde167e42005-04-28 21:15:08 +0000216 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
thsb5700942007-09-25 14:47:03 +0000245typedef struct {
246 uint8_t head;
247 uint8_t sector;
248 uint8_t cylinder;
249} mbr_chs_t;
250
bellardde167e42005-04-28 21:15:08 +0000251typedef struct partition_t {
252 uint8_t attributes; /* 0x80 = bootable */
thsb5700942007-09-25 14:47:03 +0000253 mbr_chs_t start_CHS;
254 uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
255 mbr_chs_t end_CHS;
bellardde167e42005-04-28 21:15:08 +0000256 uint32_t start_sector_long;
thsb5700942007-09-25 14:47:03 +0000257 uint32_t length_sector_long;
bellardde167e42005-04-28 21:15:08 +0000258} __attribute__((packed)) partition_t;
259
260typedef struct mbr_t {
thsb5700942007-09-25 14:47:03 +0000261 uint8_t ignored[0x1b8];
262 uint32_t nt_id;
263 uint8_t ignored2[2];
bellardde167e42005-04-28 21:15:08 +0000264 partition_t partition[4];
265 uint8_t magic[2];
266} __attribute__((packed)) mbr_t;
267
268typedef struct direntry_t {
269 uint8_t name[8];
270 uint8_t extension[3];
271 uint8_t attributes;
272 uint8_t reserved[2];
273 uint16_t ctime;
274 uint16_t cdate;
275 uint16_t adate;
276 uint16_t begin_hi;
277 uint16_t mtime;
278 uint16_t mdate;
279 uint16_t begin;
280 uint32_t size;
281} __attribute__((packed)) direntry_t;
282
283/* this structure are used to transparently access the files */
284
285typedef struct mapping_t {
bellarda0464332005-12-18 18:29:50 +0000286 /* begin is the first cluster, end is the last+1 */
287 uint32_t begin,end;
bellardde167e42005-04-28 21:15:08 +0000288 /* as s->directory is growable, no pointer may be used here */
289 unsigned int dir_index;
bellarda0464332005-12-18 18:29:50 +0000290 /* the clusters of a file may be in any order; this points to the first */
291 int first_mapping_index;
292 union {
293 /* offset is
294 * - the offset in the file (in clusters) for a file, or
295 * - the next cluster of the directory for a directory, and
296 * - the address of the buffer for a faked entry
297 */
298 struct {
299 uint32_t offset;
300 } file;
301 struct {
302 int parent_mapping_index;
303 int first_dir_index;
304 } dir;
305 } info;
306 /* path contains the full path, i.e. it always starts with s->path */
307 char* path;
308
309 enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2,
310 MODE_DIRECTORY = 4, MODE_FAKED = 8,
311 MODE_DELETED = 16, MODE_RENAMED = 32 } mode;
312 int read_only;
bellardde167e42005-04-28 21:15:08 +0000313} mapping_t;
314
bellarda0464332005-12-18 18:29:50 +0000315#ifdef DEBUG
316static void print_direntry(const struct direntry_t*);
317static void print_mapping(const struct mapping_t* mapping);
318#endif
bellardde167e42005-04-28 21:15:08 +0000319
320/* here begins the real VVFAT driver */
321
322typedef struct BDRVVVFATState {
bellarda0464332005-12-18 18:29:50 +0000323 BlockDriverState* bs; /* pointer to parent */
bellardde167e42005-04-28 21:15:08 +0000324 unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
325 unsigned char first_sectors[0x40*0x200];
ths3b46e622007-09-17 08:09:54 +0000326
bellardde167e42005-04-28 21:15:08 +0000327 int fat_type; /* 16 or 32 */
328 array_t fat,directory,mapping;
ths3b46e622007-09-17 08:09:54 +0000329
bellardde167e42005-04-28 21:15:08 +0000330 unsigned int cluster_size;
331 unsigned int sectors_per_cluster;
332 unsigned int sectors_per_fat;
333 unsigned int sectors_of_root_directory;
bellarda0464332005-12-18 18:29:50 +0000334 uint32_t last_cluster_of_root_directory;
bellardde167e42005-04-28 21:15:08 +0000335 unsigned int faked_sectors; /* how many sectors are faked before file data */
336 uint32_t sector_count; /* total number of sectors of the partition */
337 uint32_t cluster_count; /* total number of clusters of this partition */
bellardde167e42005-04-28 21:15:08 +0000338 uint32_t max_fat_value;
ths3b46e622007-09-17 08:09:54 +0000339
bellardde167e42005-04-28 21:15:08 +0000340 int current_fd;
bellardde167e42005-04-28 21:15:08 +0000341 mapping_t* current_mapping;
bellarda0464332005-12-18 18:29:50 +0000342 unsigned char* cluster; /* points to current cluster */
343 unsigned char* cluster_buffer; /* points to a buffer to hold temp data */
bellardde167e42005-04-28 21:15:08 +0000344 unsigned int current_cluster;
345
346 /* write support */
bellarda0464332005-12-18 18:29:50 +0000347 BlockDriverState* write_target;
348 char* qcow_filename;
349 BlockDriverState* qcow;
350 void* fat2;
351 char* used_clusters;
352 array_t commits;
353 const char* path;
354 int downcase_short_names;
bellardde167e42005-04-28 21:15:08 +0000355} BDRVVVFATState;
356
thsb5700942007-09-25 14:47:03 +0000357/* take the sector position spos and convert it to Cylinder/Head/Sector position
358 * if the position is outside the specified geometry, fill maximum value for CHS
359 * and return 1 to signal overflow.
360 */
361static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
362 int head,sector;
363 sector = spos % (bs->secs); spos/= bs->secs;
364 head = spos % (bs->heads); spos/= bs->heads;
365 if(spos >= bs->cyls){
366 /* Overflow,
367 it happens if 32bit sector positions are used, while CHS is only 24bit.
368 Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
369 chs->head = 0xFF;
370 chs->sector = 0xFF;
371 chs->cylinder = 0xFF;
372 return 1;
373 }
374 chs->head = (uint8_t)head;
375 chs->sector = (uint8_t)( (sector+1) | ((spos>>8)<<6) );
376 chs->cylinder = (uint8_t)spos;
377 return 0;
378}
bellardde167e42005-04-28 21:15:08 +0000379
bellardde167e42005-04-28 21:15:08 +0000380static void init_mbr(BDRVVVFATState* s)
381{
382 /* TODO: if the files mbr.img and bootsect.img exist, use them */
383 mbr_t* real_mbr=(mbr_t*)s->first_sectors;
384 partition_t* partition=&(real_mbr->partition[0]);
thsb5700942007-09-25 14:47:03 +0000385 int lba;
bellardde167e42005-04-28 21:15:08 +0000386
387 memset(s->first_sectors,0,512);
ths3b46e622007-09-17 08:09:54 +0000388
thsb5700942007-09-25 14:47:03 +0000389 /* Win NT Disk Signature */
390 real_mbr->nt_id= cpu_to_le32(0xbe1afdfa);
391
bellardde167e42005-04-28 21:15:08 +0000392 partition->attributes=0x80; /* bootable */
thsb5700942007-09-25 14:47:03 +0000393
394 /* LBA is used when partition is outside the CHS geometry */
395 lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1);
396 lba|= sector2CHS(s->bs, &partition->end_CHS, s->sector_count);
397
398 /*LBA partitions are identified only by start/length_sector_long not by CHS*/
399 partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1);
400 partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1);
401
bellarda0464332005-12-18 18:29:50 +0000402 /* FAT12/FAT16/FAT32 */
thsb5700942007-09-25 14:47:03 +0000403 /* DOS uses different types when partition is LBA,
404 probably to prevent older versions from using CHS on them */
405 partition->fs_type= s->fat_type==12 ? 0x1:
406 s->fat_type==16 ? (lba?0xe:0x06):
407 /*fat_tyoe==32*/ (lba?0xc:0x0b);
bellardde167e42005-04-28 21:15:08 +0000408
409 real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
410}
411
bellarda0464332005-12-18 18:29:50 +0000412/* direntry functions */
413
bellardde167e42005-04-28 21:15:08 +0000414/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
415static inline int short2long_name(unsigned char* dest,const char* src)
416{
417 int i;
418 for(i=0;i<129 && src[i];i++) {
419 dest[2*i]=src[i];
420 dest[2*i+1]=0;
421 }
422 dest[2*i]=dest[2*i+1]=0;
423 for(i=2*i+2;(i%26);i++)
424 dest[i]=0xff;
425 return i;
426}
427
428static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
429{
430 char buffer[258];
431 int length=short2long_name(buffer,filename),
432 number_of_entries=(length+25)/26,i;
433 direntry_t* entry;
434
435 for(i=0;i<number_of_entries;i++) {
436 entry=array_get_next(&(s->directory));
437 entry->attributes=0xf;
438 entry->reserved[0]=0;
439 entry->begin=0;
440 entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
441 }
442 for(i=0;i<length;i++) {
443 int offset=(i%26);
444 if(offset<10) offset=1+offset;
445 else if(offset<22) offset=14+offset-10;
446 else offset=28+offset-22;
447 entry=array_get(&(s->directory),s->directory.next-1-(i/26));
448 entry->name[offset]=buffer[i];
449 }
450 return array_get(&(s->directory),s->directory.next-number_of_entries);
451}
452
bellarda0464332005-12-18 18:29:50 +0000453static char is_free(const direntry_t* direntry)
454{
455 /* return direntry->name[0]==0 ; */
456 return direntry->attributes == 0 || direntry->name[0]==0xe5;
457}
458
459static char is_volume_label(const direntry_t* direntry)
460{
461 return direntry->attributes == 0x28;
462}
463
464static char is_long_name(const direntry_t* direntry)
465{
466 return direntry->attributes == 0xf;
467}
468
469static char is_short_name(const direntry_t* direntry)
470{
471 return !is_volume_label(direntry) && !is_long_name(direntry)
472 && !is_free(direntry);
473}
474
475static char is_directory(const direntry_t* direntry)
476{
477 return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
478}
479
480static inline char is_dot(const direntry_t* direntry)
481{
482 return is_short_name(direntry) && direntry->name[0] == '.';
483}
484
485static char is_file(const direntry_t* direntry)
486{
487 return is_short_name(direntry) && !is_directory(direntry);
488}
489
490static inline uint32_t begin_of_direntry(const direntry_t* direntry)
491{
492 return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
493}
494
495static inline uint32_t filesize_of_direntry(const direntry_t* direntry)
496{
497 return le32_to_cpu(direntry->size);
498}
499
500static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
501{
502 direntry->begin = cpu_to_le16(begin & 0xffff);
503 direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
504}
505
bellardde167e42005-04-28 21:15:08 +0000506/* fat functions */
507
bellarda0464332005-12-18 18:29:50 +0000508static inline uint8_t fat_chksum(const direntry_t* entry)
bellardde167e42005-04-28 21:15:08 +0000509{
510 uint8_t chksum=0;
511 int i;
512
513 for(i=0;i<11;i++)
514 chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0))
515 +(unsigned char)entry->name[i];
ths3b46e622007-09-17 08:09:54 +0000516
bellardde167e42005-04-28 21:15:08 +0000517 return chksum;
518}
519
520/* if return_time==0, this returns the fat_date, else the fat_time */
521static uint16_t fat_datetime(time_t time,int return_time) {
522 struct tm* t;
523#ifdef _WIN32
524 t=localtime(&time); /* this is not thread safe */
525#else
526 struct tm t1;
527 t=&t1;
528 localtime_r(&time,t);
529#endif
530 if(return_time)
531 return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
532 return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
533}
534
535static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
536{
bellarda0464332005-12-18 18:29:50 +0000537 if(s->fat_type==32) {
538 uint32_t* entry=array_get(&(s->fat),cluster);
539 *entry=cpu_to_le32(value);
bellardde167e42005-04-28 21:15:08 +0000540 } else if(s->fat_type==16) {
541 uint16_t* entry=array_get(&(s->fat),cluster);
542 *entry=cpu_to_le16(value&0xffff);
543 } else {
bellarda0464332005-12-18 18:29:50 +0000544 int offset = (cluster*3/2);
545 unsigned char* p = array_get(&(s->fat), offset);
546 switch (cluster&1) {
547 case 0:
548 p[0] = value&0xff;
549 p[1] = (p[1]&0xf0) | ((value>>8)&0xf);
550 break;
551 case 1:
552 p[0] = (p[0]&0xf) | ((value&0xf)<<4);
553 p[1] = (value>>4);
554 break;
555 }
bellardde167e42005-04-28 21:15:08 +0000556 }
557}
558
559static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
560{
bellarda0464332005-12-18 18:29:50 +0000561 if(s->fat_type==32) {
562 uint32_t* entry=array_get(&(s->fat),cluster);
563 return le32_to_cpu(*entry);
bellardde167e42005-04-28 21:15:08 +0000564 } else if(s->fat_type==16) {
565 uint16_t* entry=array_get(&(s->fat),cluster);
566 return le16_to_cpu(*entry);
567 } else {
bellarda0464332005-12-18 18:29:50 +0000568 const uint8_t* x=s->fat.pointer+cluster*3/2;
569 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
bellardde167e42005-04-28 21:15:08 +0000570 }
571}
572
573static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
574{
575 if(fat_entry>s->max_fat_value-8)
576 return -1;
577 return 0;
578}
579
580static inline void init_fat(BDRVVVFATState* s)
581{
bellarda0464332005-12-18 18:29:50 +0000582 if (s->fat_type == 12) {
583 array_init(&(s->fat),1);
584 array_ensure_allocated(&(s->fat),
585 s->sectors_per_fat * 0x200 * 3 / 2 - 1);
586 } else {
587 array_init(&(s->fat),(s->fat_type==32?4:2));
588 array_ensure_allocated(&(s->fat),
589 s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
590 }
bellardde167e42005-04-28 21:15:08 +0000591 memset(s->fat.pointer,0,s->fat.size);
ths3b46e622007-09-17 08:09:54 +0000592
bellardde167e42005-04-28 21:15:08 +0000593 switch(s->fat_type) {
594 case 12: s->max_fat_value=0xfff; break;
595 case 16: s->max_fat_value=0xffff; break;
bellarda0464332005-12-18 18:29:50 +0000596 case 32: s->max_fat_value=0x0fffffff; break;
bellardde167e42005-04-28 21:15:08 +0000597 default: s->max_fat_value=0; /* error... */
598 }
599
600}
601
bellarda0464332005-12-18 18:29:50 +0000602/* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */
603/* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */
604static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
605 unsigned int directory_start, const char* filename, int is_dot)
bellardde167e42005-04-28 21:15:08 +0000606{
bellarda0464332005-12-18 18:29:50 +0000607 int i,j,long_index=s->directory.next;
bellardde167e42005-04-28 21:15:08 +0000608 direntry_t* entry=0;
609 direntry_t* entry_long=0;
610
611 if(is_dot) {
612 entry=array_get_next(&(s->directory));
613 memset(entry->name,0x20,11);
614 memcpy(entry->name,filename,strlen(filename));
615 return entry;
616 }
ths3b46e622007-09-17 08:09:54 +0000617
bellardde167e42005-04-28 21:15:08 +0000618 entry_long=create_long_filename(s,filename);
ths3b46e622007-09-17 08:09:54 +0000619
ths5fafdf22007-09-16 21:08:06 +0000620 i = strlen(filename);
bellarda0464332005-12-18 18:29:50 +0000621 for(j = i - 1; j>0 && filename[j]!='.';j--);
622 if (j > 0)
623 i = (j > 8 ? 8 : j);
624 else if (i > 8)
625 i = 8;
626
bellardde167e42005-04-28 21:15:08 +0000627 entry=array_get_next(&(s->directory));
628 memset(entry->name,0x20,11);
629 strncpy(entry->name,filename,i);
ths3b46e622007-09-17 08:09:54 +0000630
bellarda0464332005-12-18 18:29:50 +0000631 if(j > 0)
632 for (i = 0; i < 3 && filename[j+1+i]; i++)
633 entry->extension[i] = filename[j+1+i];
bellardde167e42005-04-28 21:15:08 +0000634
635 /* upcase & remove unwanted characters */
636 for(i=10;i>=0;i--) {
bellarda0464332005-12-18 18:29:50 +0000637 if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--);
bellardde167e42005-04-28 21:15:08 +0000638 if(entry->name[i]<=' ' || entry->name[i]>0x7f
bellarda0464332005-12-18 18:29:50 +0000639 || strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
bellardde167e42005-04-28 21:15:08 +0000640 entry->name[i]='_';
641 else if(entry->name[i]>='a' && entry->name[i]<='z')
642 entry->name[i]+='A'-'a';
643 }
644
645 /* mangle duplicates */
646 while(1) {
647 direntry_t* entry1=array_get(&(s->directory),directory_start);
648 int j;
649
650 for(;entry1<entry;entry1++)
bellarda0464332005-12-18 18:29:50 +0000651 if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11))
bellardde167e42005-04-28 21:15:08 +0000652 break; /* found dupe */
653 if(entry1==entry) /* no dupe found */
654 break;
655
ths5fafdf22007-09-16 21:08:06 +0000656 /* use all 8 characters of name */
bellardde167e42005-04-28 21:15:08 +0000657 if(entry->name[7]==' ') {
658 int j;
659 for(j=6;j>0 && entry->name[j]==' ';j--)
660 entry->name[j]='~';
661 }
662
663 /* increment number */
664 for(j=7;j>0 && entry->name[j]=='9';j--)
665 entry->name[j]='0';
666 if(j>0) {
667 if(entry->name[j]<'0' || entry->name[j]>'9')
668 entry->name[j]='0';
669 else
670 entry->name[j]++;
671 }
672 }
673
674 /* calculate checksum; propagate to long name */
675 if(entry_long) {
676 uint8_t chksum=fat_chksum(entry);
677
678 /* calculate anew, because realloc could have taken place */
679 entry_long=array_get(&(s->directory),long_index);
bellarda0464332005-12-18 18:29:50 +0000680 while(entry_long<entry && is_long_name(entry_long)) {
bellardde167e42005-04-28 21:15:08 +0000681 entry_long->reserved[1]=chksum;
682 entry_long++;
683 }
684 }
685
686 return entry;
687}
688
bellarda0464332005-12-18 18:29:50 +0000689/*
690 * Read a directory. (the index of the corresponding mapping must be passed).
691 */
692static int read_directory(BDRVVVFATState* s, int mapping_index)
bellardde167e42005-04-28 21:15:08 +0000693{
bellarda0464332005-12-18 18:29:50 +0000694 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
695 direntry_t* direntry;
696 const char* dirname = mapping->path;
697 int first_cluster = mapping->begin;
698 int parent_index = mapping->info.dir.parent_mapping_index;
699 mapping_t* parent_mapping = (mapping_t*)
700 (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : 0);
701 int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
bellardde167e42005-04-28 21:15:08 +0000702
703 DIR* dir=opendir(dirname);
704 struct dirent* entry;
bellardde167e42005-04-28 21:15:08 +0000705 int i;
706
bellarda0464332005-12-18 18:29:50 +0000707 assert(mapping->mode & MODE_DIRECTORY);
708
709 if(!dir) {
710 mapping->end = mapping->begin;
bellardde167e42005-04-28 21:15:08 +0000711 return -1;
bellarda0464332005-12-18 18:29:50 +0000712 }
ths3b46e622007-09-17 08:09:54 +0000713
bellarda0464332005-12-18 18:29:50 +0000714 i = mapping->info.dir.first_dir_index =
715 first_cluster == 0 ? 0 : s->directory.next;
716
ths5fafdf22007-09-16 21:08:06 +0000717 /* actually read the directory, and allocate the mappings */
bellardde167e42005-04-28 21:15:08 +0000718 while((entry=readdir(dir))) {
719 unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
720 char* buffer;
721 direntry_t* direntry;
bellarda0464332005-12-18 18:29:50 +0000722 struct stat st;
bellardde167e42005-04-28 21:15:08 +0000723 int is_dot=!strcmp(entry->d_name,".");
724 int is_dotdot=!strcmp(entry->d_name,"..");
725
bellarda0464332005-12-18 18:29:50 +0000726 if(first_cluster == 0 && (is_dotdot || is_dot))
bellardde167e42005-04-28 21:15:08 +0000727 continue;
ths5fafdf22007-09-16 21:08:06 +0000728
bellardde167e42005-04-28 21:15:08 +0000729 buffer=(char*)malloc(length);
bellarda0464332005-12-18 18:29:50 +0000730 assert(buffer);
bellardde167e42005-04-28 21:15:08 +0000731 snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
732
733 if(stat(buffer,&st)<0) {
734 free(buffer);
735 continue;
736 }
737
738 /* create directory entry for this file */
bellarda0464332005-12-18 18:29:50 +0000739 direntry=create_short_and_long_name(s, i, entry->d_name,
740 is_dot || is_dotdot);
bellardde167e42005-04-28 21:15:08 +0000741 direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
742 direntry->reserved[0]=direntry->reserved[1]=0;
743 direntry->ctime=fat_datetime(st.st_ctime,1);
744 direntry->cdate=fat_datetime(st.st_ctime,0);
745 direntry->adate=fat_datetime(st.st_atime,0);
746 direntry->begin_hi=0;
747 direntry->mtime=fat_datetime(st.st_mtime,1);
748 direntry->mdate=fat_datetime(st.st_mtime,0);
749 if(is_dotdot)
bellarda0464332005-12-18 18:29:50 +0000750 set_begin_of_direntry(direntry, first_cluster_of_parent);
bellardde167e42005-04-28 21:15:08 +0000751 else if(is_dot)
bellarda0464332005-12-18 18:29:50 +0000752 set_begin_of_direntry(direntry, first_cluster);
bellardde167e42005-04-28 21:15:08 +0000753 else
bellarda0464332005-12-18 18:29:50 +0000754 direntry->begin=0; /* do that later */
755 if (st.st_size > 0x7fffffff) {
756 fprintf(stderr, "File %s is larger than 2GB\n", buffer);
757 free(buffer);
758 return -2;
759 }
760 direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
bellardde167e42005-04-28 21:15:08 +0000761
762 /* create mapping for this file */
bellarda0464332005-12-18 18:29:50 +0000763 if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
764 s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
bellardde167e42005-04-28 21:15:08 +0000765 s->current_mapping->begin=0;
766 s->current_mapping->end=st.st_size;
bellarda0464332005-12-18 18:29:50 +0000767 /*
768 * we get the direntry of the most recent direntry, which
769 * contains the short name and all the relevant information.
770 */
bellardde167e42005-04-28 21:15:08 +0000771 s->current_mapping->dir_index=s->directory.next-1;
bellarda0464332005-12-18 18:29:50 +0000772 s->current_mapping->first_mapping_index = -1;
773 if (S_ISDIR(st.st_mode)) {
774 s->current_mapping->mode = MODE_DIRECTORY;
775 s->current_mapping->info.dir.parent_mapping_index =
776 mapping_index;
777 } else {
778 s->current_mapping->mode = MODE_UNDEFINED;
779 s->current_mapping->info.file.offset = 0;
780 }
781 s->current_mapping->path=buffer;
782 s->current_mapping->read_only =
783 (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
bellardde167e42005-04-28 21:15:08 +0000784 }
785 }
786 closedir(dir);
787
788 /* fill with zeroes up to the end of the cluster */
789 while(s->directory.next%(0x10*s->sectors_per_cluster)) {
790 direntry_t* direntry=array_get_next(&(s->directory));
791 memset(direntry,0,sizeof(direntry_t));
792 }
793
bellarda0464332005-12-18 18:29:50 +0000794/* TODO: if there are more entries, bootsector has to be adjusted! */
795#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster)
796 if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) {
797 /* root directory */
798 int cur = s->directory.next;
799 array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
800 memset(array_get(&(s->directory), cur), 0,
801 (ROOT_ENTRIES - cur) * sizeof(direntry_t));
bellardde167e42005-04-28 21:15:08 +0000802 }
ths5fafdf22007-09-16 21:08:06 +0000803
bellarda0464332005-12-18 18:29:50 +0000804 /* reget the mapping, since s->mapping was possibly realloc()ed */
805 mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
806 first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
807 * 0x20 / s->cluster_size;
808 mapping->end = first_cluster;
bellardde167e42005-04-28 21:15:08 +0000809
bellarda0464332005-12-18 18:29:50 +0000810 direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
811 set_begin_of_direntry(direntry, mapping->begin);
ths3b46e622007-09-17 08:09:54 +0000812
bellardde167e42005-04-28 21:15:08 +0000813 return 0;
814}
815
bellarda0464332005-12-18 18:29:50 +0000816static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
bellardde167e42005-04-28 21:15:08 +0000817{
bellarda0464332005-12-18 18:29:50 +0000818 return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
819}
820
821static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
822{
823 return s->faked_sectors + s->sectors_per_cluster * cluster_num;
824}
825
826static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num)
827{
828 return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster;
829}
830
831#ifdef DBG
832static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping)
833{
834 if(mapping->mode==MODE_UNDEFINED)
835 return 0;
836 return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index);
837}
838#endif
839
840static int init_directories(BDRVVVFATState* s,
841 const char* dirname)
842{
843 bootsector_t* bootsector;
844 mapping_t* mapping;
bellardde167e42005-04-28 21:15:08 +0000845 unsigned int i;
846 unsigned int cluster;
847
848 memset(&(s->first_sectors[0]),0,0x40*0x200);
849
bellardde167e42005-04-28 21:15:08 +0000850 s->cluster_size=s->sectors_per_cluster*0x200;
bellarda0464332005-12-18 18:29:50 +0000851 s->cluster_buffer=malloc(s->cluster_size);
852 assert(s->cluster_buffer);
853
854 /*
855 * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
856 * where sc is sector_count,
857 * spf is sectors_per_fat,
858 * spc is sectors_per_clusters, and
859 * fat_type = 12, 16 or 32.
860 */
861 i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
862 s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
ths3b46e622007-09-17 08:09:54 +0000863
bellardde167e42005-04-28 21:15:08 +0000864 array_init(&(s->mapping),sizeof(mapping_t));
865 array_init(&(s->directory),sizeof(direntry_t));
bellardde167e42005-04-28 21:15:08 +0000866
867 /* add volume label */
868 {
869 direntry_t* entry=array_get_next(&(s->directory));
870 entry->attributes=0x28; /* archive | volume label */
871 snprintf(entry->name,11,"QEMU VVFAT");
872 }
873
bellardde167e42005-04-28 21:15:08 +0000874 /* Now build FAT, and write back information into directory */
875 init_fat(s);
876
bellarda0464332005-12-18 18:29:50 +0000877 s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
878 s->cluster_count=sector2cluster(s, s->sector_count);
bellardde167e42005-04-28 21:15:08 +0000879
bellarda0464332005-12-18 18:29:50 +0000880 mapping = array_get_next(&(s->mapping));
881 mapping->begin = 0;
882 mapping->dir_index = 0;
883 mapping->info.dir.parent_mapping_index = -1;
884 mapping->first_mapping_index = -1;
885 mapping->path = strdup(dirname);
886 i = strlen(mapping->path);
887 if (i > 0 && mapping->path[i - 1] == '/')
888 mapping->path[i - 1] = '\0';
889 mapping->mode = MODE_DIRECTORY;
890 mapping->read_only = 0;
891 s->path = mapping->path;
bellardde167e42005-04-28 21:15:08 +0000892
bellarda0464332005-12-18 18:29:50 +0000893 for (i = 0, cluster = 0; i < s->mapping.next; i++) {
894 int j;
ths5fafdf22007-09-16 21:08:06 +0000895 /* MS-DOS expects the FAT to be 0 for the root directory
bellarda0464332005-12-18 18:29:50 +0000896 * (except for the media byte). */
897 /* LATER TODO: still true for FAT32? */
898 int fix_fat = (i != 0);
899 mapping = array_get(&(s->mapping), i);
bellardde167e42005-04-28 21:15:08 +0000900
bellarda0464332005-12-18 18:29:50 +0000901 if (mapping->mode & MODE_DIRECTORY) {
902 mapping->begin = cluster;
903 if(read_directory(s, i)) {
904 fprintf(stderr, "Could not read directory %s\n",
905 mapping->path);
bellardde167e42005-04-28 21:15:08 +0000906 return -1;
907 }
bellarda0464332005-12-18 18:29:50 +0000908 mapping = array_get(&(s->mapping), i);
909 } else {
910 assert(mapping->mode == MODE_UNDEFINED);
bellardde167e42005-04-28 21:15:08 +0000911 mapping->mode=MODE_NORMAL;
bellarda0464332005-12-18 18:29:50 +0000912 mapping->begin = cluster;
913 if (mapping->end > 0) {
914 direntry_t* direntry = array_get(&(s->directory),
915 mapping->dir_index);
bellardde167e42005-04-28 21:15:08 +0000916
bellarda0464332005-12-18 18:29:50 +0000917 mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
918 set_begin_of_direntry(direntry, mapping->begin);
919 } else {
920 mapping->end = cluster + 1;
921 fix_fat = 0;
922 }
923 }
924
925 assert(mapping->begin < mapping->end);
926
927 /* fix fat for entry */
928 if (fix_fat) {
929 for(j = mapping->begin; j < mapping->end - 1; j++)
930 fat_set(s, j, j+1);
931 fat_set(s, mapping->end - 1, s->max_fat_value);
932 }
933
934 /* next free cluster */
935 cluster = mapping->end;
936
937 if(cluster > s->cluster_count) {
938 fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type);
939 return -1;
bellardde167e42005-04-28 21:15:08 +0000940 }
941 }
942
bellarda0464332005-12-18 18:29:50 +0000943 mapping = array_get(&(s->mapping), 0);
944 s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
945 s->last_cluster_of_root_directory = mapping->end;
bellardde167e42005-04-28 21:15:08 +0000946
bellarda0464332005-12-18 18:29:50 +0000947 /* the FAT signature */
948 fat_set(s,0,s->max_fat_value);
949 fat_set(s,1,s->max_fat_value);
950
951 s->current_mapping = NULL;
952
953 bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
bellardde167e42005-04-28 21:15:08 +0000954 bootsector->jump[0]=0xeb;
955 bootsector->jump[1]=0x3e;
956 bootsector->jump[2]=0x90;
957 memcpy(bootsector->name,"QEMU ",8);
958 bootsector->sector_size=cpu_to_le16(0x200);
959 bootsector->sectors_per_cluster=s->sectors_per_cluster;
960 bootsector->reserved_sectors=cpu_to_le16(1);
961 bootsector->number_of_fats=0x2; /* number of FATs */
962 bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
bellarda0464332005-12-18 18:29:50 +0000963 bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
964 bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
965 s->fat.pointer[0] = bootsector->media_type;
bellardde167e42005-04-28 21:15:08 +0000966 bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
bellarda0464332005-12-18 18:29:50 +0000967 bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
968 bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
bellardde167e42005-04-28 21:15:08 +0000969 bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
bellarda0464332005-12-18 18:29:50 +0000970 bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
bellardde167e42005-04-28 21:15:08 +0000971
bellarda0464332005-12-18 18:29:50 +0000972 /* LATER TODO: if FAT32, this is wrong */
973 bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
bellardde167e42005-04-28 21:15:08 +0000974 bootsector->u.fat16.current_head=0;
975 bootsector->u.fat16.signature=0x29;
976 bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
977
978 memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
979 memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8);
980 bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
981
982 return 0;
983}
984
bellard83f64092006-08-01 16:21:11 +0000985#ifdef DEBUG
bellarda0464332005-12-18 18:29:50 +0000986static BDRVVVFATState *vvv = NULL;
bellard83f64092006-08-01 16:21:11 +0000987#endif
bellarda0464332005-12-18 18:29:50 +0000988
989static int enable_write_target(BDRVVVFATState *s);
990static int is_consistent(BDRVVVFATState *s);
991
bellard83f64092006-08-01 16:21:11 +0000992static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
bellardde167e42005-04-28 21:15:08 +0000993{
994 BDRVVVFATState *s = bs->opaque;
bellarda0464332005-12-18 18:29:50 +0000995 int floppy = 0;
bellardde167e42005-04-28 21:15:08 +0000996 int i;
997
bellard83f64092006-08-01 16:21:11 +0000998#ifdef DEBUG
bellarda0464332005-12-18 18:29:50 +0000999 vvv = s;
bellard83f64092006-08-01 16:21:11 +00001000#endif
bellarda0464332005-12-18 18:29:50 +00001001
1002DLOG(if (stderr == NULL) {
1003 stderr = fopen("vvfat.log", "a");
1004 setbuf(stderr, NULL);
1005})
1006
1007 s->bs = bs;
1008
bellardde167e42005-04-28 21:15:08 +00001009 s->fat_type=16;
bellarda0464332005-12-18 18:29:50 +00001010 /* LATER TODO: if FAT32, adjust */
bellarda0464332005-12-18 18:29:50 +00001011 s->sectors_per_cluster=0x10;
thsb5700942007-09-25 14:47:03 +00001012 /* 504MB disk*/
1013 bs->cyls=1024; bs->heads=16; bs->secs=63;
bellardde167e42005-04-28 21:15:08 +00001014
1015 s->current_cluster=0xffffffff;
bellardde167e42005-04-28 21:15:08 +00001016
bellardde167e42005-04-28 21:15:08 +00001017 s->first_sectors_number=0x40;
bellarda0464332005-12-18 18:29:50 +00001018 /* read only is the default for safety */
1019 bs->read_only = 1;
1020 s->qcow = s->write_target = NULL;
1021 s->qcow_filename = NULL;
1022 s->fat2 = NULL;
1023 s->downcase_short_names = 1;
ths3b46e622007-09-17 08:09:54 +00001024
bellarda0464332005-12-18 18:29:50 +00001025 if (!strstart(dirname, "fat:", NULL))
1026 return -1;
1027
bellarda0464332005-12-18 18:29:50 +00001028 if (strstr(dirname, ":floppy:")) {
1029 floppy = 1;
1030 s->fat_type = 12;
1031 s->first_sectors_number = 1;
1032 s->sectors_per_cluster=2;
1033 bs->cyls = 80; bs->heads = 2; bs->secs = 36;
1034 }
1035
thsb5700942007-09-25 14:47:03 +00001036 s->sector_count=bs->cyls*bs->heads*bs->secs;
1037
bellarda0464332005-12-18 18:29:50 +00001038 if (strstr(dirname, ":32:")) {
1039 fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
1040 s->fat_type = 32;
1041 } else if (strstr(dirname, ":16:")) {
1042 s->fat_type = 16;
1043 } else if (strstr(dirname, ":12:")) {
1044 s->fat_type = 12;
1045 s->sector_count=2880;
1046 }
1047
thsb5700942007-09-25 14:47:03 +00001048 if (strstr(dirname, ":rw:")) {
1049 if (enable_write_target(s))
1050 return -1;
1051 bs->read_only = 0;
1052 }
1053
bellarda0464332005-12-18 18:29:50 +00001054 i = strrchr(dirname, ':') - dirname;
1055 assert(i >= 3);
1056 if (dirname[i-2] == ':' && isalpha(dirname[i-1]))
1057 /* workaround for DOS drive names */
1058 dirname += i-1;
1059 else
1060 dirname += i+1;
1061
1062 bs->total_sectors=bs->cyls*bs->heads*bs->secs;
thsb5700942007-09-25 14:47:03 +00001063
bellarda0464332005-12-18 18:29:50 +00001064 if(init_directories(s, dirname))
bellardde167e42005-04-28 21:15:08 +00001065 return -1;
1066
thsb5700942007-09-25 14:47:03 +00001067 s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
1068
bellardde167e42005-04-28 21:15:08 +00001069 if(s->first_sectors_number==0x40)
1070 init_mbr(s);
1071
bellarda0464332005-12-18 18:29:50 +00001072 /* for some reason or other, MS-DOS does not like to know about CHS... */
1073 if (floppy)
1074 bs->heads = bs->cyls = bs->secs = 0;
bellardde167e42005-04-28 21:15:08 +00001075
bellarda0464332005-12-18 18:29:50 +00001076 // assert(is_consistent(s));
bellardde167e42005-04-28 21:15:08 +00001077 return 0;
1078}
1079
1080static inline void vvfat_close_current_file(BDRVVVFATState *s)
1081{
1082 if(s->current_mapping) {
bellarda0464332005-12-18 18:29:50 +00001083 s->current_mapping = NULL;
1084 if (s->current_fd) {
1085 close(s->current_fd);
1086 s->current_fd = 0;
1087 }
bellardde167e42005-04-28 21:15:08 +00001088 }
bellarda0464332005-12-18 18:29:50 +00001089 s->current_cluster = -1;
bellardde167e42005-04-28 21:15:08 +00001090}
1091
1092/* mappings between index1 and index2-1 are supposed to be ordered
1093 * return value is the index of the last mapping for which end>cluster_num
1094 */
1095static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
1096{
1097 int index3=index1+1;
bellardde167e42005-04-28 21:15:08 +00001098 while(1) {
1099 mapping_t* mapping;
1100 index3=(index1+index2)/2;
1101 mapping=array_get(&(s->mapping),index3);
bellarda0464332005-12-18 18:29:50 +00001102 assert(mapping->begin < mapping->end);
1103 if(mapping->begin>=cluster_num) {
bellardde167e42005-04-28 21:15:08 +00001104 assert(index2!=index3 || index2==0);
1105 if(index2==index3)
bellarda0464332005-12-18 18:29:50 +00001106 return index1;
bellardde167e42005-04-28 21:15:08 +00001107 index2=index3;
1108 } else {
1109 if(index1==index3)
bellarda0464332005-12-18 18:29:50 +00001110 return mapping->end<=cluster_num ? index2 : index1;
bellardde167e42005-04-28 21:15:08 +00001111 index1=index3;
1112 }
1113 assert(index1<=index2);
bellarda0464332005-12-18 18:29:50 +00001114 DLOG(mapping=array_get(&(s->mapping),index1);
1115 assert(mapping->begin<=cluster_num);
ths5fafdf22007-09-16 21:08:06 +00001116 assert(index2 >= s->mapping.next ||
bellarda0464332005-12-18 18:29:50 +00001117 ((mapping = array_get(&(s->mapping),index2)) &&
1118 mapping->end>cluster_num)));
bellardde167e42005-04-28 21:15:08 +00001119 }
1120}
1121
1122static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
1123{
1124 int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
1125 mapping_t* mapping;
1126 if(index>=s->mapping.next)
1127 return 0;
1128 mapping=array_get(&(s->mapping),index);
1129 if(mapping->begin>cluster_num)
1130 return 0;
bellarda0464332005-12-18 18:29:50 +00001131 assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
bellardde167e42005-04-28 21:15:08 +00001132 return mapping;
1133}
1134
bellarda0464332005-12-18 18:29:50 +00001135/*
1136 * This function simply compares path == mapping->path. Since the mappings
1137 * are sorted by cluster, this is expensive: O(n).
1138 */
1139static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s,
1140 const char* path)
1141{
1142 int i;
1143
1144 for (i = 0; i < s->mapping.next; i++) {
1145 mapping_t* mapping = array_get(&(s->mapping), i);
1146 if (mapping->first_mapping_index < 0 &&
1147 !strcmp(path, mapping->path))
1148 return mapping;
1149 }
1150
1151 return NULL;
1152}
1153
1154static int open_file(BDRVVVFATState* s,mapping_t* mapping)
bellardde167e42005-04-28 21:15:08 +00001155{
1156 if(!mapping)
1157 return -1;
bellardde167e42005-04-28 21:15:08 +00001158 if(!s->current_mapping ||
bellarda0464332005-12-18 18:29:50 +00001159 strcmp(s->current_mapping->path,mapping->path)) {
bellardde167e42005-04-28 21:15:08 +00001160 /* open file */
bellarda0464332005-12-18 18:29:50 +00001161 int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
bellardde167e42005-04-28 21:15:08 +00001162 if(fd<0)
1163 return -1;
1164 vvfat_close_current_file(s);
1165 s->current_fd = fd;
bellardde167e42005-04-28 21:15:08 +00001166 s->current_mapping = mapping;
1167 }
1168 return 0;
1169}
1170
1171static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
1172{
1173 if(s->current_cluster != cluster_num) {
1174 int result=0;
1175 off_t offset;
bellarda0464332005-12-18 18:29:50 +00001176 assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
bellardde167e42005-04-28 21:15:08 +00001177 if(!s->current_mapping
1178 || s->current_mapping->begin>cluster_num
1179 || s->current_mapping->end<=cluster_num) {
1180 /* binary search of mappings for file */
1181 mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
bellardde167e42005-04-28 21:15:08 +00001182
bellarda0464332005-12-18 18:29:50 +00001183 assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
1184
1185 if (mapping && mapping->mode & MODE_DIRECTORY) {
1186 vvfat_close_current_file(s);
1187 s->current_mapping = mapping;
1188read_cluster_directory:
1189 offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
1190 s->cluster = s->directory.pointer+offset
1191 + 0x20*s->current_mapping->info.dir.first_dir_index;
1192 assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
1193 assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
1194 s->current_cluster = cluster_num;
1195 return 0;
1196 }
1197
1198 if(open_file(s,mapping))
1199 return -2;
1200 } else if (s->current_mapping->mode & MODE_DIRECTORY)
1201 goto read_cluster_directory;
1202
1203 assert(s->current_fd);
1204
1205 offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
bellardde167e42005-04-28 21:15:08 +00001206 if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
1207 return -3;
bellarda0464332005-12-18 18:29:50 +00001208 s->cluster=s->cluster_buffer;
bellardde167e42005-04-28 21:15:08 +00001209 result=read(s->current_fd,s->cluster,s->cluster_size);
1210 if(result<0) {
1211 s->current_cluster = -1;
1212 return -1;
1213 }
1214 s->current_cluster = cluster_num;
1215 }
1216 return 0;
1217}
1218
bellarda0464332005-12-18 18:29:50 +00001219#ifdef DEBUG
1220static void hexdump(const void* address, uint32_t len)
1221{
1222 const unsigned char* p = address;
1223 int i, j;
1224
1225 for (i = 0; i < len; i += 16) {
1226 for (j = 0; j < 16 && i + j < len; j++)
1227 fprintf(stderr, "%02x ", p[i + j]);
1228 for (; j < 16; j++)
1229 fprintf(stderr, " ");
1230 fprintf(stderr, " ");
1231 for (j = 0; j < 16 && i + j < len; j++)
1232 fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
1233 fprintf(stderr, "\n");
1234 }
1235}
1236
1237static void print_direntry(const direntry_t* direntry)
1238{
1239 int j = 0;
1240 char buffer[1024];
1241
1242 fprintf(stderr, "direntry 0x%x: ", (int)direntry);
1243 if(!direntry)
1244 return;
1245 if(is_long_name(direntry)) {
1246 unsigned char* c=(unsigned char*)direntry;
1247 int i;
1248 for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
1249#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = '°'; j++;}
1250 ADD_CHAR(c[i]);
1251 for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
1252 ADD_CHAR(c[i]);
1253 for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
1254 ADD_CHAR(c[i]);
1255 buffer[j] = 0;
1256 fprintf(stderr, "%s\n", buffer);
1257 } else {
1258 int i;
1259 for(i=0;i<11;i++)
1260 ADD_CHAR(direntry->name[i]);
1261 buffer[j] = 0;
1262 fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
1263 buffer,
1264 direntry->attributes,
1265 begin_of_direntry(direntry),le32_to_cpu(direntry->size));
1266 }
1267}
1268
1269static void print_mapping(const mapping_t* mapping)
1270{
1271 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);
1272 if (mapping->mode & MODE_DIRECTORY)
1273 fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
1274 else
1275 fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
1276}
1277#endif
1278
ths5fafdf22007-09-16 21:08:06 +00001279static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
bellardde167e42005-04-28 21:15:08 +00001280 uint8_t *buf, int nb_sectors)
1281{
1282 BDRVVVFATState *s = bs->opaque;
1283 int i;
1284
bellardde167e42005-04-28 21:15:08 +00001285 for(i=0;i<nb_sectors;i++,sector_num++) {
bellarda0464332005-12-18 18:29:50 +00001286 if (sector_num >= s->sector_count)
1287 return -1;
1288 if (s->qcow) {
1289 int n;
1290 if (s->qcow->drv->bdrv_is_allocated(s->qcow,
1291 sector_num, nb_sectors-i, &n)) {
1292DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
1293 if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
1294 return -1;
1295 i += n - 1;
1296 sector_num += n - 1;
1297 continue;
1298 }
1299DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
1300 }
bellardde167e42005-04-28 21:15:08 +00001301 if(sector_num<s->faked_sectors) {
bellarda0464332005-12-18 18:29:50 +00001302 if(sector_num<s->first_sectors_number)
1303 memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
1304 else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
1305 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
1306 else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
1307 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
bellardde167e42005-04-28 21:15:08 +00001308 } else {
bellarda0464332005-12-18 18:29:50 +00001309 uint32_t sector=sector_num-s->faked_sectors,
1310 sector_offset_in_cluster=(sector%s->sectors_per_cluster),
1311 cluster_num=sector/s->sectors_per_cluster;
1312 if(read_cluster(s, cluster_num) != 0) {
1313 /* LATER TODO: strict: return -1; */
1314 memset(buf+i*0x200,0,0x200);
1315 continue;
1316 }
1317 memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
1318 }
1319 }
1320 return 0;
1321}
1322
1323/* LATER TODO: statify all functions */
1324
1325/*
1326 * Idea of the write support (use snapshot):
1327 *
1328 * 1. check if all data is consistent, recording renames, modifications,
1329 * new files and directories (in s->commits).
1330 *
1331 * 2. if the data is not consistent, stop committing
1332 *
1333 * 3. handle renames, and create new files and directories (do not yet
1334 * write their contents)
1335 *
1336 * 4. walk the directories, fixing the mapping and direntries, and marking
1337 * the handled mappings as not deleted
1338 *
1339 * 5. commit the contents of the files
1340 *
1341 * 6. handle deleted files and directories
1342 *
1343 */
1344
1345typedef struct commit_t {
1346 char* path;
1347 union {
1348 struct { uint32_t cluster; } rename;
1349 struct { int dir_index; uint32_t modified_offset; } writeout;
1350 struct { uint32_t first_cluster; } new_file;
1351 struct { uint32_t cluster; } mkdir;
1352 } param;
1353 /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
1354 enum {
1355 ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
1356 } action;
1357} commit_t;
1358
1359static void clear_commits(BDRVVVFATState* s)
1360{
1361 int i;
1362DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
1363 for (i = 0; i < s->commits.next; i++) {
1364 commit_t* commit = array_get(&(s->commits), i);
1365 assert(commit->path || commit->action == ACTION_WRITEOUT);
1366 if (commit->action != ACTION_WRITEOUT) {
1367 assert(commit->path);
1368 free(commit->path);
1369 } else
1370 assert(commit->path == NULL);
1371 }
1372 s->commits.next = 0;
1373}
1374
1375static void schedule_rename(BDRVVVFATState* s,
1376 uint32_t cluster, char* new_path)
1377{
1378 commit_t* commit = array_get_next(&(s->commits));
1379 commit->path = new_path;
1380 commit->param.rename.cluster = cluster;
1381 commit->action = ACTION_RENAME;
1382}
1383
1384static void schedule_writeout(BDRVVVFATState* s,
1385 int dir_index, uint32_t modified_offset)
1386{
1387 commit_t* commit = array_get_next(&(s->commits));
1388 commit->path = NULL;
1389 commit->param.writeout.dir_index = dir_index;
1390 commit->param.writeout.modified_offset = modified_offset;
1391 commit->action = ACTION_WRITEOUT;
1392}
1393
1394static void schedule_new_file(BDRVVVFATState* s,
1395 char* path, uint32_t first_cluster)
1396{
1397 commit_t* commit = array_get_next(&(s->commits));
1398 commit->path = path;
1399 commit->param.new_file.first_cluster = first_cluster;
1400 commit->action = ACTION_NEW_FILE;
1401}
1402
1403static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
1404{
1405 commit_t* commit = array_get_next(&(s->commits));
1406 commit->path = path;
1407 commit->param.mkdir.cluster = cluster;
1408 commit->action = ACTION_MKDIR;
1409}
1410
1411typedef struct {
1412 unsigned char name[1024];
1413 int checksum, len;
1414 int sequence_number;
1415} long_file_name;
1416
1417static void lfn_init(long_file_name* lfn)
1418{
1419 lfn->sequence_number = lfn->len = 0;
1420 lfn->checksum = 0x100;
1421}
1422
1423/* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
1424static int parse_long_name(long_file_name* lfn,
1425 const direntry_t* direntry)
1426{
1427 int i, j, offset;
1428 const unsigned char* pointer = (const unsigned char*)direntry;
1429
1430 if (!is_long_name(direntry))
1431 return 1;
1432
1433 if (pointer[0] & 0x40) {
1434 lfn->sequence_number = pointer[0] & 0x3f;
1435 lfn->checksum = pointer[13];
1436 lfn->name[0] = 0;
1437 } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
1438 return -1;
1439 else if (pointer[13] != lfn->checksum)
1440 return -2;
1441 else if (pointer[12] || pointer[26] || pointer[27])
1442 return -3;
1443
1444 offset = 13 * (lfn->sequence_number - 1);
1445 for (i = 0, j = 1; i < 13; i++, j+=2) {
1446 if (j == 11)
1447 j = 14;
1448 else if (j == 26)
1449 j = 28;
1450
1451 if (pointer[j+1] == 0)
1452 lfn->name[offset + i] = pointer[j];
1453 else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
1454 return -4;
1455 else
1456 lfn->name[offset + i] = 0;
1457 }
1458
1459 if (pointer[0] & 0x40)
1460 lfn->len = offset + strlen(lfn->name + offset);
1461
1462 return 0;
1463}
1464
1465/* returns 0 if successful, >0 if no short_name, and <0 on error */
1466static int parse_short_name(BDRVVVFATState* s,
1467 long_file_name* lfn, direntry_t* direntry)
1468{
1469 int i, j;
1470
1471 if (!is_short_name(direntry))
1472 return 1;
1473
1474 for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
1475 for (i = 0; i <= j; i++) {
1476 if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
1477 return -1;
1478 else if (s->downcase_short_names)
1479 lfn->name[i] = tolower(direntry->name[i]);
1480 else
1481 lfn->name[i] = direntry->name[i];
1482 }
1483
1484 for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
1485 if (j >= 0) {
1486 lfn->name[i++] = '.';
1487 lfn->name[i + j + 1] = '\0';
1488 for (;j >= 0; j--) {
1489 if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
1490 return -2;
1491 else if (s->downcase_short_names)
1492 lfn->name[i + j] = tolower(direntry->extension[j]);
1493 else
1494 lfn->name[i + j] = direntry->extension[j];
1495 }
1496 } else
1497 lfn->name[i + j + 1] = '\0';
1498
1499 lfn->len = strlen(lfn->name);
1500
1501 return 0;
1502}
1503
1504static inline uint32_t modified_fat_get(BDRVVVFATState* s,
1505 unsigned int cluster)
1506{
1507 if (cluster < s->last_cluster_of_root_directory) {
1508 if (cluster + 1 == s->last_cluster_of_root_directory)
1509 return s->max_fat_value;
1510 else
1511 return cluster + 1;
1512 }
1513
1514 if (s->fat_type==32) {
1515 uint32_t* entry=((uint32_t*)s->fat2)+cluster;
1516 return le32_to_cpu(*entry);
1517 } else if (s->fat_type==16) {
1518 uint16_t* entry=((uint16_t*)s->fat2)+cluster;
1519 return le16_to_cpu(*entry);
1520 } else {
1521 const uint8_t* x=s->fat2+cluster*3/2;
1522 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
1523 }
1524}
1525
1526static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
1527{
1528 int was_modified = 0;
1529 int i, dummy;
1530
1531 if (s->qcow == NULL)
1532 return 0;
1533
1534 for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
1535 was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
1536 cluster2sector(s, cluster_num) + i, 1, &dummy);
1537
1538 return was_modified;
1539}
1540
1541static const char* get_basename(const char* path)
1542{
1543 char* basename = strrchr(path, '/');
1544 if (basename == NULL)
1545 return path;
1546 else
1547 return basename + 1; /* strip '/' */
1548}
1549
1550/*
1551 * The array s->used_clusters holds the states of the clusters. If it is
1552 * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
1553 * was modified, bit 3 is set.
1554 * If any cluster is allocated, but not part of a file or directory, this
1555 * driver refuses to commit.
1556 */
1557typedef enum {
1558 USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
1559} used_t;
1560
1561/*
1562 * get_cluster_count_for_direntry() not only determines how many clusters
1563 * are occupied by direntry, but also if it was renamed or modified.
1564 *
1565 * A file is thought to be renamed *only* if there already was a file with
1566 * exactly the same first cluster, but a different name.
1567 *
1568 * Further, the files/directories handled by this function are
1569 * assumed to be *not* deleted (and *only* those).
1570 */
1571static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
1572 direntry_t* direntry, const char* path)
1573{
1574 /*
1575 * This is a little bit tricky:
1576 * IF the guest OS just inserts a cluster into the file chain,
1577 * and leaves the rest alone, (i.e. the original file had clusters
1578 * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
1579 *
1580 * - do_commit will write the cluster into the file at the given
1581 * offset, but
1582 *
1583 * - the cluster which is overwritten should be moved to a later
1584 * position in the file.
1585 *
1586 * I am not aware that any OS does something as braindead, but this
1587 * situation could happen anyway when not committing for a long time.
1588 * Just to be sure that this does not bite us, detect it, and copy the
1589 * contents of the clusters to-be-overwritten into the qcow.
1590 */
1591 int copy_it = 0;
1592 int was_modified = 0;
1593 int32_t ret = 0;
1594
1595 uint32_t cluster_num = begin_of_direntry(direntry);
1596 uint32_t offset = 0;
1597 int first_mapping_index = -1;
1598 mapping_t* mapping = NULL;
1599 const char* basename2 = NULL;
1600
1601 vvfat_close_current_file(s);
1602
1603 /* the root directory */
1604 if (cluster_num == 0)
1605 return 0;
1606
1607 /* write support */
1608 if (s->qcow) {
1609 basename2 = get_basename(path);
1610
1611 mapping = find_mapping_for_cluster(s, cluster_num);
1612
1613 if (mapping) {
bellardda2414e2006-04-23 14:36:41 +00001614 const char* basename;
1615
bellarda0464332005-12-18 18:29:50 +00001616 assert(mapping->mode & MODE_DELETED);
1617 mapping->mode &= ~MODE_DELETED;
1618
bellardda2414e2006-04-23 14:36:41 +00001619 basename = get_basename(mapping->path);
bellarda0464332005-12-18 18:29:50 +00001620
1621 assert(mapping->mode & MODE_NORMAL);
1622
1623 /* rename */
1624 if (strcmp(basename, basename2))
1625 schedule_rename(s, cluster_num, strdup(path));
1626 } else if (is_file(direntry))
1627 /* new file */
1628 schedule_new_file(s, strdup(path), cluster_num);
1629 else {
1630 assert(0);
1631 return 0;
1632 }
1633 }
1634
1635 while(1) {
1636 if (s->qcow) {
1637 if (!copy_it && cluster_was_modified(s, cluster_num)) {
1638 if (mapping == NULL ||
1639 mapping->begin > cluster_num ||
1640 mapping->end <= cluster_num)
1641 mapping = find_mapping_for_cluster(s, cluster_num);
1642
1643
1644 if (mapping &&
1645 (mapping->mode & MODE_DIRECTORY) == 0) {
1646
1647 /* was modified in qcow */
1648 if (offset != mapping->info.file.offset + s->cluster_size
1649 * (cluster_num - mapping->begin)) {
1650 /* offset of this cluster in file chain has changed */
1651 assert(0);
1652 copy_it = 1;
1653 } else if (offset == 0) {
1654 const char* basename = get_basename(mapping->path);
1655
1656 if (strcmp(basename, basename2))
1657 copy_it = 1;
1658 first_mapping_index = array_index(&(s->mapping), mapping);
1659 }
1660
1661 if (mapping->first_mapping_index != first_mapping_index
1662 && mapping->info.file.offset > 0) {
1663 assert(0);
1664 copy_it = 1;
1665 }
1666
1667 /* need to write out? */
1668 if (!was_modified && is_file(direntry)) {
1669 was_modified = 1;
1670 schedule_writeout(s, mapping->dir_index, offset);
1671 }
bellardde167e42005-04-28 21:15:08 +00001672 }
bellarda0464332005-12-18 18:29:50 +00001673 }
bellardde167e42005-04-28 21:15:08 +00001674
bellarda0464332005-12-18 18:29:50 +00001675 if (copy_it) {
1676 int i, dummy;
1677 /*
1678 * This is horribly inefficient, but that is okay, since
1679 * it is rarely executed, if at all.
1680 */
1681 int64_t offset = cluster2sector(s, cluster_num);
bellardde167e42005-04-28 21:15:08 +00001682
bellarda0464332005-12-18 18:29:50 +00001683 vvfat_close_current_file(s);
1684 for (i = 0; i < s->sectors_per_cluster; i++)
1685 if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
1686 offset + i, 1, &dummy)) {
1687 if (vvfat_read(s->bs,
1688 offset, s->cluster_buffer, 1))
1689 return -1;
1690 if (s->qcow->drv->bdrv_write(s->qcow,
1691 offset, s->cluster_buffer, 1))
1692 return -2;
1693 }
bellardde167e42005-04-28 21:15:08 +00001694 }
1695 }
bellarda0464332005-12-18 18:29:50 +00001696
1697 ret++;
1698 if (s->used_clusters[cluster_num] & USED_ANY)
1699 return 0;
1700 s->used_clusters[cluster_num] = USED_FILE;
1701
1702 cluster_num = modified_fat_get(s, cluster_num);
1703
1704 if (fat_eof(s, cluster_num))
1705 return ret;
1706 else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
1707 return -1;
1708
1709 offset += s->cluster_size;
bellardde167e42005-04-28 21:15:08 +00001710 }
1711}
1712
bellarda0464332005-12-18 18:29:50 +00001713/*
ths5fafdf22007-09-16 21:08:06 +00001714 * This function looks at the modified data (qcow).
bellarda0464332005-12-18 18:29:50 +00001715 * It returns 0 upon inconsistency or error, and the number of clusters
1716 * used by the directory, its subdirectories and their files.
1717 */
1718static int check_directory_consistency(BDRVVVFATState *s,
1719 int cluster_num, const char* path)
bellardde167e42005-04-28 21:15:08 +00001720{
bellarda0464332005-12-18 18:29:50 +00001721 int ret = 0;
1722 unsigned char* cluster = malloc(s->cluster_size);
1723 direntry_t* direntries = (direntry_t*)cluster;
1724 mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
bellardde167e42005-04-28 21:15:08 +00001725
bellarda0464332005-12-18 18:29:50 +00001726 long_file_name lfn;
1727 int path_len = strlen(path);
1728 char path2[PATH_MAX];
bellardde167e42005-04-28 21:15:08 +00001729
bellarda0464332005-12-18 18:29:50 +00001730 assert(path_len < PATH_MAX); /* len was tested before! */
1731 strcpy(path2, path);
1732 path2[path_len] = '/';
1733 path2[path_len + 1] = '\0';
bellardde167e42005-04-28 21:15:08 +00001734
bellarda0464332005-12-18 18:29:50 +00001735 if (mapping) {
1736 const char* basename = get_basename(mapping->path);
1737 const char* basename2 = get_basename(path);
1738
1739 assert(mapping->mode & MODE_DIRECTORY);
1740
1741 assert(mapping->mode & MODE_DELETED);
1742 mapping->mode &= ~MODE_DELETED;
1743
1744 if (strcmp(basename, basename2))
1745 schedule_rename(s, cluster_num, strdup(path));
1746 } else
1747 /* new directory */
1748 schedule_mkdir(s, cluster_num, strdup(path));
ths3b46e622007-09-17 08:09:54 +00001749
bellarda0464332005-12-18 18:29:50 +00001750 lfn_init(&lfn);
1751 do {
1752 int i;
1753 int subret = 0;
1754
1755 ret++;
1756
1757 if (s->used_clusters[cluster_num] & USED_ANY) {
1758 fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
1759 return 0;
bellardde167e42005-04-28 21:15:08 +00001760 }
bellarda0464332005-12-18 18:29:50 +00001761 s->used_clusters[cluster_num] = USED_DIRECTORY;
bellardde167e42005-04-28 21:15:08 +00001762
bellarda0464332005-12-18 18:29:50 +00001763DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
1764 subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
1765 s->sectors_per_cluster);
1766 if (subret) {
1767 fprintf(stderr, "Error fetching direntries\n");
1768 fail:
1769 free(cluster);
1770 return 0;
1771 }
1772
1773 for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
1774 int cluster_count;
1775
1776DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i));
1777 if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
1778 is_free(direntries + i))
1779 continue;
1780
1781 subret = parse_long_name(&lfn, direntries + i);
1782 if (subret < 0) {
1783 fprintf(stderr, "Error in long name\n");
1784 goto fail;
bellardde167e42005-04-28 21:15:08 +00001785 }
bellarda0464332005-12-18 18:29:50 +00001786 if (subret == 0 || is_free(direntries + i))
1787 continue;
1788
1789 if (fat_chksum(direntries+i) != lfn.checksum) {
1790 subret = parse_short_name(s, &lfn, direntries + i);
1791 if (subret < 0) {
1792 fprintf(stderr, "Error in short name (%d)\n", subret);
1793 goto fail;
1794 }
1795 if (subret > 0 || !strcmp(lfn.name, ".")
1796 || !strcmp(lfn.name, ".."))
1797 continue;
1798 }
1799 lfn.checksum = 0x100; /* cannot use long name twice */
1800
1801 if (path_len + 1 + lfn.len >= PATH_MAX) {
1802 fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
1803 goto fail;
1804 }
1805 strcpy(path2 + path_len + 1, lfn.name);
1806
1807 if (is_directory(direntries + i)) {
1808 if (begin_of_direntry(direntries + i) == 0) {
1809 DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
1810 goto fail;
1811 }
1812 cluster_count = check_directory_consistency(s,
1813 begin_of_direntry(direntries + i), path2);
1814 if (cluster_count == 0) {
1815 DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
1816 goto fail;
1817 }
1818 } else if (is_file(direntries + i)) {
1819 /* check file size with FAT */
1820 cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
1821 if (cluster_count !=
1822 (le32_to_cpu(direntries[i].size) + s->cluster_size
1823 - 1) / s->cluster_size) {
1824 DLOG(fprintf(stderr, "Cluster count mismatch\n"));
1825 goto fail;
1826 }
1827 } else
1828 assert(0); /* cluster_count = 0; */
1829
1830 ret += cluster_count;
bellardde167e42005-04-28 21:15:08 +00001831 }
bellarda0464332005-12-18 18:29:50 +00001832
1833 cluster_num = modified_fat_get(s, cluster_num);
1834 } while(!fat_eof(s, cluster_num));
1835
1836 free(cluster);
1837 return ret;
bellardde167e42005-04-28 21:15:08 +00001838}
1839
bellarda0464332005-12-18 18:29:50 +00001840/* returns 1 on success */
1841static int is_consistent(BDRVVVFATState* s)
bellardde167e42005-04-28 21:15:08 +00001842{
bellarda0464332005-12-18 18:29:50 +00001843 int i, check;
1844 int used_clusters_count = 0;
1845
1846DLOG(checkpoint());
1847 /*
1848 * - get modified FAT
1849 * - compare the two FATs (TODO)
1850 * - get buffer for marking used clusters
1851 * - recurse direntries from root (using bs->bdrv_read to make
1852 * sure to get the new data)
1853 * - check that the FAT agrees with the size
1854 * - count the number of clusters occupied by this directory and
1855 * its files
1856 * - check that the cumulative used cluster count agrees with the
1857 * FAT
1858 * - if all is fine, return number of used clusters
1859 */
1860 if (s->fat2 == NULL) {
1861 int size = 0x200 * s->sectors_per_fat;
1862 s->fat2 = malloc(size);
1863 memcpy(s->fat2, s->fat.pointer, size);
1864 }
1865 check = vvfat_read(s->bs,
1866 s->first_sectors_number, s->fat2, s->sectors_per_fat);
1867 if (check) {
1868 fprintf(stderr, "Could not copy fat\n");
bellardde167e42005-04-28 21:15:08 +00001869 return 0;
1870 }
bellarda0464332005-12-18 18:29:50 +00001871 assert (s->used_clusters);
1872 for (i = 0; i < sector2cluster(s, s->sector_count); i++)
1873 s->used_clusters[i] &= ~USED_ANY;
1874
1875 clear_commits(s);
1876
1877 /* mark every mapped file/directory as deleted.
1878 * (check_directory_consistency() will unmark those still present). */
1879 if (s->qcow)
1880 for (i = 0; i < s->mapping.next; i++) {
1881 mapping_t* mapping = array_get(&(s->mapping), i);
1882 if (mapping->first_mapping_index < 0)
1883 mapping->mode |= MODE_DELETED;
1884 }
1885
1886 used_clusters_count = check_directory_consistency(s, 0, s->path);
1887 if (used_clusters_count <= 0) {
1888 DLOG(fprintf(stderr, "problem in directory\n"));
1889 return 0;
1890 }
1891
1892 check = s->last_cluster_of_root_directory;
1893 for (i = check; i < sector2cluster(s, s->sector_count); i++) {
1894 if (modified_fat_get(s, i)) {
1895 if(!s->used_clusters[i]) {
1896 DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
1897 return 0;
1898 }
1899 check++;
1900 }
1901
1902 if (s->used_clusters[i] == USED_ALLOCATED) {
1903 /* allocated, but not used... */
1904 DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
1905 return 0;
1906 }
1907 }
1908
1909 if (check != used_clusters_count)
1910 return 0;
1911
1912 return used_clusters_count;
bellardde167e42005-04-28 21:15:08 +00001913}
1914
bellarda0464332005-12-18 18:29:50 +00001915static inline void adjust_mapping_indices(BDRVVVFATState* s,
1916 int offset, int adjust)
bellardde167e42005-04-28 21:15:08 +00001917{
bellarda0464332005-12-18 18:29:50 +00001918 int i;
1919
1920 for (i = 0; i < s->mapping.next; i++) {
1921 mapping_t* mapping = array_get(&(s->mapping), i);
1922
1923#define ADJUST_MAPPING_INDEX(name) \
1924 if (mapping->name >= offset) \
1925 mapping->name += adjust
1926
1927 ADJUST_MAPPING_INDEX(first_mapping_index);
1928 if (mapping->mode & MODE_DIRECTORY)
1929 ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
1930 }
bellardde167e42005-04-28 21:15:08 +00001931}
1932
bellarda0464332005-12-18 18:29:50 +00001933/* insert or update mapping */
1934static mapping_t* insert_mapping(BDRVVVFATState* s,
1935 uint32_t begin, uint32_t end)
bellardde167e42005-04-28 21:15:08 +00001936{
bellarda0464332005-12-18 18:29:50 +00001937 /*
1938 * - find mapping where mapping->begin >= begin,
1939 * - if mapping->begin > begin: insert
1940 * - adjust all references to mappings!
1941 * - else: adjust
1942 * - replace name
1943 */
1944 int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
1945 mapping_t* mapping = NULL;
1946 mapping_t* first_mapping = array_get(&(s->mapping), 0);
1947
1948 if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
1949 && mapping->begin < begin) {
1950 mapping->end = begin;
1951 index++;
1952 mapping = array_get(&(s->mapping), index);
1953 }
1954 if (index >= s->mapping.next || mapping->begin > begin) {
1955 mapping = array_insert(&(s->mapping), index, 1);
1956 mapping->path = NULL;
1957 adjust_mapping_indices(s, index, +1);
1958 }
1959
bellardde167e42005-04-28 21:15:08 +00001960 mapping->begin = begin;
bellarda0464332005-12-18 18:29:50 +00001961 mapping->end = end;
1962
1963DLOG(mapping_t* next_mapping;
1964assert(index + 1 >= s->mapping.next ||
1965((next_mapping = array_get(&(s->mapping), index + 1)) &&
1966 next_mapping->begin >= end)));
1967
1968 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1969 s->current_mapping = array_get(&(s->mapping),
1970 s->current_mapping - first_mapping);
1971
1972 return mapping;
bellardde167e42005-04-28 21:15:08 +00001973}
1974
bellarda0464332005-12-18 18:29:50 +00001975static int remove_mapping(BDRVVVFATState* s, int mapping_index)
bellardde167e42005-04-28 21:15:08 +00001976{
bellarda0464332005-12-18 18:29:50 +00001977 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
1978 mapping_t* first_mapping = array_get(&(s->mapping), 0);
bellardde167e42005-04-28 21:15:08 +00001979
bellarda0464332005-12-18 18:29:50 +00001980 /* free mapping */
1981 if (mapping->first_mapping_index < 0)
1982 free(mapping->path);
bellardde167e42005-04-28 21:15:08 +00001983
bellarda0464332005-12-18 18:29:50 +00001984 /* remove from s->mapping */
1985 array_remove(&(s->mapping), mapping_index);
bellardde167e42005-04-28 21:15:08 +00001986
bellarda0464332005-12-18 18:29:50 +00001987 /* adjust all references to mappings */
1988 adjust_mapping_indices(s, mapping_index, -1);
bellardde167e42005-04-28 21:15:08 +00001989
bellarda0464332005-12-18 18:29:50 +00001990 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1991 s->current_mapping = array_get(&(s->mapping),
1992 s->current_mapping - first_mapping);
bellardde167e42005-04-28 21:15:08 +00001993
1994 return 0;
1995}
1996
bellarda0464332005-12-18 18:29:50 +00001997static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
bellardde167e42005-04-28 21:15:08 +00001998{
bellarda0464332005-12-18 18:29:50 +00001999 int i;
2000 for (i = 0; i < s->mapping.next; i++) {
2001 mapping_t* mapping = array_get(&(s->mapping), i);
2002 if (mapping->dir_index >= offset)
2003 mapping->dir_index += adjust;
2004 if ((mapping->mode & MODE_DIRECTORY) &&
2005 mapping->info.dir.first_dir_index >= offset)
2006 mapping->info.dir.first_dir_index += adjust;
bellardde167e42005-04-28 21:15:08 +00002007 }
bellardde167e42005-04-28 21:15:08 +00002008}
2009
bellarda0464332005-12-18 18:29:50 +00002010static direntry_t* insert_direntries(BDRVVVFATState* s,
2011 int dir_index, int count)
bellardde167e42005-04-28 21:15:08 +00002012{
bellarda0464332005-12-18 18:29:50 +00002013 /*
2014 * make room in s->directory,
2015 * adjust_dirindices
2016 */
2017 direntry_t* result = array_insert(&(s->directory), dir_index, count);
2018 if (result == NULL)
2019 return NULL;
2020 adjust_dirindices(s, dir_index, count);
bellardde167e42005-04-28 21:15:08 +00002021 return result;
2022}
2023
bellarda0464332005-12-18 18:29:50 +00002024static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
bellardde167e42005-04-28 21:15:08 +00002025{
bellarda0464332005-12-18 18:29:50 +00002026 int ret = array_remove_slice(&(s->directory), dir_index, count);
2027 if (ret)
2028 return ret;
2029 adjust_dirindices(s, dir_index, -count);
bellardde167e42005-04-28 21:15:08 +00002030 return 0;
2031}
2032
bellarda0464332005-12-18 18:29:50 +00002033/*
2034 * Adapt the mappings of the cluster chain starting at first cluster
2035 * (i.e. if a file starts at first_cluster, the chain is followed according
2036 * to the modified fat, and the corresponding entries in s->mapping are
2037 * adjusted)
2038 */
2039static int commit_mappings(BDRVVVFATState* s,
2040 uint32_t first_cluster, int dir_index)
bellardde167e42005-04-28 21:15:08 +00002041{
bellarda0464332005-12-18 18:29:50 +00002042 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2043 direntry_t* direntry = array_get(&(s->directory), dir_index);
2044 uint32_t cluster = first_cluster;
bellardde167e42005-04-28 21:15:08 +00002045
bellarda0464332005-12-18 18:29:50 +00002046 vvfat_close_current_file(s);
bellardde167e42005-04-28 21:15:08 +00002047
bellarda0464332005-12-18 18:29:50 +00002048 assert(mapping);
2049 assert(mapping->begin == first_cluster);
2050 mapping->first_mapping_index = -1;
2051 mapping->dir_index = dir_index;
2052 mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
2053 MODE_DIRECTORY : MODE_NORMAL;
bellardde167e42005-04-28 21:15:08 +00002054
bellarda0464332005-12-18 18:29:50 +00002055 while (!fat_eof(s, cluster)) {
2056 uint32_t c, c1;
bellardde167e42005-04-28 21:15:08 +00002057
bellarda0464332005-12-18 18:29:50 +00002058 for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
2059 c = c1, c1 = modified_fat_get(s, c1));
bellardde167e42005-04-28 21:15:08 +00002060
bellarda0464332005-12-18 18:29:50 +00002061 c++;
2062 if (c > mapping->end) {
2063 int index = array_index(&(s->mapping), mapping);
2064 int i, max_i = s->mapping.next - index;
2065 for (i = 1; i < max_i && mapping[i].begin < c; i++);
2066 while (--i > 0)
2067 remove_mapping(s, index + 1);
2068 }
2069 assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
2070 || mapping[1].begin >= c);
2071 mapping->end = c;
2072
2073 if (!fat_eof(s, c1)) {
2074 int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
2075 mapping_t* next_mapping = i >= s->mapping.next ? NULL :
2076 array_get(&(s->mapping), i);
2077
2078 if (next_mapping == NULL || next_mapping->begin > c1) {
2079 int i1 = array_index(&(s->mapping), mapping);
2080
2081 next_mapping = insert_mapping(s, c1, c1+1);
2082
2083 if (c1 < c)
2084 i1++;
2085 mapping = array_get(&(s->mapping), i1);
bellardde167e42005-04-28 21:15:08 +00002086 }
bellarda0464332005-12-18 18:29:50 +00002087
2088 next_mapping->dir_index = mapping->dir_index;
ths5fafdf22007-09-16 21:08:06 +00002089 next_mapping->first_mapping_index =
bellarda0464332005-12-18 18:29:50 +00002090 mapping->first_mapping_index < 0 ?
2091 array_index(&(s->mapping), mapping) :
2092 mapping->first_mapping_index;
2093 next_mapping->path = mapping->path;
2094 next_mapping->mode = mapping->mode;
2095 next_mapping->read_only = mapping->read_only;
2096 if (mapping->mode & MODE_DIRECTORY) {
2097 next_mapping->info.dir.parent_mapping_index =
2098 mapping->info.dir.parent_mapping_index;
2099 next_mapping->info.dir.first_dir_index =
2100 mapping->info.dir.first_dir_index +
2101 0x10 * s->sectors_per_cluster *
2102 (mapping->end - mapping->begin);
2103 } else
2104 next_mapping->info.file.offset = mapping->info.file.offset +
2105 mapping->end - mapping->begin;
2106
2107 mapping = next_mapping;
2108 }
ths3b46e622007-09-17 08:09:54 +00002109
bellarda0464332005-12-18 18:29:50 +00002110 cluster = c1;
2111 }
2112
2113 return 0;
2114}
2115
2116static int commit_direntries(BDRVVVFATState* s,
2117 int dir_index, int parent_mapping_index)
2118{
2119 direntry_t* direntry = array_get(&(s->directory), dir_index);
2120 uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
2121 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2122
2123 int factor = 0x10 * s->sectors_per_cluster;
2124 int old_cluster_count, new_cluster_count;
2125 int current_dir_index = mapping->info.dir.first_dir_index;
2126 int first_dir_index = current_dir_index;
2127 int ret, i;
2128 uint32_t c;
2129
2130DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
2131
2132 assert(direntry);
2133 assert(mapping);
2134 assert(mapping->begin == first_cluster);
2135 assert(mapping->info.dir.first_dir_index < s->directory.next);
2136 assert(mapping->mode & MODE_DIRECTORY);
2137 assert(dir_index == 0 || is_directory(direntry));
2138
2139 mapping->info.dir.parent_mapping_index = parent_mapping_index;
2140
2141 if (first_cluster == 0) {
2142 old_cluster_count = new_cluster_count =
2143 s->last_cluster_of_root_directory;
2144 } else {
2145 for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2146 c = fat_get(s, c))
2147 old_cluster_count++;
2148
2149 for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2150 c = modified_fat_get(s, c))
2151 new_cluster_count++;
2152 }
2153
2154 if (new_cluster_count > old_cluster_count) {
2155 if (insert_direntries(s,
2156 current_dir_index + factor * old_cluster_count,
2157 factor * (new_cluster_count - old_cluster_count)) == NULL)
2158 return -1;
2159 } else if (new_cluster_count < old_cluster_count)
2160 remove_direntries(s,
2161 current_dir_index + factor * new_cluster_count,
2162 factor * (old_cluster_count - new_cluster_count));
2163
2164 for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
2165 void* direntry = array_get(&(s->directory), current_dir_index);
2166 int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
2167 s->sectors_per_cluster);
2168 if (ret)
2169 return ret;
2170 assert(!strncmp(s->directory.pointer, "QEMU", 4));
2171 current_dir_index += factor;
2172 }
2173
2174 ret = commit_mappings(s, first_cluster, dir_index);
2175 if (ret)
2176 return ret;
2177
2178 /* recurse */
2179 for (i = 0; i < factor * new_cluster_count; i++) {
2180 direntry = array_get(&(s->directory), first_dir_index + i);
2181 if (is_directory(direntry) && !is_dot(direntry)) {
2182 mapping = find_mapping_for_cluster(s, first_cluster);
2183 assert(mapping->mode & MODE_DIRECTORY);
2184 ret = commit_direntries(s, first_dir_index + i,
2185 array_index(&(s->mapping), mapping));
2186 if (ret)
2187 return ret;
bellardde167e42005-04-28 21:15:08 +00002188 }
2189 }
bellarda0464332005-12-18 18:29:50 +00002190
bellardde167e42005-04-28 21:15:08 +00002191 return 0;
2192}
2193
bellarda0464332005-12-18 18:29:50 +00002194/* commit one file (adjust contents, adjust mapping),
2195 return first_mapping_index */
2196static int commit_one_file(BDRVVVFATState* s,
2197 int dir_index, uint32_t offset)
2198{
2199 direntry_t* direntry = array_get(&(s->directory), dir_index);
2200 uint32_t c = begin_of_direntry(direntry);
2201 uint32_t first_cluster = c;
2202 mapping_t* mapping = find_mapping_for_cluster(s, c);
2203 uint32_t size = filesize_of_direntry(direntry);
2204 char* cluster = malloc(s->cluster_size);
2205 uint32_t i;
2206 int fd = 0;
2207
2208 assert(offset < size);
2209 assert((offset % s->cluster_size) == 0);
2210
2211 for (i = s->cluster_size; i < offset; i += s->cluster_size)
2212 c = modified_fat_get(s, c);
2213
bellard6bcb76c2006-09-09 12:03:20 +00002214 fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
bellarda0464332005-12-18 18:29:50 +00002215 if (fd < 0) {
2216 fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
2217 strerror(errno), errno);
2218 return fd;
2219 }
2220 if (offset > 0)
2221 if (lseek(fd, offset, SEEK_SET) != offset)
2222 return -3;
2223
2224 while (offset < size) {
2225 uint32_t c1;
2226 int rest_size = (size - offset > s->cluster_size ?
2227 s->cluster_size : size - offset);
2228 int ret;
2229
2230 c1 = modified_fat_get(s, c);
2231
2232 assert((size - offset == 0 && fat_eof(s, c)) ||
2233 (size > offset && c >=2 && !fat_eof(s, c)));
2234 assert(size >= 0);
2235
2236 ret = vvfat_read(s->bs, cluster2sector(s, c),
2237 cluster, (rest_size + 0x1ff) / 0x200);
2238
2239 if (ret < 0)
2240 return ret;
2241
2242 if (write(fd, cluster, rest_size) < 0)
2243 return -2;
2244
2245 offset += rest_size;
2246 c = c1;
2247 }
2248
2249 ftruncate(fd, size);
2250 close(fd);
2251
2252 return commit_mappings(s, first_cluster, dir_index);
2253}
2254
2255#ifdef DEBUG
2256/* test, if all mappings point to valid direntries */
2257static void check1(BDRVVVFATState* s)
2258{
2259 int i;
2260 for (i = 0; i < s->mapping.next; i++) {
2261 mapping_t* mapping = array_get(&(s->mapping), i);
2262 if (mapping->mode & MODE_DELETED) {
2263 fprintf(stderr, "deleted\n");
2264 continue;
2265 }
2266 assert(mapping->dir_index >= 0);
2267 assert(mapping->dir_index < s->directory.next);
2268 direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
2269 assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
2270 if (mapping->mode & MODE_DIRECTORY) {
2271 assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
2272 assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
2273 }
2274 }
2275}
2276
2277/* test, if all direntries have mappings */
2278static void check2(BDRVVVFATState* s)
2279{
2280 int i;
2281 int first_mapping = -1;
2282
2283 for (i = 0; i < s->directory.next; i++) {
2284 direntry_t* direntry = array_get(&(s->directory), i);
2285
2286 if (is_short_name(direntry) && begin_of_direntry(direntry)) {
2287 mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
2288 assert(mapping);
2289 assert(mapping->dir_index == i || is_dot(direntry));
2290 assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
2291 }
2292
2293 if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
2294 /* cluster start */
2295 int j, count = 0;
2296
2297 for (j = 0; j < s->mapping.next; j++) {
2298 mapping_t* mapping = array_get(&(s->mapping), j);
2299 if (mapping->mode & MODE_DELETED)
2300 continue;
2301 if (mapping->mode & MODE_DIRECTORY) {
2302 if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
2303 assert(++count == 1);
2304 if (mapping->first_mapping_index == -1)
2305 first_mapping = array_index(&(s->mapping), mapping);
2306 else
2307 assert(first_mapping == mapping->first_mapping_index);
2308 if (mapping->info.dir.parent_mapping_index < 0)
2309 assert(j == 0);
2310 else {
2311 mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
2312 assert(parent->mode & MODE_DIRECTORY);
2313 assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
2314 }
2315 }
2316 }
2317 }
2318 if (count == 0)
2319 first_mapping = -1;
2320 }
2321 }
2322}
2323#endif
2324
2325static int handle_renames_and_mkdirs(BDRVVVFATState* s)
2326{
2327 int i;
2328
2329#ifdef DEBUG
2330 fprintf(stderr, "handle_renames\n");
2331 for (i = 0; i < s->commits.next; i++) {
2332 commit_t* commit = array_get(&(s->commits), i);
2333 fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
2334 }
2335#endif
2336
2337 for (i = 0; i < s->commits.next;) {
2338 commit_t* commit = array_get(&(s->commits), i);
2339 if (commit->action == ACTION_RENAME) {
2340 mapping_t* mapping = find_mapping_for_cluster(s,
2341 commit->param.rename.cluster);
2342 char* old_path = mapping->path;
2343
2344 assert(commit->path);
2345 mapping->path = commit->path;
2346 if (rename(old_path, mapping->path))
2347 return -2;
2348
2349 if (mapping->mode & MODE_DIRECTORY) {
2350 int l1 = strlen(mapping->path);
2351 int l2 = strlen(old_path);
2352 int diff = l1 - l2;
2353 direntry_t* direntry = array_get(&(s->directory),
2354 mapping->info.dir.first_dir_index);
2355 uint32_t c = mapping->begin;
2356 int i = 0;
2357
2358 /* recurse */
2359 while (!fat_eof(s, c)) {
2360 do {
2361 direntry_t* d = direntry + i;
2362
2363 if (is_file(d) || (is_directory(d) && !is_dot(d))) {
2364 mapping_t* m = find_mapping_for_cluster(s,
2365 begin_of_direntry(d));
2366 int l = strlen(m->path);
2367 char* new_path = malloc(l + diff + 1);
2368
2369 assert(!strncmp(m->path, mapping->path, l2));
2370
2371 strcpy(new_path, mapping->path);
2372 strcpy(new_path + l1, m->path + l2);
2373
2374 schedule_rename(s, m->begin, new_path);
2375 }
2376 i++;
2377 } while((i % (0x10 * s->sectors_per_cluster)) != 0);
2378 c = fat_get(s, c);
2379 }
2380 }
2381
2382 free(old_path);
2383 array_remove(&(s->commits), i);
2384 continue;
2385 } else if (commit->action == ACTION_MKDIR) {
2386 mapping_t* mapping;
2387 int j, parent_path_len;
2388
bellard48c2f062005-12-19 22:11:49 +00002389#ifdef __MINGW32__
2390 if (mkdir(commit->path))
2391 return -5;
2392#else
2393 if (mkdir(commit->path, 0755))
2394 return -5;
2395#endif
bellarda0464332005-12-18 18:29:50 +00002396
2397 mapping = insert_mapping(s, commit->param.mkdir.cluster,
2398 commit->param.mkdir.cluster + 1);
2399 if (mapping == NULL)
2400 return -6;
2401
2402 mapping->mode = MODE_DIRECTORY;
2403 mapping->read_only = 0;
2404 mapping->path = commit->path;
2405 j = s->directory.next;
2406 assert(j);
2407 insert_direntries(s, s->directory.next,
2408 0x10 * s->sectors_per_cluster);
2409 mapping->info.dir.first_dir_index = j;
2410
2411 parent_path_len = strlen(commit->path)
2412 - strlen(get_basename(commit->path)) - 1;
2413 for (j = 0; j < s->mapping.next; j++) {
2414 mapping_t* m = array_get(&(s->mapping), j);
2415 if (m->first_mapping_index < 0 && m != mapping &&
2416 !strncmp(m->path, mapping->path, parent_path_len) &&
2417 strlen(m->path) == parent_path_len)
2418 break;
2419 }
2420 assert(j < s->mapping.next);
2421 mapping->info.dir.parent_mapping_index = j;
2422
2423 array_remove(&(s->commits), i);
2424 continue;
2425 }
2426
2427 i++;
2428 }
2429 return 0;
2430}
2431
2432/*
2433 * TODO: make sure that the short name is not matching *another* file
2434 */
2435static int handle_commits(BDRVVVFATState* s)
2436{
2437 int i, fail = 0;
2438
2439 vvfat_close_current_file(s);
2440
2441 for (i = 0; !fail && i < s->commits.next; i++) {
2442 commit_t* commit = array_get(&(s->commits), i);
2443 switch(commit->action) {
2444 case ACTION_RENAME: case ACTION_MKDIR:
2445 assert(0);
2446 fail = -2;
2447 break;
2448 case ACTION_WRITEOUT: {
2449 direntry_t* entry = array_get(&(s->directory),
2450 commit->param.writeout.dir_index);
2451 uint32_t begin = begin_of_direntry(entry);
2452 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2453
2454 assert(mapping);
2455 assert(mapping->begin == begin);
2456 assert(commit->path == NULL);
2457
2458 if (commit_one_file(s, commit->param.writeout.dir_index,
2459 commit->param.writeout.modified_offset))
2460 fail = -3;
2461
2462 break;
2463 }
2464 case ACTION_NEW_FILE: {
2465 int begin = commit->param.new_file.first_cluster;
2466 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2467 direntry_t* entry;
2468 int i;
2469
2470 /* find direntry */
2471 for (i = 0; i < s->directory.next; i++) {
2472 entry = array_get(&(s->directory), i);
2473 if (is_file(entry) && begin_of_direntry(entry) == begin)
2474 break;
2475 }
2476
2477 if (i >= s->directory.next) {
2478 fail = -6;
2479 continue;
2480 }
2481
2482 /* make sure there exists an initial mapping */
2483 if (mapping && mapping->begin != begin) {
2484 mapping->end = begin;
2485 mapping = NULL;
2486 }
2487 if (mapping == NULL) {
2488 mapping = insert_mapping(s, begin, begin+1);
2489 }
2490 /* most members will be fixed in commit_mappings() */
2491 assert(commit->path);
2492 mapping->path = commit->path;
2493 mapping->read_only = 0;
2494 mapping->mode = MODE_NORMAL;
2495 mapping->info.file.offset = 0;
2496
2497 if (commit_one_file(s, i, 0))
2498 fail = -7;
2499
2500 break;
2501 }
2502 default:
2503 assert(0);
2504 }
2505 }
2506 if (i > 0 && array_remove_slice(&(s->commits), 0, i))
2507 return -1;
2508 return fail;
2509}
2510
2511static int handle_deletes(BDRVVVFATState* s)
2512{
2513 int i, deferred = 1, deleted = 1;
2514
2515 /* delete files corresponding to mappings marked as deleted */
2516 /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
2517 while (deferred && deleted) {
2518 deferred = 0;
2519 deleted = 0;
2520
2521 for (i = 1; i < s->mapping.next; i++) {
2522 mapping_t* mapping = array_get(&(s->mapping), i);
2523 if (mapping->mode & MODE_DELETED) {
2524 direntry_t* entry = array_get(&(s->directory),
2525 mapping->dir_index);
2526
2527 if (is_free(entry)) {
2528 /* remove file/directory */
2529 if (mapping->mode & MODE_DIRECTORY) {
2530 int j, next_dir_index = s->directory.next,
2531 first_dir_index = mapping->info.dir.first_dir_index;
2532
2533 if (rmdir(mapping->path) < 0) {
2534 if (errno == ENOTEMPTY) {
2535 deferred++;
2536 continue;
2537 } else
2538 return -5;
2539 }
2540
2541 for (j = 1; j < s->mapping.next; j++) {
2542 mapping_t* m = array_get(&(s->mapping), j);
2543 if (m->mode & MODE_DIRECTORY &&
2544 m->info.dir.first_dir_index >
2545 first_dir_index &&
2546 m->info.dir.first_dir_index <
2547 next_dir_index)
2548 next_dir_index =
2549 m->info.dir.first_dir_index;
2550 }
2551 remove_direntries(s, first_dir_index,
2552 next_dir_index - first_dir_index);
2553
2554 deleted++;
2555 }
2556 } else {
2557 if (unlink(mapping->path))
2558 return -4;
2559 deleted++;
2560 }
2561 DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
2562 remove_mapping(s, i);
2563 }
2564 }
2565 }
2566
2567 return 0;
2568}
2569
2570/*
2571 * synchronize mapping with new state:
2572 *
2573 * - copy FAT (with bdrv_read)
2574 * - mark all filenames corresponding to mappings as deleted
2575 * - recurse direntries from root (using bs->bdrv_read)
2576 * - delete files corresponding to mappings marked as deleted
2577 */
2578static int do_commit(BDRVVVFATState* s)
2579{
2580 int ret = 0;
2581
2582 /* the real meat are the commits. Nothing to do? Move along! */
2583 if (s->commits.next == 0)
2584 return 0;
2585
2586 vvfat_close_current_file(s);
2587
2588 ret = handle_renames_and_mkdirs(s);
2589 if (ret) {
2590 fprintf(stderr, "Error handling renames (%d)\n", ret);
2591 assert(0);
2592 return ret;
2593 }
2594
ths5fafdf22007-09-16 21:08:06 +00002595 /* copy FAT (with bdrv_read) */
bellarda0464332005-12-18 18:29:50 +00002596 memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
2597
2598 /* recurse direntries from root (using bs->bdrv_read) */
2599 ret = commit_direntries(s, 0, -1);
2600 if (ret) {
2601 fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
2602 assert(0);
2603 return ret;
2604 }
2605
2606 ret = handle_commits(s);
2607 if (ret) {
2608 fprintf(stderr, "Error handling commits (%d)\n", ret);
2609 assert(0);
2610 return ret;
2611 }
2612
2613 ret = handle_deletes(s);
2614 if (ret) {
2615 fprintf(stderr, "Error deleting\n");
2616 assert(0);
2617 return ret;
2618 }
2619
2620 s->qcow->drv->bdrv_make_empty(s->qcow);
2621
2622 memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
2623
2624DLOG(checkpoint());
2625 return 0;
2626}
2627
2628static int try_commit(BDRVVVFATState* s)
2629{
2630 vvfat_close_current_file(s);
2631DLOG(checkpoint());
2632 if(!is_consistent(s))
2633 return -1;
2634 return do_commit(s);
2635}
2636
ths5fafdf22007-09-16 21:08:06 +00002637static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
bellardde167e42005-04-28 21:15:08 +00002638 const uint8_t *buf, int nb_sectors)
2639{
ths5fafdf22007-09-16 21:08:06 +00002640 BDRVVVFATState *s = bs->opaque;
bellarda0464332005-12-18 18:29:50 +00002641 int i, ret;
bellardde167e42005-04-28 21:15:08 +00002642
bellarda0464332005-12-18 18:29:50 +00002643DLOG(checkpoint());
bellardde167e42005-04-28 21:15:08 +00002644
bellarda0464332005-12-18 18:29:50 +00002645 vvfat_close_current_file(s);
bellardde167e42005-04-28 21:15:08 +00002646
bellarda0464332005-12-18 18:29:50 +00002647 /*
2648 * Some sanity checks:
2649 * - do not allow writing to the boot sector
2650 * - do not allow to write non-ASCII filenames
2651 */
bellardde167e42005-04-28 21:15:08 +00002652
bellarda0464332005-12-18 18:29:50 +00002653 if (sector_num < s->first_sectors_number)
2654 return -1;
bellardde167e42005-04-28 21:15:08 +00002655
bellarda0464332005-12-18 18:29:50 +00002656 for (i = sector2cluster(s, sector_num);
2657 i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
2658 mapping_t* mapping = find_mapping_for_cluster(s, i);
2659 if (mapping) {
2660 if (mapping->read_only) {
2661 fprintf(stderr, "Tried to write to write-protected file %s\n",
2662 mapping->path);
2663 return -1;
2664 }
bellardde167e42005-04-28 21:15:08 +00002665
bellarda0464332005-12-18 18:29:50 +00002666 if (mapping->mode & MODE_DIRECTORY) {
2667 int begin = cluster2sector(s, i);
2668 int end = begin + s->sectors_per_cluster, k;
2669 int dir_index;
2670 const direntry_t* direntries;
2671 long_file_name lfn;
bellardde167e42005-04-28 21:15:08 +00002672
bellarda0464332005-12-18 18:29:50 +00002673 lfn_init(&lfn);
bellardde167e42005-04-28 21:15:08 +00002674
bellarda0464332005-12-18 18:29:50 +00002675 if (begin < sector_num)
2676 begin = sector_num;
2677 if (end > sector_num + nb_sectors)
2678 end = sector_num + nb_sectors;
ths5fafdf22007-09-16 21:08:06 +00002679 dir_index = mapping->dir_index +
bellarda0464332005-12-18 18:29:50 +00002680 0x10 * (begin - mapping->begin * s->sectors_per_cluster);
2681 direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
bellardde167e42005-04-28 21:15:08 +00002682
bellarda0464332005-12-18 18:29:50 +00002683 for (k = 0; k < (end - begin) * 0x10; k++) {
2684 /* do not allow non-ASCII filenames */
2685 if (parse_long_name(&lfn, direntries + k) < 0) {
2686 fprintf(stderr, "Warning: non-ASCII filename\n");
2687 return -1;
bellardde167e42005-04-28 21:15:08 +00002688 }
bellarda0464332005-12-18 18:29:50 +00002689 /* no access to the direntry of a read-only file */
2690 else if (is_short_name(direntries+k) &&
2691 (direntries[k].attributes & 1)) {
2692 if (memcmp(direntries + k,
2693 array_get(&(s->directory), dir_index + k),
2694 sizeof(direntry_t))) {
2695 fprintf(stderr, "Warning: tried to write to write-protected file\n");
2696 return -1;
bellardde167e42005-04-28 21:15:08 +00002697 }
bellardde167e42005-04-28 21:15:08 +00002698 }
2699 }
2700 }
bellarda0464332005-12-18 18:29:50 +00002701 i = mapping->end;
2702 } else
2703 i++;
bellardde167e42005-04-28 21:15:08 +00002704 }
bellarda0464332005-12-18 18:29:50 +00002705
2706 /*
2707 * Use qcow backend. Commit later.
2708 */
2709DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
2710 ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
2711 if (ret < 0) {
2712 fprintf(stderr, "Error writing to qcow backend\n");
2713 return ret;
2714 }
2715
2716 for (i = sector2cluster(s, sector_num);
2717 i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
2718 if (i >= 0)
2719 s->used_clusters[i] |= USED_ALLOCATED;
2720
2721DLOG(checkpoint());
2722 /* TODO: add timeout */
2723 try_commit(s);
2724
2725DLOG(checkpoint());
2726 return 0;
2727}
2728
2729static int vvfat_is_allocated(BlockDriverState *bs,
2730 int64_t sector_num, int nb_sectors, int* n)
2731{
2732 BDRVVVFATState* s = bs->opaque;
2733 *n = s->sector_count - sector_num;
2734 if (*n > nb_sectors)
2735 *n = nb_sectors;
2736 else if (*n < 0)
2737 return 0;
ths5fafdf22007-09-16 21:08:06 +00002738 return 1;
bellarda0464332005-12-18 18:29:50 +00002739}
2740
2741static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
2742 const uint8_t* buffer, int nb_sectors) {
2743 BDRVVVFATState* s = bs->opaque;
2744 return try_commit(s);
2745}
2746
2747static void write_target_close(BlockDriverState *bs) {
2748 BDRVVVFATState* s = bs->opaque;
2749 bdrv_delete(s->qcow);
2750 free(s->qcow_filename);
2751}
2752
2753static BlockDriver vvfat_write_target = {
2754 "vvfat_write_target", 0, NULL, NULL, NULL,
2755 write_target_commit,
2756 write_target_close,
2757 NULL, NULL, NULL
2758};
2759
2760static int enable_write_target(BDRVVVFATState *s)
2761{
2762 int size = sector2cluster(s, s->sector_count);
2763 s->used_clusters = calloc(size, 1);
2764
2765 array_init(&(s->commits), sizeof(commit_t));
2766
2767 s->qcow_filename = malloc(1024);
bellard83f64092006-08-01 16:21:11 +00002768 get_tmp_filename(s->qcow_filename, 1024);
bellarda0464332005-12-18 18:29:50 +00002769 if (bdrv_create(&bdrv_qcow,
2770 s->qcow_filename, s->sector_count, "fat:", 0) < 0)
2771 return -1;
2772 s->qcow = bdrv_new("");
2773 if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0)
2774 return -1;
2775
2776#ifndef _WIN32
2777 unlink(s->qcow_filename);
2778#endif
2779
2780 s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
2781 s->bs->backing_hd->drv = &vvfat_write_target;
2782 s->bs->backing_hd->opaque = s;
2783
bellardde167e42005-04-28 21:15:08 +00002784 return 0;
2785}
2786
2787static void vvfat_close(BlockDriverState *bs)
2788{
2789 BDRVVVFATState *s = bs->opaque;
2790
2791 vvfat_close_current_file(s);
2792 array_free(&(s->fat));
2793 array_free(&(s->directory));
2794 array_free(&(s->mapping));
bellarda0464332005-12-18 18:29:50 +00002795 if(s->cluster_buffer)
2796 free(s->cluster_buffer);
bellardde167e42005-04-28 21:15:08 +00002797}
2798
2799BlockDriver bdrv_vvfat = {
2800 "vvfat",
2801 sizeof(BDRVVVFATState),
bellard83f64092006-08-01 16:21:11 +00002802 NULL, /* no probe for protocols */
bellardde167e42005-04-28 21:15:08 +00002803 vvfat_open,
2804 vvfat_read,
2805 vvfat_write,
2806 vvfat_close,
pbrook7a6cba62006-06-04 11:39:07 +00002807 NULL, /* ??? Not sure if we can do any meaningful flushing. */
bellarda0464332005-12-18 18:29:50 +00002808 NULL,
bellard83f64092006-08-01 16:21:11 +00002809 vvfat_is_allocated,
2810 .protocol_name = "fat",
bellardde167e42005-04-28 21:15:08 +00002811};
2812
bellarda0464332005-12-18 18:29:50 +00002813#ifdef DEBUG
2814static void checkpoint() {
2815 assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
2816 check1(vvv);
2817 check2(vvv);
2818 assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
2819#if 0
2820 if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
2821 fprintf(stderr, "Nonono!\n");
2822 mapping_t* mapping;
2823 direntry_t* direntry;
2824 assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
2825 assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
2826 if (vvv->mapping.next<47)
2827 return;
2828 assert((mapping = array_get(&(vvv->mapping), 47)));
2829 assert(mapping->dir_index < vvv->directory.next);
2830 direntry = array_get(&(vvv->directory), mapping->dir_index);
2831 assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0);
2832#endif
2833 return;
2834 /* avoid compiler warnings: */
2835 hexdump(NULL, 100);
2836 remove_mapping(vvv, NULL);
2837 print_mapping(NULL);
2838 print_direntry(NULL);
2839}
2840#endif
bellardde167e42005-04-28 21:15:08 +00002841