blob: 79804a710782f6c541a862755060fcd281b32b7a [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
blueswir13f47aa82008-03-09 06:59:01 +000056static void checkpoint(void);
bellarda0464332005-12-18 18:29:50 +000057
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 < array->next);
97 return array->pointer + index * array->item_size;
98}
99
100static inline int array_ensure_allocated(array_t* array, int index)
101{
102 if((index + 1) * array->item_size > array->size) {
103 int new_size = (index + 32) * array->item_size;
ths2137b4c2008-08-06 08:37:17 +0000104 array->pointer = qemu_realloc(array->pointer, new_size);
bellarda0464332005-12-18 18:29:50 +0000105 if (!array->pointer)
106 return -1;
107 array->size = new_size;
108 array->next = index + 1;
bellardde167e42005-04-28 21:15:08 +0000109 }
bellarda0464332005-12-18 18:29:50 +0000110
111 return 0;
bellardde167e42005-04-28 21:15:08 +0000112}
113
114static inline void* array_get_next(array_t* array) {
bellarda0464332005-12-18 18:29:50 +0000115 unsigned int next = array->next;
116 void* result;
117
118 if (array_ensure_allocated(array, next) < 0)
119 return NULL;
120
121 array->next = next + 1;
122 result = array_get(array, next);
123
bellardde167e42005-04-28 21:15:08 +0000124 return result;
125}
126
127static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
128 if((array->next+count)*array->item_size>array->size) {
129 int increment=count*array->item_size;
ths2137b4c2008-08-06 08:37:17 +0000130 array->pointer=qemu_realloc(array->pointer,array->size+increment);
bellardde167e42005-04-28 21:15:08 +0000131 if(!array->pointer)
132 return 0;
133 array->size+=increment;
134 }
135 memmove(array->pointer+(index+count)*array->item_size,
136 array->pointer+index*array->item_size,
137 (array->next-index)*array->item_size);
138 array->next+=count;
139 return array->pointer+index*array->item_size;
140}
141
142/* this performs a "roll", so that the element which was at index_from becomes
143 * index_to, but the order of all other elements is preserved. */
144static inline int array_roll(array_t* array,int index_to,int index_from,int count)
145{
146 char* buf;
147 char* from;
148 char* to;
149 int is;
150
151 if(!array ||
152 index_to<0 || index_to>=array->next ||
153 index_from<0 || index_from>=array->next)
154 return -1;
ths3b46e622007-09-17 08:09:54 +0000155
bellardde167e42005-04-28 21:15:08 +0000156 if(index_to==index_from)
157 return 0;
158
159 is=array->item_size;
160 from=array->pointer+index_from*is;
161 to=array->pointer+index_to*is;
162 buf=malloc(is*count);
163 memcpy(buf,from,is*count);
164
165 if(index_to<index_from)
166 memmove(to+is*count,to,from-to);
167 else
168 memmove(from,from+is*count,to-from);
ths3b46e622007-09-17 08:09:54 +0000169
bellardde167e42005-04-28 21:15:08 +0000170 memcpy(to,buf,is*count);
171
172 free(buf);
173
174 return 0;
175}
176
pbrook9596ebb2007-11-18 01:44:38 +0000177static inline int array_remove_slice(array_t* array,int index, int count)
bellarda0464332005-12-18 18:29:50 +0000178{
179 assert(index >=0);
180 assert(count > 0);
181 assert(index + count <= array->next);
182 if(array_roll(array,array->next-1,index,count))
183 return -1;
184 array->next -= count;
185 return 0;
186}
187
pbrook9596ebb2007-11-18 01:44:38 +0000188static int array_remove(array_t* array,int index)
bellardde167e42005-04-28 21:15:08 +0000189{
bellarda0464332005-12-18 18:29:50 +0000190 return array_remove_slice(array, index, 1);
191}
192
193/* return the index for a given member */
pbrook9596ebb2007-11-18 01:44:38 +0000194static int array_index(array_t* array, void* pointer)
bellarda0464332005-12-18 18:29:50 +0000195{
196 size_t offset = (char*)pointer - array->pointer;
bellarda0464332005-12-18 18:29:50 +0000197 assert((offset % array->item_size) == 0);
198 assert(offset/array->item_size < array->next);
199 return offset/array->item_size;
bellardde167e42005-04-28 21:15:08 +0000200}
201
202/* These structures are used to fake a disk and the VFAT filesystem.
203 * For this reason we need to use __attribute__((packed)). */
204
205typedef struct bootsector_t {
206 uint8_t jump[3];
207 uint8_t name[8];
208 uint16_t sector_size;
209 uint8_t sectors_per_cluster;
210 uint16_t reserved_sectors;
211 uint8_t number_of_fats;
212 uint16_t root_entries;
bellarda0464332005-12-18 18:29:50 +0000213 uint16_t total_sectors16;
bellardde167e42005-04-28 21:15:08 +0000214 uint8_t media_type;
215 uint16_t sectors_per_fat;
216 uint16_t sectors_per_track;
217 uint16_t number_of_heads;
218 uint32_t hidden_sectors;
219 uint32_t total_sectors;
220 union {
221 struct {
222 uint8_t drive_number;
223 uint8_t current_head;
224 uint8_t signature;
225 uint32_t id;
226 uint8_t volume_label[11];
227 } __attribute__((packed)) fat16;
228 struct {
229 uint32_t sectors_per_fat;
230 uint16_t flags;
231 uint8_t major,minor;
232 uint32_t first_cluster_of_root_directory;
233 uint16_t info_sector;
234 uint16_t backup_boot_sector;
235 uint16_t ignored;
236 } __attribute__((packed)) fat32;
237 } u;
238 uint8_t fat_type[8];
239 uint8_t ignored[0x1c0];
240 uint8_t magic[2];
241} __attribute__((packed)) bootsector_t;
242
thsb5700942007-09-25 14:47:03 +0000243typedef struct {
244 uint8_t head;
245 uint8_t sector;
246 uint8_t cylinder;
247} mbr_chs_t;
248
bellardde167e42005-04-28 21:15:08 +0000249typedef struct partition_t {
250 uint8_t attributes; /* 0x80 = bootable */
thsb5700942007-09-25 14:47:03 +0000251 mbr_chs_t start_CHS;
252 uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
253 mbr_chs_t end_CHS;
bellardde167e42005-04-28 21:15:08 +0000254 uint32_t start_sector_long;
thsb5700942007-09-25 14:47:03 +0000255 uint32_t length_sector_long;
bellardde167e42005-04-28 21:15:08 +0000256} __attribute__((packed)) partition_t;
257
258typedef struct mbr_t {
thsb5700942007-09-25 14:47:03 +0000259 uint8_t ignored[0x1b8];
260 uint32_t nt_id;
261 uint8_t ignored2[2];
bellardde167e42005-04-28 21:15:08 +0000262 partition_t partition[4];
263 uint8_t magic[2];
264} __attribute__((packed)) mbr_t;
265
266typedef struct direntry_t {
267 uint8_t name[8];
268 uint8_t extension[3];
269 uint8_t attributes;
270 uint8_t reserved[2];
271 uint16_t ctime;
272 uint16_t cdate;
273 uint16_t adate;
274 uint16_t begin_hi;
275 uint16_t mtime;
276 uint16_t mdate;
277 uint16_t begin;
278 uint32_t size;
279} __attribute__((packed)) direntry_t;
280
281/* this structure are used to transparently access the files */
282
283typedef struct mapping_t {
bellarda0464332005-12-18 18:29:50 +0000284 /* begin is the first cluster, end is the last+1 */
285 uint32_t begin,end;
bellardde167e42005-04-28 21:15:08 +0000286 /* as s->directory is growable, no pointer may be used here */
287 unsigned int dir_index;
bellarda0464332005-12-18 18:29:50 +0000288 /* the clusters of a file may be in any order; this points to the first */
289 int first_mapping_index;
290 union {
291 /* offset is
292 * - the offset in the file (in clusters) for a file, or
293 * - the next cluster of the directory for a directory, and
294 * - the address of the buffer for a faked entry
295 */
296 struct {
297 uint32_t offset;
298 } file;
299 struct {
300 int parent_mapping_index;
301 int first_dir_index;
302 } dir;
303 } info;
304 /* path contains the full path, i.e. it always starts with s->path */
305 char* path;
306
307 enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2,
308 MODE_DIRECTORY = 4, MODE_FAKED = 8,
309 MODE_DELETED = 16, MODE_RENAMED = 32 } mode;
310 int read_only;
bellardde167e42005-04-28 21:15:08 +0000311} mapping_t;
312
bellarda0464332005-12-18 18:29:50 +0000313#ifdef DEBUG
314static void print_direntry(const struct direntry_t*);
315static void print_mapping(const struct mapping_t* mapping);
316#endif
bellardde167e42005-04-28 21:15:08 +0000317
318/* here begins the real VVFAT driver */
319
320typedef struct BDRVVVFATState {
bellarda0464332005-12-18 18:29:50 +0000321 BlockDriverState* bs; /* pointer to parent */
bellardde167e42005-04-28 21:15:08 +0000322 unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
323 unsigned char first_sectors[0x40*0x200];
ths3b46e622007-09-17 08:09:54 +0000324
bellardde167e42005-04-28 21:15:08 +0000325 int fat_type; /* 16 or 32 */
326 array_t fat,directory,mapping;
ths3b46e622007-09-17 08:09:54 +0000327
bellardde167e42005-04-28 21:15:08 +0000328 unsigned int cluster_size;
329 unsigned int sectors_per_cluster;
330 unsigned int sectors_per_fat;
331 unsigned int sectors_of_root_directory;
bellarda0464332005-12-18 18:29:50 +0000332 uint32_t last_cluster_of_root_directory;
bellardde167e42005-04-28 21:15:08 +0000333 unsigned int faked_sectors; /* how many sectors are faked before file data */
334 uint32_t sector_count; /* total number of sectors of the partition */
335 uint32_t cluster_count; /* total number of clusters of this partition */
bellardde167e42005-04-28 21:15:08 +0000336 uint32_t max_fat_value;
ths3b46e622007-09-17 08:09:54 +0000337
bellardde167e42005-04-28 21:15:08 +0000338 int current_fd;
bellardde167e42005-04-28 21:15:08 +0000339 mapping_t* current_mapping;
bellarda0464332005-12-18 18:29:50 +0000340 unsigned char* cluster; /* points to current cluster */
341 unsigned char* cluster_buffer; /* points to a buffer to hold temp data */
bellardde167e42005-04-28 21:15:08 +0000342 unsigned int current_cluster;
343
344 /* write support */
bellarda0464332005-12-18 18:29:50 +0000345 BlockDriverState* write_target;
346 char* qcow_filename;
347 BlockDriverState* qcow;
348 void* fat2;
349 char* used_clusters;
350 array_t commits;
351 const char* path;
352 int downcase_short_names;
bellardde167e42005-04-28 21:15:08 +0000353} BDRVVVFATState;
354
thsb5700942007-09-25 14:47:03 +0000355/* take the sector position spos and convert it to Cylinder/Head/Sector position
356 * if the position is outside the specified geometry, fill maximum value for CHS
357 * and return 1 to signal overflow.
358 */
359static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
360 int head,sector;
361 sector = spos % (bs->secs); spos/= bs->secs;
362 head = spos % (bs->heads); spos/= bs->heads;
363 if(spos >= bs->cyls){
364 /* Overflow,
365 it happens if 32bit sector positions are used, while CHS is only 24bit.
366 Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
367 chs->head = 0xFF;
368 chs->sector = 0xFF;
369 chs->cylinder = 0xFF;
370 return 1;
371 }
372 chs->head = (uint8_t)head;
373 chs->sector = (uint8_t)( (sector+1) | ((spos>>8)<<6) );
374 chs->cylinder = (uint8_t)spos;
375 return 0;
376}
bellardde167e42005-04-28 21:15:08 +0000377
bellardde167e42005-04-28 21:15:08 +0000378static void init_mbr(BDRVVVFATState* s)
379{
380 /* TODO: if the files mbr.img and bootsect.img exist, use them */
381 mbr_t* real_mbr=(mbr_t*)s->first_sectors;
382 partition_t* partition=&(real_mbr->partition[0]);
thsb5700942007-09-25 14:47:03 +0000383 int lba;
bellardde167e42005-04-28 21:15:08 +0000384
385 memset(s->first_sectors,0,512);
ths3b46e622007-09-17 08:09:54 +0000386
thsb5700942007-09-25 14:47:03 +0000387 /* Win NT Disk Signature */
388 real_mbr->nt_id= cpu_to_le32(0xbe1afdfa);
389
bellardde167e42005-04-28 21:15:08 +0000390 partition->attributes=0x80; /* bootable */
thsb5700942007-09-25 14:47:03 +0000391
392 /* LBA is used when partition is outside the CHS geometry */
393 lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1);
394 lba|= sector2CHS(s->bs, &partition->end_CHS, s->sector_count);
395
396 /*LBA partitions are identified only by start/length_sector_long not by CHS*/
397 partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1);
398 partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1);
399
bellarda0464332005-12-18 18:29:50 +0000400 /* FAT12/FAT16/FAT32 */
thsb5700942007-09-25 14:47:03 +0000401 /* DOS uses different types when partition is LBA,
402 probably to prevent older versions from using CHS on them */
403 partition->fs_type= s->fat_type==12 ? 0x1:
404 s->fat_type==16 ? (lba?0xe:0x06):
405 /*fat_tyoe==32*/ (lba?0xc:0x0b);
bellardde167e42005-04-28 21:15:08 +0000406
407 real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
408}
409
bellarda0464332005-12-18 18:29:50 +0000410/* direntry functions */
411
bellardde167e42005-04-28 21:15:08 +0000412/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
ths60fe76f2007-12-16 03:02:09 +0000413static inline int short2long_name(char* dest,const char* src)
bellardde167e42005-04-28 21:15:08 +0000414{
415 int i;
balrog1e080d52007-12-24 13:26:04 +0000416 int len;
bellardde167e42005-04-28 21:15:08 +0000417 for(i=0;i<129 && src[i];i++) {
418 dest[2*i]=src[i];
419 dest[2*i+1]=0;
420 }
balrog1e080d52007-12-24 13:26:04 +0000421 len=2*i;
bellardde167e42005-04-28 21:15:08 +0000422 dest[2*i]=dest[2*i+1]=0;
423 for(i=2*i+2;(i%26);i++)
424 dest[i]=0xff;
balrog1e080d52007-12-24 13:26:04 +0000425 return len;
bellardde167e42005-04-28 21:15:08 +0000426}
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 }
balrog1e080d52007-12-24 13:26:04 +0000442 for(i=0;i<26*number_of_entries;i++) {
bellardde167e42005-04-28 21:15:08 +0000443 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{
thsad1a8972008-07-01 16:44:58 +0000455 return direntry->name[0]==0xe5 || direntry->name[0]==0x00;
bellarda0464332005-12-18 18:29:50 +0000456}
457
458static char is_volume_label(const direntry_t* direntry)
459{
460 return direntry->attributes == 0x28;
461}
462
463static char is_long_name(const direntry_t* direntry)
464{
465 return direntry->attributes == 0xf;
466}
467
468static char is_short_name(const direntry_t* direntry)
469{
470 return !is_volume_label(direntry) && !is_long_name(direntry)
471 && !is_free(direntry);
472}
473
474static char is_directory(const direntry_t* direntry)
475{
476 return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
477}
478
479static inline char is_dot(const direntry_t* direntry)
480{
481 return is_short_name(direntry) && direntry->name[0] == '.';
482}
483
484static char is_file(const direntry_t* direntry)
485{
486 return is_short_name(direntry) && !is_directory(direntry);
487}
488
489static inline uint32_t begin_of_direntry(const direntry_t* direntry)
490{
491 return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
492}
493
494static inline uint32_t filesize_of_direntry(const direntry_t* direntry)
495{
496 return le32_to_cpu(direntry->size);
497}
498
499static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
500{
501 direntry->begin = cpu_to_le16(begin & 0xffff);
502 direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
503}
504
bellardde167e42005-04-28 21:15:08 +0000505/* fat functions */
506
bellarda0464332005-12-18 18:29:50 +0000507static inline uint8_t fat_chksum(const direntry_t* entry)
bellardde167e42005-04-28 21:15:08 +0000508{
509 uint8_t chksum=0;
510 int i;
511
512 for(i=0;i<11;i++)
513 chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0))
514 +(unsigned char)entry->name[i];
ths3b46e622007-09-17 08:09:54 +0000515
bellardde167e42005-04-28 21:15:08 +0000516 return chksum;
517}
518
519/* if return_time==0, this returns the fat_date, else the fat_time */
520static uint16_t fat_datetime(time_t time,int return_time) {
521 struct tm* t;
522#ifdef _WIN32
523 t=localtime(&time); /* this is not thread safe */
524#else
525 struct tm t1;
526 t=&t1;
527 localtime_r(&time,t);
528#endif
529 if(return_time)
530 return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
531 return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
532}
533
534static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
535{
bellarda0464332005-12-18 18:29:50 +0000536 if(s->fat_type==32) {
537 uint32_t* entry=array_get(&(s->fat),cluster);
538 *entry=cpu_to_le32(value);
bellardde167e42005-04-28 21:15:08 +0000539 } else if(s->fat_type==16) {
540 uint16_t* entry=array_get(&(s->fat),cluster);
541 *entry=cpu_to_le16(value&0xffff);
542 } else {
bellarda0464332005-12-18 18:29:50 +0000543 int offset = (cluster*3/2);
544 unsigned char* p = array_get(&(s->fat), offset);
545 switch (cluster&1) {
546 case 0:
547 p[0] = value&0xff;
548 p[1] = (p[1]&0xf0) | ((value>>8)&0xf);
549 break;
550 case 1:
551 p[0] = (p[0]&0xf) | ((value&0xf)<<4);
552 p[1] = (value>>4);
553 break;
554 }
bellardde167e42005-04-28 21:15:08 +0000555 }
556}
557
558static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
559{
bellarda0464332005-12-18 18:29:50 +0000560 if(s->fat_type==32) {
561 uint32_t* entry=array_get(&(s->fat),cluster);
562 return le32_to_cpu(*entry);
bellardde167e42005-04-28 21:15:08 +0000563 } else if(s->fat_type==16) {
564 uint16_t* entry=array_get(&(s->fat),cluster);
565 return le16_to_cpu(*entry);
566 } else {
thsffe8ab82007-12-16 03:16:05 +0000567 const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2;
bellarda0464332005-12-18 18:29:50 +0000568 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
bellardde167e42005-04-28 21:15:08 +0000569 }
570}
571
572static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
573{
574 if(fat_entry>s->max_fat_value-8)
575 return -1;
576 return 0;
577}
578
579static inline void init_fat(BDRVVVFATState* s)
580{
bellarda0464332005-12-18 18:29:50 +0000581 if (s->fat_type == 12) {
582 array_init(&(s->fat),1);
583 array_ensure_allocated(&(s->fat),
584 s->sectors_per_fat * 0x200 * 3 / 2 - 1);
585 } else {
586 array_init(&(s->fat),(s->fat_type==32?4:2));
587 array_ensure_allocated(&(s->fat),
588 s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
589 }
bellardde167e42005-04-28 21:15:08 +0000590 memset(s->fat.pointer,0,s->fat.size);
ths3b46e622007-09-17 08:09:54 +0000591
bellardde167e42005-04-28 21:15:08 +0000592 switch(s->fat_type) {
593 case 12: s->max_fat_value=0xfff; break;
594 case 16: s->max_fat_value=0xffff; break;
bellarda0464332005-12-18 18:29:50 +0000595 case 32: s->max_fat_value=0x0fffffff; break;
bellardde167e42005-04-28 21:15:08 +0000596 default: s->max_fat_value=0; /* error... */
597 }
598
599}
600
bellarda0464332005-12-18 18:29:50 +0000601/* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */
602/* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */
603static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
604 unsigned int directory_start, const char* filename, int is_dot)
bellardde167e42005-04-28 21:15:08 +0000605{
bellarda0464332005-12-18 18:29:50 +0000606 int i,j,long_index=s->directory.next;
bellardde167e42005-04-28 21:15:08 +0000607 direntry_t* entry=0;
608 direntry_t* entry_long=0;
609
610 if(is_dot) {
611 entry=array_get_next(&(s->directory));
612 memset(entry->name,0x20,11);
613 memcpy(entry->name,filename,strlen(filename));
614 return entry;
615 }
ths3b46e622007-09-17 08:09:54 +0000616
bellardde167e42005-04-28 21:15:08 +0000617 entry_long=create_long_filename(s,filename);
ths3b46e622007-09-17 08:09:54 +0000618
ths5fafdf22007-09-16 21:08:06 +0000619 i = strlen(filename);
bellarda0464332005-12-18 18:29:50 +0000620 for(j = i - 1; j>0 && filename[j]!='.';j--);
621 if (j > 0)
622 i = (j > 8 ? 8 : j);
623 else if (i > 8)
624 i = 8;
625
bellardde167e42005-04-28 21:15:08 +0000626 entry=array_get_next(&(s->directory));
627 memset(entry->name,0x20,11);
thsffe8ab82007-12-16 03:16:05 +0000628 strncpy((char*)entry->name,filename,i);
ths3b46e622007-09-17 08:09:54 +0000629
bellarda0464332005-12-18 18:29:50 +0000630 if(j > 0)
631 for (i = 0; i < 3 && filename[j+1+i]; i++)
632 entry->extension[i] = filename[j+1+i];
bellardde167e42005-04-28 21:15:08 +0000633
634 /* upcase & remove unwanted characters */
635 for(i=10;i>=0;i--) {
bellarda0464332005-12-18 18:29:50 +0000636 if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--);
bellardde167e42005-04-28 21:15:08 +0000637 if(entry->name[i]<=' ' || entry->name[i]>0x7f
bellarda0464332005-12-18 18:29:50 +0000638 || strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
bellardde167e42005-04-28 21:15:08 +0000639 entry->name[i]='_';
640 else if(entry->name[i]>='a' && entry->name[i]<='z')
641 entry->name[i]+='A'-'a';
642 }
643
644 /* mangle duplicates */
645 while(1) {
646 direntry_t* entry1=array_get(&(s->directory),directory_start);
647 int j;
648
649 for(;entry1<entry;entry1++)
bellarda0464332005-12-18 18:29:50 +0000650 if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11))
bellardde167e42005-04-28 21:15:08 +0000651 break; /* found dupe */
652 if(entry1==entry) /* no dupe found */
653 break;
654
ths5fafdf22007-09-16 21:08:06 +0000655 /* use all 8 characters of name */
bellardde167e42005-04-28 21:15:08 +0000656 if(entry->name[7]==' ') {
657 int j;
658 for(j=6;j>0 && entry->name[j]==' ';j--)
659 entry->name[j]='~';
660 }
661
662 /* increment number */
663 for(j=7;j>0 && entry->name[j]=='9';j--)
664 entry->name[j]='0';
665 if(j>0) {
666 if(entry->name[j]<'0' || entry->name[j]>'9')
667 entry->name[j]='0';
668 else
669 entry->name[j]++;
670 }
671 }
672
673 /* calculate checksum; propagate to long name */
674 if(entry_long) {
675 uint8_t chksum=fat_chksum(entry);
676
677 /* calculate anew, because realloc could have taken place */
678 entry_long=array_get(&(s->directory),long_index);
bellarda0464332005-12-18 18:29:50 +0000679 while(entry_long<entry && is_long_name(entry_long)) {
bellardde167e42005-04-28 21:15:08 +0000680 entry_long->reserved[1]=chksum;
681 entry_long++;
682 }
683 }
684
685 return entry;
686}
687
bellarda0464332005-12-18 18:29:50 +0000688/*
689 * Read a directory. (the index of the corresponding mapping must be passed).
690 */
691static int read_directory(BDRVVVFATState* s, int mapping_index)
bellardde167e42005-04-28 21:15:08 +0000692{
bellarda0464332005-12-18 18:29:50 +0000693 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
694 direntry_t* direntry;
695 const char* dirname = mapping->path;
696 int first_cluster = mapping->begin;
697 int parent_index = mapping->info.dir.parent_mapping_index;
698 mapping_t* parent_mapping = (mapping_t*)
699 (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : 0);
700 int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
bellardde167e42005-04-28 21:15:08 +0000701
702 DIR* dir=opendir(dirname);
703 struct dirent* entry;
bellardde167e42005-04-28 21:15:08 +0000704 int i;
705
bellarda0464332005-12-18 18:29:50 +0000706 assert(mapping->mode & MODE_DIRECTORY);
707
708 if(!dir) {
709 mapping->end = mapping->begin;
bellardde167e42005-04-28 21:15:08 +0000710 return -1;
bellarda0464332005-12-18 18:29:50 +0000711 }
ths3b46e622007-09-17 08:09:54 +0000712
bellarda0464332005-12-18 18:29:50 +0000713 i = mapping->info.dir.first_dir_index =
714 first_cluster == 0 ? 0 : s->directory.next;
715
ths5fafdf22007-09-16 21:08:06 +0000716 /* actually read the directory, and allocate the mappings */
bellardde167e42005-04-28 21:15:08 +0000717 while((entry=readdir(dir))) {
718 unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
719 char* buffer;
720 direntry_t* direntry;
bellarda0464332005-12-18 18:29:50 +0000721 struct stat st;
bellardde167e42005-04-28 21:15:08 +0000722 int is_dot=!strcmp(entry->d_name,".");
723 int is_dotdot=!strcmp(entry->d_name,"..");
724
bellarda0464332005-12-18 18:29:50 +0000725 if(first_cluster == 0 && (is_dotdot || is_dot))
bellardde167e42005-04-28 21:15:08 +0000726 continue;
ths5fafdf22007-09-16 21:08:06 +0000727
bellardde167e42005-04-28 21:15:08 +0000728 buffer=(char*)malloc(length);
bellarda0464332005-12-18 18:29:50 +0000729 assert(buffer);
bellardde167e42005-04-28 21:15:08 +0000730 snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
731
732 if(stat(buffer,&st)<0) {
733 free(buffer);
734 continue;
735 }
736
737 /* create directory entry for this file */
bellarda0464332005-12-18 18:29:50 +0000738 direntry=create_short_and_long_name(s, i, entry->d_name,
739 is_dot || is_dotdot);
bellardde167e42005-04-28 21:15:08 +0000740 direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
741 direntry->reserved[0]=direntry->reserved[1]=0;
742 direntry->ctime=fat_datetime(st.st_ctime,1);
743 direntry->cdate=fat_datetime(st.st_ctime,0);
744 direntry->adate=fat_datetime(st.st_atime,0);
745 direntry->begin_hi=0;
746 direntry->mtime=fat_datetime(st.st_mtime,1);
747 direntry->mdate=fat_datetime(st.st_mtime,0);
748 if(is_dotdot)
bellarda0464332005-12-18 18:29:50 +0000749 set_begin_of_direntry(direntry, first_cluster_of_parent);
bellardde167e42005-04-28 21:15:08 +0000750 else if(is_dot)
bellarda0464332005-12-18 18:29:50 +0000751 set_begin_of_direntry(direntry, first_cluster);
bellardde167e42005-04-28 21:15:08 +0000752 else
bellarda0464332005-12-18 18:29:50 +0000753 direntry->begin=0; /* do that later */
754 if (st.st_size > 0x7fffffff) {
755 fprintf(stderr, "File %s is larger than 2GB\n", buffer);
756 free(buffer);
757 return -2;
758 }
759 direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
bellardde167e42005-04-28 21:15:08 +0000760
761 /* create mapping for this file */
bellarda0464332005-12-18 18:29:50 +0000762 if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
763 s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
bellardde167e42005-04-28 21:15:08 +0000764 s->current_mapping->begin=0;
765 s->current_mapping->end=st.st_size;
bellarda0464332005-12-18 18:29:50 +0000766 /*
767 * we get the direntry of the most recent direntry, which
768 * contains the short name and all the relevant information.
769 */
bellardde167e42005-04-28 21:15:08 +0000770 s->current_mapping->dir_index=s->directory.next-1;
bellarda0464332005-12-18 18:29:50 +0000771 s->current_mapping->first_mapping_index = -1;
772 if (S_ISDIR(st.st_mode)) {
773 s->current_mapping->mode = MODE_DIRECTORY;
774 s->current_mapping->info.dir.parent_mapping_index =
775 mapping_index;
776 } else {
777 s->current_mapping->mode = MODE_UNDEFINED;
778 s->current_mapping->info.file.offset = 0;
779 }
780 s->current_mapping->path=buffer;
781 s->current_mapping->read_only =
782 (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
bellardde167e42005-04-28 21:15:08 +0000783 }
784 }
785 closedir(dir);
786
787 /* fill with zeroes up to the end of the cluster */
788 while(s->directory.next%(0x10*s->sectors_per_cluster)) {
789 direntry_t* direntry=array_get_next(&(s->directory));
790 memset(direntry,0,sizeof(direntry_t));
791 }
792
bellarda0464332005-12-18 18:29:50 +0000793/* TODO: if there are more entries, bootsector has to be adjusted! */
794#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster)
795 if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) {
796 /* root directory */
797 int cur = s->directory.next;
798 array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
799 memset(array_get(&(s->directory), cur), 0,
800 (ROOT_ENTRIES - cur) * sizeof(direntry_t));
bellardde167e42005-04-28 21:15:08 +0000801 }
ths5fafdf22007-09-16 21:08:06 +0000802
bellarda0464332005-12-18 18:29:50 +0000803 /* reget the mapping, since s->mapping was possibly realloc()ed */
804 mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
805 first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
806 * 0x20 / s->cluster_size;
807 mapping->end = first_cluster;
bellardde167e42005-04-28 21:15:08 +0000808
bellarda0464332005-12-18 18:29:50 +0000809 direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
810 set_begin_of_direntry(direntry, mapping->begin);
ths3b46e622007-09-17 08:09:54 +0000811
bellardde167e42005-04-28 21:15:08 +0000812 return 0;
813}
814
bellarda0464332005-12-18 18:29:50 +0000815static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
bellardde167e42005-04-28 21:15:08 +0000816{
bellarda0464332005-12-18 18:29:50 +0000817 return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
818}
819
820static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
821{
822 return s->faked_sectors + s->sectors_per_cluster * cluster_num;
823}
824
825static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num)
826{
827 return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster;
828}
829
830#ifdef DBG
831static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping)
832{
833 if(mapping->mode==MODE_UNDEFINED)
834 return 0;
835 return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index);
836}
837#endif
838
839static int init_directories(BDRVVVFATState* s,
840 const char* dirname)
841{
842 bootsector_t* bootsector;
843 mapping_t* mapping;
bellardde167e42005-04-28 21:15:08 +0000844 unsigned int i;
845 unsigned int cluster;
846
847 memset(&(s->first_sectors[0]),0,0x40*0x200);
848
bellardde167e42005-04-28 21:15:08 +0000849 s->cluster_size=s->sectors_per_cluster*0x200;
bellarda0464332005-12-18 18:29:50 +0000850 s->cluster_buffer=malloc(s->cluster_size);
851 assert(s->cluster_buffer);
852
853 /*
854 * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
855 * where sc is sector_count,
856 * spf is sectors_per_fat,
857 * spc is sectors_per_clusters, and
858 * fat_type = 12, 16 or 32.
859 */
860 i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
861 s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
ths3b46e622007-09-17 08:09:54 +0000862
bellardde167e42005-04-28 21:15:08 +0000863 array_init(&(s->mapping),sizeof(mapping_t));
864 array_init(&(s->directory),sizeof(direntry_t));
bellardde167e42005-04-28 21:15:08 +0000865
866 /* add volume label */
867 {
868 direntry_t* entry=array_get_next(&(s->directory));
869 entry->attributes=0x28; /* archive | volume label */
thsffe8ab82007-12-16 03:16:05 +0000870 snprintf((char*)entry->name,11,"QEMU VVFAT");
bellardde167e42005-04-28 21:15:08 +0000871 }
872
bellardde167e42005-04-28 21:15:08 +0000873 /* Now build FAT, and write back information into directory */
874 init_fat(s);
875
bellarda0464332005-12-18 18:29:50 +0000876 s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
877 s->cluster_count=sector2cluster(s, s->sector_count);
bellardde167e42005-04-28 21:15:08 +0000878
bellarda0464332005-12-18 18:29:50 +0000879 mapping = array_get_next(&(s->mapping));
880 mapping->begin = 0;
881 mapping->dir_index = 0;
882 mapping->info.dir.parent_mapping_index = -1;
883 mapping->first_mapping_index = -1;
884 mapping->path = strdup(dirname);
885 i = strlen(mapping->path);
886 if (i > 0 && mapping->path[i - 1] == '/')
887 mapping->path[i - 1] = '\0';
888 mapping->mode = MODE_DIRECTORY;
889 mapping->read_only = 0;
890 s->path = mapping->path;
bellardde167e42005-04-28 21:15:08 +0000891
bellarda0464332005-12-18 18:29:50 +0000892 for (i = 0, cluster = 0; i < s->mapping.next; i++) {
893 int j;
ths5fafdf22007-09-16 21:08:06 +0000894 /* MS-DOS expects the FAT to be 0 for the root directory
bellarda0464332005-12-18 18:29:50 +0000895 * (except for the media byte). */
896 /* LATER TODO: still true for FAT32? */
897 int fix_fat = (i != 0);
898 mapping = array_get(&(s->mapping), i);
bellardde167e42005-04-28 21:15:08 +0000899
bellarda0464332005-12-18 18:29:50 +0000900 if (mapping->mode & MODE_DIRECTORY) {
901 mapping->begin = cluster;
902 if(read_directory(s, i)) {
903 fprintf(stderr, "Could not read directory %s\n",
904 mapping->path);
bellardde167e42005-04-28 21:15:08 +0000905 return -1;
906 }
bellarda0464332005-12-18 18:29:50 +0000907 mapping = array_get(&(s->mapping), i);
908 } else {
909 assert(mapping->mode == MODE_UNDEFINED);
bellardde167e42005-04-28 21:15:08 +0000910 mapping->mode=MODE_NORMAL;
bellarda0464332005-12-18 18:29:50 +0000911 mapping->begin = cluster;
912 if (mapping->end > 0) {
913 direntry_t* direntry = array_get(&(s->directory),
914 mapping->dir_index);
bellardde167e42005-04-28 21:15:08 +0000915
bellarda0464332005-12-18 18:29:50 +0000916 mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
917 set_begin_of_direntry(direntry, mapping->begin);
918 } else {
919 mapping->end = cluster + 1;
920 fix_fat = 0;
921 }
922 }
923
924 assert(mapping->begin < mapping->end);
925
926 /* fix fat for entry */
927 if (fix_fat) {
928 for(j = mapping->begin; j < mapping->end - 1; j++)
929 fat_set(s, j, j+1);
930 fat_set(s, mapping->end - 1, s->max_fat_value);
931 }
932
933 /* next free cluster */
934 cluster = mapping->end;
935
936 if(cluster > s->cluster_count) {
937 fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type);
938 return -1;
bellardde167e42005-04-28 21:15:08 +0000939 }
940 }
941
bellarda0464332005-12-18 18:29:50 +0000942 mapping = array_get(&(s->mapping), 0);
943 s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
944 s->last_cluster_of_root_directory = mapping->end;
bellardde167e42005-04-28 21:15:08 +0000945
bellarda0464332005-12-18 18:29:50 +0000946 /* the FAT signature */
947 fat_set(s,0,s->max_fat_value);
948 fat_set(s,1,s->max_fat_value);
949
950 s->current_mapping = NULL;
951
952 bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
bellardde167e42005-04-28 21:15:08 +0000953 bootsector->jump[0]=0xeb;
954 bootsector->jump[1]=0x3e;
955 bootsector->jump[2]=0x90;
956 memcpy(bootsector->name,"QEMU ",8);
957 bootsector->sector_size=cpu_to_le16(0x200);
958 bootsector->sectors_per_cluster=s->sectors_per_cluster;
959 bootsector->reserved_sectors=cpu_to_le16(1);
960 bootsector->number_of_fats=0x2; /* number of FATs */
961 bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
bellarda0464332005-12-18 18:29:50 +0000962 bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
963 bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
964 s->fat.pointer[0] = bootsector->media_type;
bellardde167e42005-04-28 21:15:08 +0000965 bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
bellarda0464332005-12-18 18:29:50 +0000966 bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
967 bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
bellardde167e42005-04-28 21:15:08 +0000968 bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
bellarda0464332005-12-18 18:29:50 +0000969 bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
bellardde167e42005-04-28 21:15:08 +0000970
bellarda0464332005-12-18 18:29:50 +0000971 /* LATER TODO: if FAT32, this is wrong */
972 bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
bellardde167e42005-04-28 21:15:08 +0000973 bootsector->u.fat16.current_head=0;
974 bootsector->u.fat16.signature=0x29;
975 bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
976
977 memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
978 memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8);
979 bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
980
981 return 0;
982}
983
bellard83f64092006-08-01 16:21:11 +0000984#ifdef DEBUG
bellarda0464332005-12-18 18:29:50 +0000985static BDRVVVFATState *vvv = NULL;
bellard83f64092006-08-01 16:21:11 +0000986#endif
bellarda0464332005-12-18 18:29:50 +0000987
988static int enable_write_target(BDRVVVFATState *s);
989static int is_consistent(BDRVVVFATState *s);
990
bellard83f64092006-08-01 16:21:11 +0000991static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
bellardde167e42005-04-28 21:15:08 +0000992{
993 BDRVVVFATState *s = bs->opaque;
bellarda0464332005-12-18 18:29:50 +0000994 int floppy = 0;
bellardde167e42005-04-28 21:15:08 +0000995 int i;
996
bellard83f64092006-08-01 16:21:11 +0000997#ifdef DEBUG
bellarda0464332005-12-18 18:29:50 +0000998 vvv = s;
bellard83f64092006-08-01 16:21:11 +0000999#endif
bellarda0464332005-12-18 18:29:50 +00001000
1001DLOG(if (stderr == NULL) {
1002 stderr = fopen("vvfat.log", "a");
1003 setbuf(stderr, NULL);
1004})
1005
1006 s->bs = bs;
1007
bellardde167e42005-04-28 21:15:08 +00001008 s->fat_type=16;
bellarda0464332005-12-18 18:29:50 +00001009 /* LATER TODO: if FAT32, adjust */
bellarda0464332005-12-18 18:29:50 +00001010 s->sectors_per_cluster=0x10;
thsb5700942007-09-25 14:47:03 +00001011 /* 504MB disk*/
1012 bs->cyls=1024; bs->heads=16; bs->secs=63;
bellardde167e42005-04-28 21:15:08 +00001013
1014 s->current_cluster=0xffffffff;
bellardde167e42005-04-28 21:15:08 +00001015
bellardde167e42005-04-28 21:15:08 +00001016 s->first_sectors_number=0x40;
bellarda0464332005-12-18 18:29:50 +00001017 /* read only is the default for safety */
1018 bs->read_only = 1;
1019 s->qcow = s->write_target = NULL;
1020 s->qcow_filename = NULL;
1021 s->fat2 = NULL;
1022 s->downcase_short_names = 1;
ths3b46e622007-09-17 08:09:54 +00001023
bellarda0464332005-12-18 18:29:50 +00001024 if (!strstart(dirname, "fat:", NULL))
1025 return -1;
1026
bellarda0464332005-12-18 18:29:50 +00001027 if (strstr(dirname, ":floppy:")) {
1028 floppy = 1;
1029 s->fat_type = 12;
1030 s->first_sectors_number = 1;
1031 s->sectors_per_cluster=2;
1032 bs->cyls = 80; bs->heads = 2; bs->secs = 36;
1033 }
1034
thsb5700942007-09-25 14:47:03 +00001035 s->sector_count=bs->cyls*bs->heads*bs->secs;
1036
bellarda0464332005-12-18 18:29:50 +00001037 if (strstr(dirname, ":32:")) {
1038 fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
1039 s->fat_type = 32;
1040 } else if (strstr(dirname, ":16:")) {
1041 s->fat_type = 16;
1042 } else if (strstr(dirname, ":12:")) {
1043 s->fat_type = 12;
1044 s->sector_count=2880;
1045 }
1046
thsb5700942007-09-25 14:47:03 +00001047 if (strstr(dirname, ":rw:")) {
1048 if (enable_write_target(s))
1049 return -1;
1050 bs->read_only = 0;
1051 }
1052
bellarda0464332005-12-18 18:29:50 +00001053 i = strrchr(dirname, ':') - dirname;
1054 assert(i >= 3);
1055 if (dirname[i-2] == ':' && isalpha(dirname[i-1]))
1056 /* workaround for DOS drive names */
1057 dirname += i-1;
1058 else
1059 dirname += i+1;
1060
1061 bs->total_sectors=bs->cyls*bs->heads*bs->secs;
thsb5700942007-09-25 14:47:03 +00001062
bellarda0464332005-12-18 18:29:50 +00001063 if(init_directories(s, dirname))
bellardde167e42005-04-28 21:15:08 +00001064 return -1;
1065
thsb5700942007-09-25 14:47:03 +00001066 s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
1067
bellardde167e42005-04-28 21:15:08 +00001068 if(s->first_sectors_number==0x40)
1069 init_mbr(s);
1070
bellarda0464332005-12-18 18:29:50 +00001071 /* for some reason or other, MS-DOS does not like to know about CHS... */
1072 if (floppy)
1073 bs->heads = bs->cyls = bs->secs = 0;
bellardde167e42005-04-28 21:15:08 +00001074
bellarda0464332005-12-18 18:29:50 +00001075 // assert(is_consistent(s));
bellardde167e42005-04-28 21:15:08 +00001076 return 0;
1077}
1078
1079static inline void vvfat_close_current_file(BDRVVVFATState *s)
1080{
1081 if(s->current_mapping) {
bellarda0464332005-12-18 18:29:50 +00001082 s->current_mapping = NULL;
1083 if (s->current_fd) {
1084 close(s->current_fd);
1085 s->current_fd = 0;
1086 }
bellardde167e42005-04-28 21:15:08 +00001087 }
bellarda0464332005-12-18 18:29:50 +00001088 s->current_cluster = -1;
bellardde167e42005-04-28 21:15:08 +00001089}
1090
1091/* mappings between index1 and index2-1 are supposed to be ordered
1092 * return value is the index of the last mapping for which end>cluster_num
1093 */
1094static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
1095{
1096 int index3=index1+1;
bellardde167e42005-04-28 21:15:08 +00001097 while(1) {
1098 mapping_t* mapping;
1099 index3=(index1+index2)/2;
1100 mapping=array_get(&(s->mapping),index3);
bellarda0464332005-12-18 18:29:50 +00001101 assert(mapping->begin < mapping->end);
1102 if(mapping->begin>=cluster_num) {
bellardde167e42005-04-28 21:15:08 +00001103 assert(index2!=index3 || index2==0);
1104 if(index2==index3)
bellarda0464332005-12-18 18:29:50 +00001105 return index1;
bellardde167e42005-04-28 21:15:08 +00001106 index2=index3;
1107 } else {
1108 if(index1==index3)
bellarda0464332005-12-18 18:29:50 +00001109 return mapping->end<=cluster_num ? index2 : index1;
bellardde167e42005-04-28 21:15:08 +00001110 index1=index3;
1111 }
1112 assert(index1<=index2);
bellarda0464332005-12-18 18:29:50 +00001113 DLOG(mapping=array_get(&(s->mapping),index1);
1114 assert(mapping->begin<=cluster_num);
ths5fafdf22007-09-16 21:08:06 +00001115 assert(index2 >= s->mapping.next ||
bellarda0464332005-12-18 18:29:50 +00001116 ((mapping = array_get(&(s->mapping),index2)) &&
1117 mapping->end>cluster_num)));
bellardde167e42005-04-28 21:15:08 +00001118 }
1119}
1120
1121static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
1122{
1123 int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
1124 mapping_t* mapping;
1125 if(index>=s->mapping.next)
1126 return 0;
1127 mapping=array_get(&(s->mapping),index);
1128 if(mapping->begin>cluster_num)
1129 return 0;
bellarda0464332005-12-18 18:29:50 +00001130 assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
bellardde167e42005-04-28 21:15:08 +00001131 return mapping;
1132}
1133
bellarda0464332005-12-18 18:29:50 +00001134/*
1135 * This function simply compares path == mapping->path. Since the mappings
1136 * are sorted by cluster, this is expensive: O(n).
1137 */
1138static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s,
1139 const char* path)
1140{
1141 int i;
1142
1143 for (i = 0; i < s->mapping.next; i++) {
1144 mapping_t* mapping = array_get(&(s->mapping), i);
1145 if (mapping->first_mapping_index < 0 &&
1146 !strcmp(path, mapping->path))
1147 return mapping;
1148 }
1149
1150 return NULL;
1151}
1152
1153static int open_file(BDRVVVFATState* s,mapping_t* mapping)
bellardde167e42005-04-28 21:15:08 +00001154{
1155 if(!mapping)
1156 return -1;
bellardde167e42005-04-28 21:15:08 +00001157 if(!s->current_mapping ||
bellarda0464332005-12-18 18:29:50 +00001158 strcmp(s->current_mapping->path,mapping->path)) {
bellardde167e42005-04-28 21:15:08 +00001159 /* open file */
bellarda0464332005-12-18 18:29:50 +00001160 int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
bellardde167e42005-04-28 21:15:08 +00001161 if(fd<0)
1162 return -1;
1163 vvfat_close_current_file(s);
1164 s->current_fd = fd;
bellardde167e42005-04-28 21:15:08 +00001165 s->current_mapping = mapping;
1166 }
1167 return 0;
1168}
1169
1170static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
1171{
1172 if(s->current_cluster != cluster_num) {
1173 int result=0;
1174 off_t offset;
bellarda0464332005-12-18 18:29:50 +00001175 assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
bellardde167e42005-04-28 21:15:08 +00001176 if(!s->current_mapping
1177 || s->current_mapping->begin>cluster_num
1178 || s->current_mapping->end<=cluster_num) {
1179 /* binary search of mappings for file */
1180 mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
bellardde167e42005-04-28 21:15:08 +00001181
bellarda0464332005-12-18 18:29:50 +00001182 assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
1183
1184 if (mapping && mapping->mode & MODE_DIRECTORY) {
1185 vvfat_close_current_file(s);
1186 s->current_mapping = mapping;
1187read_cluster_directory:
1188 offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
thsffe8ab82007-12-16 03:16:05 +00001189 s->cluster = (unsigned char*)s->directory.pointer+offset
bellarda0464332005-12-18 18:29:50 +00001190 + 0x20*s->current_mapping->info.dir.first_dir_index;
1191 assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
1192 assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
1193 s->current_cluster = cluster_num;
1194 return 0;
1195 }
1196
1197 if(open_file(s,mapping))
1198 return -2;
1199 } else if (s->current_mapping->mode & MODE_DIRECTORY)
1200 goto read_cluster_directory;
1201
1202 assert(s->current_fd);
1203
1204 offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
bellardde167e42005-04-28 21:15:08 +00001205 if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
1206 return -3;
bellarda0464332005-12-18 18:29:50 +00001207 s->cluster=s->cluster_buffer;
bellardde167e42005-04-28 21:15:08 +00001208 result=read(s->current_fd,s->cluster,s->cluster_size);
1209 if(result<0) {
1210 s->current_cluster = -1;
1211 return -1;
1212 }
1213 s->current_cluster = cluster_num;
1214 }
1215 return 0;
1216}
1217
bellarda0464332005-12-18 18:29:50 +00001218#ifdef DEBUG
1219static void hexdump(const void* address, uint32_t len)
1220{
1221 const unsigned char* p = address;
1222 int i, j;
1223
1224 for (i = 0; i < len; i += 16) {
1225 for (j = 0; j < 16 && i + j < len; j++)
1226 fprintf(stderr, "%02x ", p[i + j]);
1227 for (; j < 16; j++)
1228 fprintf(stderr, " ");
1229 fprintf(stderr, " ");
1230 for (j = 0; j < 16 && i + j < len; j++)
1231 fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
1232 fprintf(stderr, "\n");
1233 }
1234}
1235
1236static void print_direntry(const direntry_t* direntry)
1237{
1238 int j = 0;
1239 char buffer[1024];
1240
1241 fprintf(stderr, "direntry 0x%x: ", (int)direntry);
1242 if(!direntry)
1243 return;
1244 if(is_long_name(direntry)) {
1245 unsigned char* c=(unsigned char*)direntry;
1246 int i;
1247 for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
1248#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = '°'; j++;}
1249 ADD_CHAR(c[i]);
1250 for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
1251 ADD_CHAR(c[i]);
1252 for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
1253 ADD_CHAR(c[i]);
1254 buffer[j] = 0;
1255 fprintf(stderr, "%s\n", buffer);
1256 } else {
1257 int i;
1258 for(i=0;i<11;i++)
1259 ADD_CHAR(direntry->name[i]);
1260 buffer[j] = 0;
1261 fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
1262 buffer,
1263 direntry->attributes,
1264 begin_of_direntry(direntry),le32_to_cpu(direntry->size));
1265 }
1266}
1267
1268static void print_mapping(const mapping_t* mapping)
1269{
1270 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);
1271 if (mapping->mode & MODE_DIRECTORY)
1272 fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
1273 else
1274 fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
1275}
1276#endif
1277
ths5fafdf22007-09-16 21:08:06 +00001278static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
bellardde167e42005-04-28 21:15:08 +00001279 uint8_t *buf, int nb_sectors)
1280{
1281 BDRVVVFATState *s = bs->opaque;
1282 int i;
1283
bellardde167e42005-04-28 21:15:08 +00001284 for(i=0;i<nb_sectors;i++,sector_num++) {
bellarda0464332005-12-18 18:29:50 +00001285 if (sector_num >= s->sector_count)
1286 return -1;
1287 if (s->qcow) {
1288 int n;
1289 if (s->qcow->drv->bdrv_is_allocated(s->qcow,
1290 sector_num, nb_sectors-i, &n)) {
1291DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
1292 if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
1293 return -1;
1294 i += n - 1;
1295 sector_num += n - 1;
1296 continue;
1297 }
1298DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
1299 }
bellardde167e42005-04-28 21:15:08 +00001300 if(sector_num<s->faked_sectors) {
bellarda0464332005-12-18 18:29:50 +00001301 if(sector_num<s->first_sectors_number)
1302 memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
1303 else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
1304 memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
1305 else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
1306 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 +00001307 } else {
bellarda0464332005-12-18 18:29:50 +00001308 uint32_t sector=sector_num-s->faked_sectors,
1309 sector_offset_in_cluster=(sector%s->sectors_per_cluster),
1310 cluster_num=sector/s->sectors_per_cluster;
1311 if(read_cluster(s, cluster_num) != 0) {
1312 /* LATER TODO: strict: return -1; */
1313 memset(buf+i*0x200,0,0x200);
1314 continue;
1315 }
1316 memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
1317 }
1318 }
1319 return 0;
1320}
1321
1322/* LATER TODO: statify all functions */
1323
1324/*
1325 * Idea of the write support (use snapshot):
1326 *
1327 * 1. check if all data is consistent, recording renames, modifications,
1328 * new files and directories (in s->commits).
1329 *
1330 * 2. if the data is not consistent, stop committing
1331 *
1332 * 3. handle renames, and create new files and directories (do not yet
1333 * write their contents)
1334 *
1335 * 4. walk the directories, fixing the mapping and direntries, and marking
1336 * the handled mappings as not deleted
1337 *
1338 * 5. commit the contents of the files
1339 *
1340 * 6. handle deleted files and directories
1341 *
1342 */
1343
1344typedef struct commit_t {
1345 char* path;
1346 union {
1347 struct { uint32_t cluster; } rename;
1348 struct { int dir_index; uint32_t modified_offset; } writeout;
1349 struct { uint32_t first_cluster; } new_file;
1350 struct { uint32_t cluster; } mkdir;
1351 } param;
1352 /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
1353 enum {
1354 ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
1355 } action;
1356} commit_t;
1357
1358static void clear_commits(BDRVVVFATState* s)
1359{
1360 int i;
1361DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
1362 for (i = 0; i < s->commits.next; i++) {
1363 commit_t* commit = array_get(&(s->commits), i);
1364 assert(commit->path || commit->action == ACTION_WRITEOUT);
1365 if (commit->action != ACTION_WRITEOUT) {
1366 assert(commit->path);
1367 free(commit->path);
1368 } else
1369 assert(commit->path == NULL);
1370 }
1371 s->commits.next = 0;
1372}
1373
1374static void schedule_rename(BDRVVVFATState* s,
1375 uint32_t cluster, char* new_path)
1376{
1377 commit_t* commit = array_get_next(&(s->commits));
1378 commit->path = new_path;
1379 commit->param.rename.cluster = cluster;
1380 commit->action = ACTION_RENAME;
1381}
1382
1383static void schedule_writeout(BDRVVVFATState* s,
1384 int dir_index, uint32_t modified_offset)
1385{
1386 commit_t* commit = array_get_next(&(s->commits));
1387 commit->path = NULL;
1388 commit->param.writeout.dir_index = dir_index;
1389 commit->param.writeout.modified_offset = modified_offset;
1390 commit->action = ACTION_WRITEOUT;
1391}
1392
1393static void schedule_new_file(BDRVVVFATState* s,
1394 char* path, uint32_t first_cluster)
1395{
1396 commit_t* commit = array_get_next(&(s->commits));
1397 commit->path = path;
1398 commit->param.new_file.first_cluster = first_cluster;
1399 commit->action = ACTION_NEW_FILE;
1400}
1401
1402static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
1403{
1404 commit_t* commit = array_get_next(&(s->commits));
1405 commit->path = path;
1406 commit->param.mkdir.cluster = cluster;
1407 commit->action = ACTION_MKDIR;
1408}
1409
1410typedef struct {
ths64eaabd2008-07-03 19:54:19 +00001411 /*
1412 * Since the sequence number is at most 0x3f, and the filename
1413 * length is at most 13 times the sequence number, the maximal
1414 * filename length is 0x3f * 13 bytes.
1415 */
1416 unsigned char name[0x3f * 13 + 1];
bellarda0464332005-12-18 18:29:50 +00001417 int checksum, len;
1418 int sequence_number;
1419} long_file_name;
1420
1421static void lfn_init(long_file_name* lfn)
1422{
1423 lfn->sequence_number = lfn->len = 0;
1424 lfn->checksum = 0x100;
1425}
1426
1427/* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
1428static int parse_long_name(long_file_name* lfn,
1429 const direntry_t* direntry)
1430{
1431 int i, j, offset;
1432 const unsigned char* pointer = (const unsigned char*)direntry;
1433
1434 if (!is_long_name(direntry))
1435 return 1;
1436
1437 if (pointer[0] & 0x40) {
1438 lfn->sequence_number = pointer[0] & 0x3f;
1439 lfn->checksum = pointer[13];
1440 lfn->name[0] = 0;
ths59fdb012008-07-03 19:55:47 +00001441 lfn->name[lfn->sequence_number * 13] = 0;
bellarda0464332005-12-18 18:29:50 +00001442 } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
1443 return -1;
1444 else if (pointer[13] != lfn->checksum)
1445 return -2;
1446 else if (pointer[12] || pointer[26] || pointer[27])
1447 return -3;
1448
1449 offset = 13 * (lfn->sequence_number - 1);
1450 for (i = 0, j = 1; i < 13; i++, j+=2) {
1451 if (j == 11)
1452 j = 14;
1453 else if (j == 26)
1454 j = 28;
1455
1456 if (pointer[j+1] == 0)
1457 lfn->name[offset + i] = pointer[j];
1458 else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
1459 return -4;
1460 else
1461 lfn->name[offset + i] = 0;
1462 }
1463
1464 if (pointer[0] & 0x40)
thsffe8ab82007-12-16 03:16:05 +00001465 lfn->len = offset + strlen((char*)lfn->name + offset);
bellarda0464332005-12-18 18:29:50 +00001466
1467 return 0;
1468}
1469
1470/* returns 0 if successful, >0 if no short_name, and <0 on error */
1471static int parse_short_name(BDRVVVFATState* s,
1472 long_file_name* lfn, direntry_t* direntry)
1473{
1474 int i, j;
1475
1476 if (!is_short_name(direntry))
1477 return 1;
1478
1479 for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
1480 for (i = 0; i <= j; i++) {
1481 if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
1482 return -1;
1483 else if (s->downcase_short_names)
1484 lfn->name[i] = tolower(direntry->name[i]);
1485 else
1486 lfn->name[i] = direntry->name[i];
1487 }
1488
1489 for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
1490 if (j >= 0) {
1491 lfn->name[i++] = '.';
1492 lfn->name[i + j + 1] = '\0';
1493 for (;j >= 0; j--) {
1494 if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
1495 return -2;
1496 else if (s->downcase_short_names)
1497 lfn->name[i + j] = tolower(direntry->extension[j]);
1498 else
1499 lfn->name[i + j] = direntry->extension[j];
1500 }
1501 } else
1502 lfn->name[i + j + 1] = '\0';
1503
thsffe8ab82007-12-16 03:16:05 +00001504 lfn->len = strlen((char*)lfn->name);
bellarda0464332005-12-18 18:29:50 +00001505
1506 return 0;
1507}
1508
1509static inline uint32_t modified_fat_get(BDRVVVFATState* s,
1510 unsigned int cluster)
1511{
1512 if (cluster < s->last_cluster_of_root_directory) {
1513 if (cluster + 1 == s->last_cluster_of_root_directory)
1514 return s->max_fat_value;
1515 else
1516 return cluster + 1;
1517 }
1518
1519 if (s->fat_type==32) {
1520 uint32_t* entry=((uint32_t*)s->fat2)+cluster;
1521 return le32_to_cpu(*entry);
1522 } else if (s->fat_type==16) {
1523 uint16_t* entry=((uint16_t*)s->fat2)+cluster;
1524 return le16_to_cpu(*entry);
1525 } else {
1526 const uint8_t* x=s->fat2+cluster*3/2;
1527 return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
1528 }
1529}
1530
1531static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
1532{
1533 int was_modified = 0;
1534 int i, dummy;
1535
1536 if (s->qcow == NULL)
1537 return 0;
1538
1539 for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
1540 was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
1541 cluster2sector(s, cluster_num) + i, 1, &dummy);
1542
1543 return was_modified;
1544}
1545
1546static const char* get_basename(const char* path)
1547{
1548 char* basename = strrchr(path, '/');
1549 if (basename == NULL)
1550 return path;
1551 else
1552 return basename + 1; /* strip '/' */
1553}
1554
1555/*
1556 * The array s->used_clusters holds the states of the clusters. If it is
1557 * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
1558 * was modified, bit 3 is set.
1559 * If any cluster is allocated, but not part of a file or directory, this
1560 * driver refuses to commit.
1561 */
1562typedef enum {
1563 USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
1564} used_t;
1565
1566/*
1567 * get_cluster_count_for_direntry() not only determines how many clusters
1568 * are occupied by direntry, but also if it was renamed or modified.
1569 *
1570 * A file is thought to be renamed *only* if there already was a file with
1571 * exactly the same first cluster, but a different name.
1572 *
1573 * Further, the files/directories handled by this function are
1574 * assumed to be *not* deleted (and *only* those).
1575 */
1576static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
1577 direntry_t* direntry, const char* path)
1578{
1579 /*
1580 * This is a little bit tricky:
1581 * IF the guest OS just inserts a cluster into the file chain,
1582 * and leaves the rest alone, (i.e. the original file had clusters
1583 * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
1584 *
1585 * - do_commit will write the cluster into the file at the given
1586 * offset, but
1587 *
1588 * - the cluster which is overwritten should be moved to a later
1589 * position in the file.
1590 *
1591 * I am not aware that any OS does something as braindead, but this
1592 * situation could happen anyway when not committing for a long time.
1593 * Just to be sure that this does not bite us, detect it, and copy the
1594 * contents of the clusters to-be-overwritten into the qcow.
1595 */
1596 int copy_it = 0;
1597 int was_modified = 0;
1598 int32_t ret = 0;
1599
1600 uint32_t cluster_num = begin_of_direntry(direntry);
1601 uint32_t offset = 0;
1602 int first_mapping_index = -1;
1603 mapping_t* mapping = NULL;
1604 const char* basename2 = NULL;
1605
1606 vvfat_close_current_file(s);
1607
1608 /* the root directory */
1609 if (cluster_num == 0)
1610 return 0;
1611
1612 /* write support */
1613 if (s->qcow) {
1614 basename2 = get_basename(path);
1615
1616 mapping = find_mapping_for_cluster(s, cluster_num);
1617
1618 if (mapping) {
bellardda2414e2006-04-23 14:36:41 +00001619 const char* basename;
1620
bellarda0464332005-12-18 18:29:50 +00001621 assert(mapping->mode & MODE_DELETED);
1622 mapping->mode &= ~MODE_DELETED;
1623
bellardda2414e2006-04-23 14:36:41 +00001624 basename = get_basename(mapping->path);
bellarda0464332005-12-18 18:29:50 +00001625
1626 assert(mapping->mode & MODE_NORMAL);
1627
1628 /* rename */
1629 if (strcmp(basename, basename2))
1630 schedule_rename(s, cluster_num, strdup(path));
1631 } else if (is_file(direntry))
1632 /* new file */
1633 schedule_new_file(s, strdup(path), cluster_num);
1634 else {
1635 assert(0);
1636 return 0;
1637 }
1638 }
1639
1640 while(1) {
1641 if (s->qcow) {
1642 if (!copy_it && cluster_was_modified(s, cluster_num)) {
1643 if (mapping == NULL ||
1644 mapping->begin > cluster_num ||
1645 mapping->end <= cluster_num)
1646 mapping = find_mapping_for_cluster(s, cluster_num);
1647
1648
1649 if (mapping &&
1650 (mapping->mode & MODE_DIRECTORY) == 0) {
1651
1652 /* was modified in qcow */
1653 if (offset != mapping->info.file.offset + s->cluster_size
1654 * (cluster_num - mapping->begin)) {
1655 /* offset of this cluster in file chain has changed */
1656 assert(0);
1657 copy_it = 1;
1658 } else if (offset == 0) {
1659 const char* basename = get_basename(mapping->path);
1660
1661 if (strcmp(basename, basename2))
1662 copy_it = 1;
1663 first_mapping_index = array_index(&(s->mapping), mapping);
1664 }
1665
1666 if (mapping->first_mapping_index != first_mapping_index
1667 && mapping->info.file.offset > 0) {
1668 assert(0);
1669 copy_it = 1;
1670 }
1671
1672 /* need to write out? */
1673 if (!was_modified && is_file(direntry)) {
1674 was_modified = 1;
1675 schedule_writeout(s, mapping->dir_index, offset);
1676 }
bellardde167e42005-04-28 21:15:08 +00001677 }
bellarda0464332005-12-18 18:29:50 +00001678 }
bellardde167e42005-04-28 21:15:08 +00001679
bellarda0464332005-12-18 18:29:50 +00001680 if (copy_it) {
1681 int i, dummy;
1682 /*
1683 * This is horribly inefficient, but that is okay, since
1684 * it is rarely executed, if at all.
1685 */
1686 int64_t offset = cluster2sector(s, cluster_num);
bellardde167e42005-04-28 21:15:08 +00001687
bellarda0464332005-12-18 18:29:50 +00001688 vvfat_close_current_file(s);
1689 for (i = 0; i < s->sectors_per_cluster; i++)
1690 if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
1691 offset + i, 1, &dummy)) {
1692 if (vvfat_read(s->bs,
1693 offset, s->cluster_buffer, 1))
1694 return -1;
1695 if (s->qcow->drv->bdrv_write(s->qcow,
1696 offset, s->cluster_buffer, 1))
1697 return -2;
1698 }
bellardde167e42005-04-28 21:15:08 +00001699 }
1700 }
bellarda0464332005-12-18 18:29:50 +00001701
1702 ret++;
1703 if (s->used_clusters[cluster_num] & USED_ANY)
1704 return 0;
1705 s->used_clusters[cluster_num] = USED_FILE;
1706
1707 cluster_num = modified_fat_get(s, cluster_num);
1708
1709 if (fat_eof(s, cluster_num))
1710 return ret;
1711 else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
1712 return -1;
1713
1714 offset += s->cluster_size;
bellardde167e42005-04-28 21:15:08 +00001715 }
1716}
1717
bellarda0464332005-12-18 18:29:50 +00001718/*
ths5fafdf22007-09-16 21:08:06 +00001719 * This function looks at the modified data (qcow).
bellarda0464332005-12-18 18:29:50 +00001720 * It returns 0 upon inconsistency or error, and the number of clusters
1721 * used by the directory, its subdirectories and their files.
1722 */
1723static int check_directory_consistency(BDRVVVFATState *s,
1724 int cluster_num, const char* path)
bellardde167e42005-04-28 21:15:08 +00001725{
bellarda0464332005-12-18 18:29:50 +00001726 int ret = 0;
1727 unsigned char* cluster = malloc(s->cluster_size);
1728 direntry_t* direntries = (direntry_t*)cluster;
1729 mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
bellardde167e42005-04-28 21:15:08 +00001730
bellarda0464332005-12-18 18:29:50 +00001731 long_file_name lfn;
1732 int path_len = strlen(path);
1733 char path2[PATH_MAX];
bellardde167e42005-04-28 21:15:08 +00001734
bellarda0464332005-12-18 18:29:50 +00001735 assert(path_len < PATH_MAX); /* len was tested before! */
blueswir1363a37d2008-08-21 17:58:08 +00001736 pstrcpy(path2, sizeof(path2), path);
bellarda0464332005-12-18 18:29:50 +00001737 path2[path_len] = '/';
1738 path2[path_len + 1] = '\0';
bellardde167e42005-04-28 21:15:08 +00001739
bellarda0464332005-12-18 18:29:50 +00001740 if (mapping) {
1741 const char* basename = get_basename(mapping->path);
1742 const char* basename2 = get_basename(path);
1743
1744 assert(mapping->mode & MODE_DIRECTORY);
1745
1746 assert(mapping->mode & MODE_DELETED);
1747 mapping->mode &= ~MODE_DELETED;
1748
1749 if (strcmp(basename, basename2))
1750 schedule_rename(s, cluster_num, strdup(path));
1751 } else
1752 /* new directory */
1753 schedule_mkdir(s, cluster_num, strdup(path));
ths3b46e622007-09-17 08:09:54 +00001754
bellarda0464332005-12-18 18:29:50 +00001755 lfn_init(&lfn);
1756 do {
1757 int i;
1758 int subret = 0;
1759
1760 ret++;
1761
1762 if (s->used_clusters[cluster_num] & USED_ANY) {
1763 fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
1764 return 0;
bellardde167e42005-04-28 21:15:08 +00001765 }
bellarda0464332005-12-18 18:29:50 +00001766 s->used_clusters[cluster_num] = USED_DIRECTORY;
bellardde167e42005-04-28 21:15:08 +00001767
bellarda0464332005-12-18 18:29:50 +00001768DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
1769 subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
1770 s->sectors_per_cluster);
1771 if (subret) {
1772 fprintf(stderr, "Error fetching direntries\n");
1773 fail:
1774 free(cluster);
1775 return 0;
1776 }
1777
1778 for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
1779 int cluster_count;
1780
1781DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i));
1782 if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
1783 is_free(direntries + i))
1784 continue;
1785
1786 subret = parse_long_name(&lfn, direntries + i);
1787 if (subret < 0) {
1788 fprintf(stderr, "Error in long name\n");
1789 goto fail;
bellardde167e42005-04-28 21:15:08 +00001790 }
bellarda0464332005-12-18 18:29:50 +00001791 if (subret == 0 || is_free(direntries + i))
1792 continue;
1793
1794 if (fat_chksum(direntries+i) != lfn.checksum) {
1795 subret = parse_short_name(s, &lfn, direntries + i);
1796 if (subret < 0) {
1797 fprintf(stderr, "Error in short name (%d)\n", subret);
1798 goto fail;
1799 }
thsffe8ab82007-12-16 03:16:05 +00001800 if (subret > 0 || !strcmp((char*)lfn.name, ".")
1801 || !strcmp((char*)lfn.name, ".."))
bellarda0464332005-12-18 18:29:50 +00001802 continue;
1803 }
1804 lfn.checksum = 0x100; /* cannot use long name twice */
1805
1806 if (path_len + 1 + lfn.len >= PATH_MAX) {
1807 fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
1808 goto fail;
1809 }
blueswir1363a37d2008-08-21 17:58:08 +00001810 pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
1811 (char*)lfn.name);
bellarda0464332005-12-18 18:29:50 +00001812
1813 if (is_directory(direntries + i)) {
1814 if (begin_of_direntry(direntries + i) == 0) {
1815 DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
1816 goto fail;
1817 }
1818 cluster_count = check_directory_consistency(s,
1819 begin_of_direntry(direntries + i), path2);
1820 if (cluster_count == 0) {
1821 DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
1822 goto fail;
1823 }
1824 } else if (is_file(direntries + i)) {
1825 /* check file size with FAT */
1826 cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
1827 if (cluster_count !=
1828 (le32_to_cpu(direntries[i].size) + s->cluster_size
1829 - 1) / s->cluster_size) {
1830 DLOG(fprintf(stderr, "Cluster count mismatch\n"));
1831 goto fail;
1832 }
1833 } else
1834 assert(0); /* cluster_count = 0; */
1835
1836 ret += cluster_count;
bellardde167e42005-04-28 21:15:08 +00001837 }
bellarda0464332005-12-18 18:29:50 +00001838
1839 cluster_num = modified_fat_get(s, cluster_num);
1840 } while(!fat_eof(s, cluster_num));
1841
1842 free(cluster);
1843 return ret;
bellardde167e42005-04-28 21:15:08 +00001844}
1845
bellarda0464332005-12-18 18:29:50 +00001846/* returns 1 on success */
1847static int is_consistent(BDRVVVFATState* s)
bellardde167e42005-04-28 21:15:08 +00001848{
bellarda0464332005-12-18 18:29:50 +00001849 int i, check;
1850 int used_clusters_count = 0;
1851
1852DLOG(checkpoint());
1853 /*
1854 * - get modified FAT
1855 * - compare the two FATs (TODO)
1856 * - get buffer for marking used clusters
1857 * - recurse direntries from root (using bs->bdrv_read to make
1858 * sure to get the new data)
1859 * - check that the FAT agrees with the size
1860 * - count the number of clusters occupied by this directory and
1861 * its files
1862 * - check that the cumulative used cluster count agrees with the
1863 * FAT
1864 * - if all is fine, return number of used clusters
1865 */
1866 if (s->fat2 == NULL) {
1867 int size = 0x200 * s->sectors_per_fat;
1868 s->fat2 = malloc(size);
1869 memcpy(s->fat2, s->fat.pointer, size);
1870 }
1871 check = vvfat_read(s->bs,
1872 s->first_sectors_number, s->fat2, s->sectors_per_fat);
1873 if (check) {
1874 fprintf(stderr, "Could not copy fat\n");
bellardde167e42005-04-28 21:15:08 +00001875 return 0;
1876 }
bellarda0464332005-12-18 18:29:50 +00001877 assert (s->used_clusters);
1878 for (i = 0; i < sector2cluster(s, s->sector_count); i++)
1879 s->used_clusters[i] &= ~USED_ANY;
1880
1881 clear_commits(s);
1882
1883 /* mark every mapped file/directory as deleted.
1884 * (check_directory_consistency() will unmark those still present). */
1885 if (s->qcow)
1886 for (i = 0; i < s->mapping.next; i++) {
1887 mapping_t* mapping = array_get(&(s->mapping), i);
1888 if (mapping->first_mapping_index < 0)
1889 mapping->mode |= MODE_DELETED;
1890 }
1891
1892 used_clusters_count = check_directory_consistency(s, 0, s->path);
1893 if (used_clusters_count <= 0) {
1894 DLOG(fprintf(stderr, "problem in directory\n"));
1895 return 0;
1896 }
1897
1898 check = s->last_cluster_of_root_directory;
1899 for (i = check; i < sector2cluster(s, s->sector_count); i++) {
1900 if (modified_fat_get(s, i)) {
1901 if(!s->used_clusters[i]) {
1902 DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
1903 return 0;
1904 }
1905 check++;
1906 }
1907
1908 if (s->used_clusters[i] == USED_ALLOCATED) {
1909 /* allocated, but not used... */
1910 DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
1911 return 0;
1912 }
1913 }
1914
1915 if (check != used_clusters_count)
1916 return 0;
1917
1918 return used_clusters_count;
bellardde167e42005-04-28 21:15:08 +00001919}
1920
bellarda0464332005-12-18 18:29:50 +00001921static inline void adjust_mapping_indices(BDRVVVFATState* s,
1922 int offset, int adjust)
bellardde167e42005-04-28 21:15:08 +00001923{
bellarda0464332005-12-18 18:29:50 +00001924 int i;
1925
1926 for (i = 0; i < s->mapping.next; i++) {
1927 mapping_t* mapping = array_get(&(s->mapping), i);
1928
1929#define ADJUST_MAPPING_INDEX(name) \
1930 if (mapping->name >= offset) \
1931 mapping->name += adjust
1932
1933 ADJUST_MAPPING_INDEX(first_mapping_index);
1934 if (mapping->mode & MODE_DIRECTORY)
1935 ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
1936 }
bellardde167e42005-04-28 21:15:08 +00001937}
1938
bellarda0464332005-12-18 18:29:50 +00001939/* insert or update mapping */
1940static mapping_t* insert_mapping(BDRVVVFATState* s,
1941 uint32_t begin, uint32_t end)
bellardde167e42005-04-28 21:15:08 +00001942{
bellarda0464332005-12-18 18:29:50 +00001943 /*
1944 * - find mapping where mapping->begin >= begin,
1945 * - if mapping->begin > begin: insert
1946 * - adjust all references to mappings!
1947 * - else: adjust
1948 * - replace name
1949 */
1950 int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
1951 mapping_t* mapping = NULL;
1952 mapping_t* first_mapping = array_get(&(s->mapping), 0);
1953
1954 if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
1955 && mapping->begin < begin) {
1956 mapping->end = begin;
1957 index++;
1958 mapping = array_get(&(s->mapping), index);
1959 }
1960 if (index >= s->mapping.next || mapping->begin > begin) {
1961 mapping = array_insert(&(s->mapping), index, 1);
1962 mapping->path = NULL;
1963 adjust_mapping_indices(s, index, +1);
1964 }
1965
bellardde167e42005-04-28 21:15:08 +00001966 mapping->begin = begin;
bellarda0464332005-12-18 18:29:50 +00001967 mapping->end = end;
1968
1969DLOG(mapping_t* next_mapping;
1970assert(index + 1 >= s->mapping.next ||
1971((next_mapping = array_get(&(s->mapping), index + 1)) &&
1972 next_mapping->begin >= end)));
1973
1974 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1975 s->current_mapping = array_get(&(s->mapping),
1976 s->current_mapping - first_mapping);
1977
1978 return mapping;
bellardde167e42005-04-28 21:15:08 +00001979}
1980
bellarda0464332005-12-18 18:29:50 +00001981static int remove_mapping(BDRVVVFATState* s, int mapping_index)
bellardde167e42005-04-28 21:15:08 +00001982{
bellarda0464332005-12-18 18:29:50 +00001983 mapping_t* mapping = array_get(&(s->mapping), mapping_index);
1984 mapping_t* first_mapping = array_get(&(s->mapping), 0);
bellardde167e42005-04-28 21:15:08 +00001985
bellarda0464332005-12-18 18:29:50 +00001986 /* free mapping */
1987 if (mapping->first_mapping_index < 0)
1988 free(mapping->path);
bellardde167e42005-04-28 21:15:08 +00001989
bellarda0464332005-12-18 18:29:50 +00001990 /* remove from s->mapping */
1991 array_remove(&(s->mapping), mapping_index);
bellardde167e42005-04-28 21:15:08 +00001992
bellarda0464332005-12-18 18:29:50 +00001993 /* adjust all references to mappings */
1994 adjust_mapping_indices(s, mapping_index, -1);
bellardde167e42005-04-28 21:15:08 +00001995
bellarda0464332005-12-18 18:29:50 +00001996 if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
1997 s->current_mapping = array_get(&(s->mapping),
1998 s->current_mapping - first_mapping);
bellardde167e42005-04-28 21:15:08 +00001999
2000 return 0;
2001}
2002
bellarda0464332005-12-18 18:29:50 +00002003static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
bellardde167e42005-04-28 21:15:08 +00002004{
bellarda0464332005-12-18 18:29:50 +00002005 int i;
2006 for (i = 0; i < s->mapping.next; i++) {
2007 mapping_t* mapping = array_get(&(s->mapping), i);
2008 if (mapping->dir_index >= offset)
2009 mapping->dir_index += adjust;
2010 if ((mapping->mode & MODE_DIRECTORY) &&
2011 mapping->info.dir.first_dir_index >= offset)
2012 mapping->info.dir.first_dir_index += adjust;
bellardde167e42005-04-28 21:15:08 +00002013 }
bellardde167e42005-04-28 21:15:08 +00002014}
2015
bellarda0464332005-12-18 18:29:50 +00002016static direntry_t* insert_direntries(BDRVVVFATState* s,
2017 int dir_index, int count)
bellardde167e42005-04-28 21:15:08 +00002018{
bellarda0464332005-12-18 18:29:50 +00002019 /*
2020 * make room in s->directory,
2021 * adjust_dirindices
2022 */
2023 direntry_t* result = array_insert(&(s->directory), dir_index, count);
2024 if (result == NULL)
2025 return NULL;
2026 adjust_dirindices(s, dir_index, count);
bellardde167e42005-04-28 21:15:08 +00002027 return result;
2028}
2029
bellarda0464332005-12-18 18:29:50 +00002030static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
bellardde167e42005-04-28 21:15:08 +00002031{
bellarda0464332005-12-18 18:29:50 +00002032 int ret = array_remove_slice(&(s->directory), dir_index, count);
2033 if (ret)
2034 return ret;
2035 adjust_dirindices(s, dir_index, -count);
bellardde167e42005-04-28 21:15:08 +00002036 return 0;
2037}
2038
bellarda0464332005-12-18 18:29:50 +00002039/*
2040 * Adapt the mappings of the cluster chain starting at first cluster
2041 * (i.e. if a file starts at first_cluster, the chain is followed according
2042 * to the modified fat, and the corresponding entries in s->mapping are
2043 * adjusted)
2044 */
2045static int commit_mappings(BDRVVVFATState* s,
2046 uint32_t first_cluster, int dir_index)
bellardde167e42005-04-28 21:15:08 +00002047{
bellarda0464332005-12-18 18:29:50 +00002048 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2049 direntry_t* direntry = array_get(&(s->directory), dir_index);
2050 uint32_t cluster = first_cluster;
bellardde167e42005-04-28 21:15:08 +00002051
bellarda0464332005-12-18 18:29:50 +00002052 vvfat_close_current_file(s);
bellardde167e42005-04-28 21:15:08 +00002053
bellarda0464332005-12-18 18:29:50 +00002054 assert(mapping);
2055 assert(mapping->begin == first_cluster);
2056 mapping->first_mapping_index = -1;
2057 mapping->dir_index = dir_index;
2058 mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
2059 MODE_DIRECTORY : MODE_NORMAL;
bellardde167e42005-04-28 21:15:08 +00002060
bellarda0464332005-12-18 18:29:50 +00002061 while (!fat_eof(s, cluster)) {
2062 uint32_t c, c1;
bellardde167e42005-04-28 21:15:08 +00002063
bellarda0464332005-12-18 18:29:50 +00002064 for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
2065 c = c1, c1 = modified_fat_get(s, c1));
bellardde167e42005-04-28 21:15:08 +00002066
bellarda0464332005-12-18 18:29:50 +00002067 c++;
2068 if (c > mapping->end) {
2069 int index = array_index(&(s->mapping), mapping);
2070 int i, max_i = s->mapping.next - index;
2071 for (i = 1; i < max_i && mapping[i].begin < c; i++);
2072 while (--i > 0)
2073 remove_mapping(s, index + 1);
2074 }
2075 assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
2076 || mapping[1].begin >= c);
2077 mapping->end = c;
2078
2079 if (!fat_eof(s, c1)) {
2080 int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
2081 mapping_t* next_mapping = i >= s->mapping.next ? NULL :
2082 array_get(&(s->mapping), i);
2083
2084 if (next_mapping == NULL || next_mapping->begin > c1) {
2085 int i1 = array_index(&(s->mapping), mapping);
2086
2087 next_mapping = insert_mapping(s, c1, c1+1);
2088
2089 if (c1 < c)
2090 i1++;
2091 mapping = array_get(&(s->mapping), i1);
bellardde167e42005-04-28 21:15:08 +00002092 }
bellarda0464332005-12-18 18:29:50 +00002093
2094 next_mapping->dir_index = mapping->dir_index;
ths5fafdf22007-09-16 21:08:06 +00002095 next_mapping->first_mapping_index =
bellarda0464332005-12-18 18:29:50 +00002096 mapping->first_mapping_index < 0 ?
2097 array_index(&(s->mapping), mapping) :
2098 mapping->first_mapping_index;
2099 next_mapping->path = mapping->path;
2100 next_mapping->mode = mapping->mode;
2101 next_mapping->read_only = mapping->read_only;
2102 if (mapping->mode & MODE_DIRECTORY) {
2103 next_mapping->info.dir.parent_mapping_index =
2104 mapping->info.dir.parent_mapping_index;
2105 next_mapping->info.dir.first_dir_index =
2106 mapping->info.dir.first_dir_index +
2107 0x10 * s->sectors_per_cluster *
2108 (mapping->end - mapping->begin);
2109 } else
2110 next_mapping->info.file.offset = mapping->info.file.offset +
2111 mapping->end - mapping->begin;
2112
2113 mapping = next_mapping;
2114 }
ths3b46e622007-09-17 08:09:54 +00002115
bellarda0464332005-12-18 18:29:50 +00002116 cluster = c1;
2117 }
2118
2119 return 0;
2120}
2121
2122static int commit_direntries(BDRVVVFATState* s,
2123 int dir_index, int parent_mapping_index)
2124{
2125 direntry_t* direntry = array_get(&(s->directory), dir_index);
2126 uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
2127 mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2128
2129 int factor = 0x10 * s->sectors_per_cluster;
2130 int old_cluster_count, new_cluster_count;
2131 int current_dir_index = mapping->info.dir.first_dir_index;
2132 int first_dir_index = current_dir_index;
2133 int ret, i;
2134 uint32_t c;
2135
2136DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
2137
2138 assert(direntry);
2139 assert(mapping);
2140 assert(mapping->begin == first_cluster);
2141 assert(mapping->info.dir.first_dir_index < s->directory.next);
2142 assert(mapping->mode & MODE_DIRECTORY);
2143 assert(dir_index == 0 || is_directory(direntry));
2144
2145 mapping->info.dir.parent_mapping_index = parent_mapping_index;
2146
2147 if (first_cluster == 0) {
2148 old_cluster_count = new_cluster_count =
2149 s->last_cluster_of_root_directory;
2150 } else {
2151 for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2152 c = fat_get(s, c))
2153 old_cluster_count++;
2154
2155 for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2156 c = modified_fat_get(s, c))
2157 new_cluster_count++;
2158 }
2159
2160 if (new_cluster_count > old_cluster_count) {
2161 if (insert_direntries(s,
2162 current_dir_index + factor * old_cluster_count,
2163 factor * (new_cluster_count - old_cluster_count)) == NULL)
2164 return -1;
2165 } else if (new_cluster_count < old_cluster_count)
2166 remove_direntries(s,
2167 current_dir_index + factor * new_cluster_count,
2168 factor * (old_cluster_count - new_cluster_count));
2169
2170 for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
2171 void* direntry = array_get(&(s->directory), current_dir_index);
2172 int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
2173 s->sectors_per_cluster);
2174 if (ret)
2175 return ret;
2176 assert(!strncmp(s->directory.pointer, "QEMU", 4));
2177 current_dir_index += factor;
2178 }
2179
2180 ret = commit_mappings(s, first_cluster, dir_index);
2181 if (ret)
2182 return ret;
2183
2184 /* recurse */
2185 for (i = 0; i < factor * new_cluster_count; i++) {
2186 direntry = array_get(&(s->directory), first_dir_index + i);
2187 if (is_directory(direntry) && !is_dot(direntry)) {
2188 mapping = find_mapping_for_cluster(s, first_cluster);
2189 assert(mapping->mode & MODE_DIRECTORY);
2190 ret = commit_direntries(s, first_dir_index + i,
2191 array_index(&(s->mapping), mapping));
2192 if (ret)
2193 return ret;
bellardde167e42005-04-28 21:15:08 +00002194 }
2195 }
bellarda0464332005-12-18 18:29:50 +00002196
bellardde167e42005-04-28 21:15:08 +00002197 return 0;
2198}
2199
bellarda0464332005-12-18 18:29:50 +00002200/* commit one file (adjust contents, adjust mapping),
2201 return first_mapping_index */
2202static int commit_one_file(BDRVVVFATState* s,
2203 int dir_index, uint32_t offset)
2204{
2205 direntry_t* direntry = array_get(&(s->directory), dir_index);
2206 uint32_t c = begin_of_direntry(direntry);
2207 uint32_t first_cluster = c;
2208 mapping_t* mapping = find_mapping_for_cluster(s, c);
2209 uint32_t size = filesize_of_direntry(direntry);
2210 char* cluster = malloc(s->cluster_size);
2211 uint32_t i;
2212 int fd = 0;
2213
2214 assert(offset < size);
2215 assert((offset % s->cluster_size) == 0);
2216
2217 for (i = s->cluster_size; i < offset; i += s->cluster_size)
2218 c = modified_fat_get(s, c);
2219
bellard6bcb76c2006-09-09 12:03:20 +00002220 fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
bellarda0464332005-12-18 18:29:50 +00002221 if (fd < 0) {
2222 fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
2223 strerror(errno), errno);
2224 return fd;
2225 }
2226 if (offset > 0)
2227 if (lseek(fd, offset, SEEK_SET) != offset)
2228 return -3;
2229
2230 while (offset < size) {
2231 uint32_t c1;
2232 int rest_size = (size - offset > s->cluster_size ?
2233 s->cluster_size : size - offset);
2234 int ret;
2235
2236 c1 = modified_fat_get(s, c);
2237
2238 assert((size - offset == 0 && fat_eof(s, c)) ||
2239 (size > offset && c >=2 && !fat_eof(s, c)));
bellarda0464332005-12-18 18:29:50 +00002240
2241 ret = vvfat_read(s->bs, cluster2sector(s, c),
thsffe8ab82007-12-16 03:16:05 +00002242 (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
bellarda0464332005-12-18 18:29:50 +00002243
2244 if (ret < 0)
2245 return ret;
2246
2247 if (write(fd, cluster, rest_size) < 0)
2248 return -2;
2249
2250 offset += rest_size;
2251 c = c1;
2252 }
2253
2254 ftruncate(fd, size);
2255 close(fd);
2256
2257 return commit_mappings(s, first_cluster, dir_index);
2258}
2259
2260#ifdef DEBUG
2261/* test, if all mappings point to valid direntries */
2262static void check1(BDRVVVFATState* s)
2263{
2264 int i;
2265 for (i = 0; i < s->mapping.next; i++) {
2266 mapping_t* mapping = array_get(&(s->mapping), i);
2267 if (mapping->mode & MODE_DELETED) {
2268 fprintf(stderr, "deleted\n");
2269 continue;
2270 }
2271 assert(mapping->dir_index >= 0);
2272 assert(mapping->dir_index < s->directory.next);
2273 direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
2274 assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
2275 if (mapping->mode & MODE_DIRECTORY) {
2276 assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
2277 assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
2278 }
2279 }
2280}
2281
2282/* test, if all direntries have mappings */
2283static void check2(BDRVVVFATState* s)
2284{
2285 int i;
2286 int first_mapping = -1;
2287
2288 for (i = 0; i < s->directory.next; i++) {
2289 direntry_t* direntry = array_get(&(s->directory), i);
2290
2291 if (is_short_name(direntry) && begin_of_direntry(direntry)) {
2292 mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
2293 assert(mapping);
2294 assert(mapping->dir_index == i || is_dot(direntry));
2295 assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
2296 }
2297
2298 if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
2299 /* cluster start */
2300 int j, count = 0;
2301
2302 for (j = 0; j < s->mapping.next; j++) {
2303 mapping_t* mapping = array_get(&(s->mapping), j);
2304 if (mapping->mode & MODE_DELETED)
2305 continue;
2306 if (mapping->mode & MODE_DIRECTORY) {
2307 if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
2308 assert(++count == 1);
2309 if (mapping->first_mapping_index == -1)
2310 first_mapping = array_index(&(s->mapping), mapping);
2311 else
2312 assert(first_mapping == mapping->first_mapping_index);
2313 if (mapping->info.dir.parent_mapping_index < 0)
2314 assert(j == 0);
2315 else {
2316 mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
2317 assert(parent->mode & MODE_DIRECTORY);
2318 assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
2319 }
2320 }
2321 }
2322 }
2323 if (count == 0)
2324 first_mapping = -1;
2325 }
2326 }
2327}
2328#endif
2329
2330static int handle_renames_and_mkdirs(BDRVVVFATState* s)
2331{
2332 int i;
2333
2334#ifdef DEBUG
2335 fprintf(stderr, "handle_renames\n");
2336 for (i = 0; i < s->commits.next; i++) {
2337 commit_t* commit = array_get(&(s->commits), i);
2338 fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
2339 }
2340#endif
2341
2342 for (i = 0; i < s->commits.next;) {
2343 commit_t* commit = array_get(&(s->commits), i);
2344 if (commit->action == ACTION_RENAME) {
2345 mapping_t* mapping = find_mapping_for_cluster(s,
2346 commit->param.rename.cluster);
2347 char* old_path = mapping->path;
2348
2349 assert(commit->path);
2350 mapping->path = commit->path;
2351 if (rename(old_path, mapping->path))
2352 return -2;
2353
2354 if (mapping->mode & MODE_DIRECTORY) {
2355 int l1 = strlen(mapping->path);
2356 int l2 = strlen(old_path);
2357 int diff = l1 - l2;
2358 direntry_t* direntry = array_get(&(s->directory),
2359 mapping->info.dir.first_dir_index);
2360 uint32_t c = mapping->begin;
2361 int i = 0;
2362
2363 /* recurse */
2364 while (!fat_eof(s, c)) {
2365 do {
2366 direntry_t* d = direntry + i;
2367
2368 if (is_file(d) || (is_directory(d) && !is_dot(d))) {
2369 mapping_t* m = find_mapping_for_cluster(s,
2370 begin_of_direntry(d));
2371 int l = strlen(m->path);
2372 char* new_path = malloc(l + diff + 1);
2373
2374 assert(!strncmp(m->path, mapping->path, l2));
2375
blueswir1363a37d2008-08-21 17:58:08 +00002376 pstrcpy(new_path, l + diff + 1, mapping->path);
2377 pstrcpy(new_path + l1, l + diff + 1 - l1,
2378 m->path + l2);
bellarda0464332005-12-18 18:29:50 +00002379
2380 schedule_rename(s, m->begin, new_path);
2381 }
2382 i++;
2383 } while((i % (0x10 * s->sectors_per_cluster)) != 0);
2384 c = fat_get(s, c);
2385 }
2386 }
2387
2388 free(old_path);
2389 array_remove(&(s->commits), i);
2390 continue;
2391 } else if (commit->action == ACTION_MKDIR) {
2392 mapping_t* mapping;
2393 int j, parent_path_len;
2394
bellard48c2f062005-12-19 22:11:49 +00002395#ifdef __MINGW32__
2396 if (mkdir(commit->path))
2397 return -5;
2398#else
2399 if (mkdir(commit->path, 0755))
2400 return -5;
2401#endif
bellarda0464332005-12-18 18:29:50 +00002402
2403 mapping = insert_mapping(s, commit->param.mkdir.cluster,
2404 commit->param.mkdir.cluster + 1);
2405 if (mapping == NULL)
2406 return -6;
2407
2408 mapping->mode = MODE_DIRECTORY;
2409 mapping->read_only = 0;
2410 mapping->path = commit->path;
2411 j = s->directory.next;
2412 assert(j);
2413 insert_direntries(s, s->directory.next,
2414 0x10 * s->sectors_per_cluster);
2415 mapping->info.dir.first_dir_index = j;
2416
2417 parent_path_len = strlen(commit->path)
2418 - strlen(get_basename(commit->path)) - 1;
2419 for (j = 0; j < s->mapping.next; j++) {
2420 mapping_t* m = array_get(&(s->mapping), j);
2421 if (m->first_mapping_index < 0 && m != mapping &&
2422 !strncmp(m->path, mapping->path, parent_path_len) &&
2423 strlen(m->path) == parent_path_len)
2424 break;
2425 }
2426 assert(j < s->mapping.next);
2427 mapping->info.dir.parent_mapping_index = j;
2428
2429 array_remove(&(s->commits), i);
2430 continue;
2431 }
2432
2433 i++;
2434 }
2435 return 0;
2436}
2437
2438/*
2439 * TODO: make sure that the short name is not matching *another* file
2440 */
2441static int handle_commits(BDRVVVFATState* s)
2442{
2443 int i, fail = 0;
2444
2445 vvfat_close_current_file(s);
2446
2447 for (i = 0; !fail && i < s->commits.next; i++) {
2448 commit_t* commit = array_get(&(s->commits), i);
2449 switch(commit->action) {
2450 case ACTION_RENAME: case ACTION_MKDIR:
2451 assert(0);
2452 fail = -2;
2453 break;
2454 case ACTION_WRITEOUT: {
2455 direntry_t* entry = array_get(&(s->directory),
2456 commit->param.writeout.dir_index);
2457 uint32_t begin = begin_of_direntry(entry);
2458 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2459
2460 assert(mapping);
2461 assert(mapping->begin == begin);
2462 assert(commit->path == NULL);
2463
2464 if (commit_one_file(s, commit->param.writeout.dir_index,
2465 commit->param.writeout.modified_offset))
2466 fail = -3;
2467
2468 break;
2469 }
2470 case ACTION_NEW_FILE: {
2471 int begin = commit->param.new_file.first_cluster;
2472 mapping_t* mapping = find_mapping_for_cluster(s, begin);
2473 direntry_t* entry;
2474 int i;
2475
2476 /* find direntry */
2477 for (i = 0; i < s->directory.next; i++) {
2478 entry = array_get(&(s->directory), i);
2479 if (is_file(entry) && begin_of_direntry(entry) == begin)
2480 break;
2481 }
2482
2483 if (i >= s->directory.next) {
2484 fail = -6;
2485 continue;
2486 }
2487
2488 /* make sure there exists an initial mapping */
2489 if (mapping && mapping->begin != begin) {
2490 mapping->end = begin;
2491 mapping = NULL;
2492 }
2493 if (mapping == NULL) {
2494 mapping = insert_mapping(s, begin, begin+1);
2495 }
2496 /* most members will be fixed in commit_mappings() */
2497 assert(commit->path);
2498 mapping->path = commit->path;
2499 mapping->read_only = 0;
2500 mapping->mode = MODE_NORMAL;
2501 mapping->info.file.offset = 0;
2502
2503 if (commit_one_file(s, i, 0))
2504 fail = -7;
2505
2506 break;
2507 }
2508 default:
2509 assert(0);
2510 }
2511 }
2512 if (i > 0 && array_remove_slice(&(s->commits), 0, i))
2513 return -1;
2514 return fail;
2515}
2516
2517static int handle_deletes(BDRVVVFATState* s)
2518{
2519 int i, deferred = 1, deleted = 1;
2520
2521 /* delete files corresponding to mappings marked as deleted */
2522 /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
2523 while (deferred && deleted) {
2524 deferred = 0;
2525 deleted = 0;
2526
2527 for (i = 1; i < s->mapping.next; i++) {
2528 mapping_t* mapping = array_get(&(s->mapping), i);
2529 if (mapping->mode & MODE_DELETED) {
2530 direntry_t* entry = array_get(&(s->directory),
2531 mapping->dir_index);
2532
2533 if (is_free(entry)) {
2534 /* remove file/directory */
2535 if (mapping->mode & MODE_DIRECTORY) {
2536 int j, next_dir_index = s->directory.next,
2537 first_dir_index = mapping->info.dir.first_dir_index;
2538
2539 if (rmdir(mapping->path) < 0) {
2540 if (errno == ENOTEMPTY) {
2541 deferred++;
2542 continue;
2543 } else
2544 return -5;
2545 }
2546
2547 for (j = 1; j < s->mapping.next; j++) {
2548 mapping_t* m = array_get(&(s->mapping), j);
2549 if (m->mode & MODE_DIRECTORY &&
2550 m->info.dir.first_dir_index >
2551 first_dir_index &&
2552 m->info.dir.first_dir_index <
2553 next_dir_index)
2554 next_dir_index =
2555 m->info.dir.first_dir_index;
2556 }
2557 remove_direntries(s, first_dir_index,
2558 next_dir_index - first_dir_index);
2559
2560 deleted++;
2561 }
2562 } else {
2563 if (unlink(mapping->path))
2564 return -4;
2565 deleted++;
2566 }
2567 DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
2568 remove_mapping(s, i);
2569 }
2570 }
2571 }
2572
2573 return 0;
2574}
2575
2576/*
2577 * synchronize mapping with new state:
2578 *
2579 * - copy FAT (with bdrv_read)
2580 * - mark all filenames corresponding to mappings as deleted
2581 * - recurse direntries from root (using bs->bdrv_read)
2582 * - delete files corresponding to mappings marked as deleted
2583 */
2584static int do_commit(BDRVVVFATState* s)
2585{
2586 int ret = 0;
2587
2588 /* the real meat are the commits. Nothing to do? Move along! */
2589 if (s->commits.next == 0)
2590 return 0;
2591
2592 vvfat_close_current_file(s);
2593
2594 ret = handle_renames_and_mkdirs(s);
2595 if (ret) {
2596 fprintf(stderr, "Error handling renames (%d)\n", ret);
2597 assert(0);
2598 return ret;
2599 }
2600
ths5fafdf22007-09-16 21:08:06 +00002601 /* copy FAT (with bdrv_read) */
bellarda0464332005-12-18 18:29:50 +00002602 memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
2603
2604 /* recurse direntries from root (using bs->bdrv_read) */
2605 ret = commit_direntries(s, 0, -1);
2606 if (ret) {
2607 fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
2608 assert(0);
2609 return ret;
2610 }
2611
2612 ret = handle_commits(s);
2613 if (ret) {
2614 fprintf(stderr, "Error handling commits (%d)\n", ret);
2615 assert(0);
2616 return ret;
2617 }
2618
2619 ret = handle_deletes(s);
2620 if (ret) {
2621 fprintf(stderr, "Error deleting\n");
2622 assert(0);
2623 return ret;
2624 }
2625
2626 s->qcow->drv->bdrv_make_empty(s->qcow);
2627
2628 memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
2629
2630DLOG(checkpoint());
2631 return 0;
2632}
2633
2634static int try_commit(BDRVVVFATState* s)
2635{
2636 vvfat_close_current_file(s);
2637DLOG(checkpoint());
2638 if(!is_consistent(s))
2639 return -1;
2640 return do_commit(s);
2641}
2642
ths5fafdf22007-09-16 21:08:06 +00002643static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
bellardde167e42005-04-28 21:15:08 +00002644 const uint8_t *buf, int nb_sectors)
2645{
ths5fafdf22007-09-16 21:08:06 +00002646 BDRVVVFATState *s = bs->opaque;
bellarda0464332005-12-18 18:29:50 +00002647 int i, ret;
bellardde167e42005-04-28 21:15:08 +00002648
bellarda0464332005-12-18 18:29:50 +00002649DLOG(checkpoint());
bellardde167e42005-04-28 21:15:08 +00002650
bellarda0464332005-12-18 18:29:50 +00002651 vvfat_close_current_file(s);
bellardde167e42005-04-28 21:15:08 +00002652
bellarda0464332005-12-18 18:29:50 +00002653 /*
2654 * Some sanity checks:
2655 * - do not allow writing to the boot sector
2656 * - do not allow to write non-ASCII filenames
2657 */
bellardde167e42005-04-28 21:15:08 +00002658
bellarda0464332005-12-18 18:29:50 +00002659 if (sector_num < s->first_sectors_number)
2660 return -1;
bellardde167e42005-04-28 21:15:08 +00002661
bellarda0464332005-12-18 18:29:50 +00002662 for (i = sector2cluster(s, sector_num);
2663 i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
2664 mapping_t* mapping = find_mapping_for_cluster(s, i);
2665 if (mapping) {
2666 if (mapping->read_only) {
2667 fprintf(stderr, "Tried to write to write-protected file %s\n",
2668 mapping->path);
2669 return -1;
2670 }
bellardde167e42005-04-28 21:15:08 +00002671
bellarda0464332005-12-18 18:29:50 +00002672 if (mapping->mode & MODE_DIRECTORY) {
2673 int begin = cluster2sector(s, i);
2674 int end = begin + s->sectors_per_cluster, k;
2675 int dir_index;
2676 const direntry_t* direntries;
2677 long_file_name lfn;
bellardde167e42005-04-28 21:15:08 +00002678
bellarda0464332005-12-18 18:29:50 +00002679 lfn_init(&lfn);
bellardde167e42005-04-28 21:15:08 +00002680
bellarda0464332005-12-18 18:29:50 +00002681 if (begin < sector_num)
2682 begin = sector_num;
2683 if (end > sector_num + nb_sectors)
2684 end = sector_num + nb_sectors;
ths5fafdf22007-09-16 21:08:06 +00002685 dir_index = mapping->dir_index +
bellarda0464332005-12-18 18:29:50 +00002686 0x10 * (begin - mapping->begin * s->sectors_per_cluster);
2687 direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
bellardde167e42005-04-28 21:15:08 +00002688
bellarda0464332005-12-18 18:29:50 +00002689 for (k = 0; k < (end - begin) * 0x10; k++) {
2690 /* do not allow non-ASCII filenames */
2691 if (parse_long_name(&lfn, direntries + k) < 0) {
2692 fprintf(stderr, "Warning: non-ASCII filename\n");
2693 return -1;
bellardde167e42005-04-28 21:15:08 +00002694 }
bellarda0464332005-12-18 18:29:50 +00002695 /* no access to the direntry of a read-only file */
2696 else if (is_short_name(direntries+k) &&
2697 (direntries[k].attributes & 1)) {
2698 if (memcmp(direntries + k,
2699 array_get(&(s->directory), dir_index + k),
2700 sizeof(direntry_t))) {
2701 fprintf(stderr, "Warning: tried to write to write-protected file\n");
2702 return -1;
bellardde167e42005-04-28 21:15:08 +00002703 }
bellardde167e42005-04-28 21:15:08 +00002704 }
2705 }
2706 }
bellarda0464332005-12-18 18:29:50 +00002707 i = mapping->end;
2708 } else
2709 i++;
bellardde167e42005-04-28 21:15:08 +00002710 }
bellarda0464332005-12-18 18:29:50 +00002711
2712 /*
2713 * Use qcow backend. Commit later.
2714 */
2715DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
2716 ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
2717 if (ret < 0) {
2718 fprintf(stderr, "Error writing to qcow backend\n");
2719 return ret;
2720 }
2721
2722 for (i = sector2cluster(s, sector_num);
2723 i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
2724 if (i >= 0)
2725 s->used_clusters[i] |= USED_ALLOCATED;
2726
2727DLOG(checkpoint());
2728 /* TODO: add timeout */
2729 try_commit(s);
2730
2731DLOG(checkpoint());
2732 return 0;
2733}
2734
2735static int vvfat_is_allocated(BlockDriverState *bs,
2736 int64_t sector_num, int nb_sectors, int* n)
2737{
2738 BDRVVVFATState* s = bs->opaque;
2739 *n = s->sector_count - sector_num;
2740 if (*n > nb_sectors)
2741 *n = nb_sectors;
2742 else if (*n < 0)
2743 return 0;
ths5fafdf22007-09-16 21:08:06 +00002744 return 1;
bellarda0464332005-12-18 18:29:50 +00002745}
2746
2747static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
2748 const uint8_t* buffer, int nb_sectors) {
2749 BDRVVVFATState* s = bs->opaque;
2750 return try_commit(s);
2751}
2752
2753static void write_target_close(BlockDriverState *bs) {
2754 BDRVVVFATState* s = bs->opaque;
2755 bdrv_delete(s->qcow);
2756 free(s->qcow_filename);
2757}
2758
2759static BlockDriver vvfat_write_target = {
2760 "vvfat_write_target", 0, NULL, NULL, NULL,
2761 write_target_commit,
2762 write_target_close,
2763 NULL, NULL, NULL
2764};
2765
2766static int enable_write_target(BDRVVVFATState *s)
2767{
2768 int size = sector2cluster(s, s->sector_count);
2769 s->used_clusters = calloc(size, 1);
2770
2771 array_init(&(s->commits), sizeof(commit_t));
2772
2773 s->qcow_filename = malloc(1024);
bellard83f64092006-08-01 16:21:11 +00002774 get_tmp_filename(s->qcow_filename, 1024);
bellarda0464332005-12-18 18:29:50 +00002775 if (bdrv_create(&bdrv_qcow,
2776 s->qcow_filename, s->sector_count, "fat:", 0) < 0)
2777 return -1;
2778 s->qcow = bdrv_new("");
2779 if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0)
2780 return -1;
2781
2782#ifndef _WIN32
2783 unlink(s->qcow_filename);
2784#endif
2785
2786 s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
2787 s->bs->backing_hd->drv = &vvfat_write_target;
2788 s->bs->backing_hd->opaque = s;
2789
bellardde167e42005-04-28 21:15:08 +00002790 return 0;
2791}
2792
2793static void vvfat_close(BlockDriverState *bs)
2794{
2795 BDRVVVFATState *s = bs->opaque;
2796
2797 vvfat_close_current_file(s);
2798 array_free(&(s->fat));
2799 array_free(&(s->directory));
2800 array_free(&(s->mapping));
bellarda0464332005-12-18 18:29:50 +00002801 if(s->cluster_buffer)
2802 free(s->cluster_buffer);
bellardde167e42005-04-28 21:15:08 +00002803}
2804
2805BlockDriver bdrv_vvfat = {
2806 "vvfat",
2807 sizeof(BDRVVVFATState),
bellard83f64092006-08-01 16:21:11 +00002808 NULL, /* no probe for protocols */
bellardde167e42005-04-28 21:15:08 +00002809 vvfat_open,
2810 vvfat_read,
2811 vvfat_write,
2812 vvfat_close,
pbrook7a6cba62006-06-04 11:39:07 +00002813 NULL, /* ??? Not sure if we can do any meaningful flushing. */
bellarda0464332005-12-18 18:29:50 +00002814 NULL,
bellard83f64092006-08-01 16:21:11 +00002815 vvfat_is_allocated,
2816 .protocol_name = "fat",
bellardde167e42005-04-28 21:15:08 +00002817};
2818
bellarda0464332005-12-18 18:29:50 +00002819#ifdef DEBUG
blueswir13f47aa82008-03-09 06:59:01 +00002820static void checkpoint(void) {
bellarda0464332005-12-18 18:29:50 +00002821 assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
2822 check1(vvv);
2823 check2(vvv);
2824 assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
2825#if 0
2826 if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
2827 fprintf(stderr, "Nonono!\n");
2828 mapping_t* mapping;
2829 direntry_t* direntry;
2830 assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
2831 assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
2832 if (vvv->mapping.next<47)
2833 return;
2834 assert((mapping = array_get(&(vvv->mapping), 47)));
2835 assert(mapping->dir_index < vvv->directory.next);
2836 direntry = array_get(&(vvv->directory), mapping->dir_index);
2837 assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0);
2838#endif
2839 return;
2840 /* avoid compiler warnings: */
2841 hexdump(NULL, 100);
2842 remove_mapping(vvv, NULL);
2843 print_mapping(NULL);
2844 print_direntry(NULL);
2845}
2846#endif
bellardde167e42005-04-28 21:15:08 +00002847