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