Commit de167e416fa3d6e4bbdcac90973954c6bfae3070
1 parent
712e7874
Virtual VFAT support (Johannes Schindelin)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1426 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
1753 additions
and
5 deletions
Changelog
Makefile
@@ -25,7 +25,7 @@ else | @@ -25,7 +25,7 @@ else | ||
25 | endif | 25 | endif |
26 | endif | 26 | endif |
27 | 27 | ||
28 | -qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c | 28 | +qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c |
29 | $(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS) | 29 | $(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS) |
30 | 30 | ||
31 | dyngen$(EXESUF): dyngen.c | 31 | dyngen$(EXESUF): dyngen.c |
Makefile.target
@@ -314,7 +314,7 @@ endif | @@ -314,7 +314,7 @@ endif | ||
314 | 314 | ||
315 | # must use static linking to avoid leaving stuff in virtual address space | 315 | # must use static linking to avoid leaving stuff in virtual address space |
316 | VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o | 316 | VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o |
317 | -VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o | 317 | +VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o |
318 | 318 | ||
319 | SOUND_HW = sb16.o | 319 | SOUND_HW = sb16.o |
320 | AUDIODRV = audio.o noaudio.o wavaudio.o | 320 | AUDIODRV = audio.o noaudio.o wavaudio.o |
block-vvfat.c
0 → 100644
1 | +/* | ||
2 | + * QEMU Block driver for virtual VFAT (shadows a local directory) | ||
3 | + * | ||
4 | + * Copyright (c) 2004 Johannes E. Schindelin | ||
5 | + * | ||
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
7 | + * of this software and associated documentation files (the "Software"), to deal | ||
8 | + * in the Software without restriction, including without limitation the rights | ||
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
10 | + * copies of the Software, and to permit persons to whom the Software is | ||
11 | + * furnished to do so, subject to the following conditions: | ||
12 | + * | ||
13 | + * The above copyright notice and this permission notice shall be included in | ||
14 | + * all copies or substantial portions of the Software. | ||
15 | + * | ||
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
22 | + * THE SOFTWARE. | ||
23 | + */ | ||
24 | +#include <sys/stat.h> | ||
25 | +#include <dirent.h> | ||
26 | +#include <assert.h> | ||
27 | +#include "vl.h" | ||
28 | +#include "block_int.h" | ||
29 | + | ||
30 | +// TODO: new file | ||
31 | +// TODO: delete file | ||
32 | +// TODO: make root directory larger | ||
33 | +// TODO: make directory clusters connected, so they are reserved anyway... add a member which tells how many clusters are reserved after a directory | ||
34 | +// TODO: introduce another member in mapping_t which says where the directory resides in s->directory (for mkdir and rmdir) | ||
35 | +// in _read and _write, before treating direntries or file contents, get_mapping to know what it is. | ||
36 | +// TODO: mkdir | ||
37 | +// TODO: rmdir | ||
38 | + | ||
39 | +// TODO: when commit_data'ing a direntry and is_consistent, commit_remove | ||
40 | +// TODO: reset MODE_MODIFIED when commit_remove'ing | ||
41 | + | ||
42 | +#define DEBUG | ||
43 | + | ||
44 | +/* dynamic array functions */ | ||
45 | +typedef struct array_t { | ||
46 | + char* pointer; | ||
47 | + unsigned int size,next,item_size; | ||
48 | +} array_t; | ||
49 | + | ||
50 | +static inline void array_init(array_t* array,unsigned int item_size) | ||
51 | +{ | ||
52 | + array->pointer=0; | ||
53 | + array->size=0; | ||
54 | + array->next=0; | ||
55 | + array->item_size=item_size; | ||
56 | +} | ||
57 | + | ||
58 | +static inline void array_free(array_t* array) | ||
59 | +{ | ||
60 | + if(array->pointer) | ||
61 | + free(array->pointer); | ||
62 | + array->size=array->next=0; | ||
63 | +} | ||
64 | + | ||
65 | +/* make sure that memory is reserved at pointer[index*item_size] */ | ||
66 | +static inline void* array_get(array_t* array,unsigned int index) { | ||
67 | + if((index+1)*array->item_size>array->size) { | ||
68 | + int new_size=(index+32)*array->item_size; | ||
69 | + array->pointer=realloc(array->pointer,new_size); | ||
70 | + if(!array->pointer) | ||
71 | + return 0; | ||
72 | + array->size=new_size; | ||
73 | + array->next=index+1; | ||
74 | + } | ||
75 | + return array->pointer+index*array->item_size; | ||
76 | +} | ||
77 | + | ||
78 | +static inline void* array_get_next(array_t* array) { | ||
79 | + unsigned int next=array->next; | ||
80 | + void* result=array_get(array,next); | ||
81 | + array->next=next+1; | ||
82 | + return result; | ||
83 | +} | ||
84 | + | ||
85 | +static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) { | ||
86 | + if((array->next+count)*array->item_size>array->size) { | ||
87 | + int increment=count*array->item_size; | ||
88 | + array->pointer=realloc(array->pointer,array->size+increment); | ||
89 | + if(!array->pointer) | ||
90 | + return 0; | ||
91 | + array->size+=increment; | ||
92 | + } | ||
93 | + memmove(array->pointer+(index+count)*array->item_size, | ||
94 | + array->pointer+index*array->item_size, | ||
95 | + (array->next-index)*array->item_size); | ||
96 | + array->next+=count; | ||
97 | + return array->pointer+index*array->item_size; | ||
98 | +} | ||
99 | + | ||
100 | +/* this performs a "roll", so that the element which was at index_from becomes | ||
101 | + * index_to, but the order of all other elements is preserved. */ | ||
102 | +static inline int array_roll(array_t* array,int index_to,int index_from,int count) | ||
103 | +{ | ||
104 | + char* buf; | ||
105 | + char* from; | ||
106 | + char* to; | ||
107 | + int is; | ||
108 | + | ||
109 | + if(!array || | ||
110 | + index_to<0 || index_to>=array->next || | ||
111 | + index_from<0 || index_from>=array->next) | ||
112 | + return -1; | ||
113 | + | ||
114 | + if(index_to==index_from) | ||
115 | + return 0; | ||
116 | + | ||
117 | + is=array->item_size; | ||
118 | + from=array->pointer+index_from*is; | ||
119 | + to=array->pointer+index_to*is; | ||
120 | + buf=malloc(is*count); | ||
121 | + memcpy(buf,from,is*count); | ||
122 | + | ||
123 | + if(index_to<index_from) | ||
124 | + memmove(to+is*count,to,from-to); | ||
125 | + else | ||
126 | + memmove(from,from+is*count,to-from); | ||
127 | + | ||
128 | + memcpy(to,buf,is*count); | ||
129 | + | ||
130 | + free(buf); | ||
131 | + | ||
132 | + return 0; | ||
133 | +} | ||
134 | + | ||
135 | +int array_remove(array_t* array,int index) | ||
136 | +{ | ||
137 | + if(array_roll(array,array->next-1,index,1)) | ||
138 | + return -1; | ||
139 | + array->next--; | ||
140 | + return 0; | ||
141 | +} | ||
142 | + | ||
143 | +/* These structures are used to fake a disk and the VFAT filesystem. | ||
144 | + * For this reason we need to use __attribute__((packed)). */ | ||
145 | + | ||
146 | +typedef struct bootsector_t { | ||
147 | + uint8_t jump[3]; | ||
148 | + uint8_t name[8]; | ||
149 | + uint16_t sector_size; | ||
150 | + uint8_t sectors_per_cluster; | ||
151 | + uint16_t reserved_sectors; | ||
152 | + uint8_t number_of_fats; | ||
153 | + uint16_t root_entries; | ||
154 | + uint16_t zero; | ||
155 | + uint8_t media_type; | ||
156 | + uint16_t sectors_per_fat; | ||
157 | + uint16_t sectors_per_track; | ||
158 | + uint16_t number_of_heads; | ||
159 | + uint32_t hidden_sectors; | ||
160 | + uint32_t total_sectors; | ||
161 | + union { | ||
162 | + struct { | ||
163 | + uint8_t drive_number; | ||
164 | + uint8_t current_head; | ||
165 | + uint8_t signature; | ||
166 | + uint32_t id; | ||
167 | + uint8_t volume_label[11]; | ||
168 | + } __attribute__((packed)) fat16; | ||
169 | + struct { | ||
170 | + uint32_t sectors_per_fat; | ||
171 | + uint16_t flags; | ||
172 | + uint8_t major,minor; | ||
173 | + uint32_t first_cluster_of_root_directory; | ||
174 | + uint16_t info_sector; | ||
175 | + uint16_t backup_boot_sector; | ||
176 | + uint16_t ignored; | ||
177 | + } __attribute__((packed)) fat32; | ||
178 | + } u; | ||
179 | + uint8_t fat_type[8]; | ||
180 | + uint8_t ignored[0x1c0]; | ||
181 | + uint8_t magic[2]; | ||
182 | +} __attribute__((packed)) bootsector_t; | ||
183 | + | ||
184 | +typedef struct partition_t { | ||
185 | + uint8_t attributes; /* 0x80 = bootable */ | ||
186 | + uint8_t start_head; | ||
187 | + uint8_t start_sector; | ||
188 | + uint8_t start_cylinder; | ||
189 | + uint8_t fs_type; /* 0x6 = FAT16, 0xb = FAT32 */ | ||
190 | + uint8_t end_head; | ||
191 | + uint8_t end_sector; | ||
192 | + uint8_t end_cylinder; | ||
193 | + uint32_t start_sector_long; | ||
194 | + uint32_t end_sector_long; | ||
195 | +} __attribute__((packed)) partition_t; | ||
196 | + | ||
197 | +typedef struct mbr_t { | ||
198 | + uint8_t ignored[0x1be]; | ||
199 | + partition_t partition[4]; | ||
200 | + uint8_t magic[2]; | ||
201 | +} __attribute__((packed)) mbr_t; | ||
202 | + | ||
203 | +typedef struct direntry_t { | ||
204 | + uint8_t name[8]; | ||
205 | + uint8_t extension[3]; | ||
206 | + uint8_t attributes; | ||
207 | + uint8_t reserved[2]; | ||
208 | + uint16_t ctime; | ||
209 | + uint16_t cdate; | ||
210 | + uint16_t adate; | ||
211 | + uint16_t begin_hi; | ||
212 | + uint16_t mtime; | ||
213 | + uint16_t mdate; | ||
214 | + uint16_t begin; | ||
215 | + uint32_t size; | ||
216 | +} __attribute__((packed)) direntry_t; | ||
217 | + | ||
218 | +/* this structure are used to transparently access the files */ | ||
219 | + | ||
220 | +typedef struct mapping_t { | ||
221 | + /* begin is the first cluster, end is the last+1, | ||
222 | + * offset is the offset in the file in clusters of this slice */ | ||
223 | + off_t begin,end,offset; | ||
224 | + char* filename; | ||
225 | + | ||
226 | + /* as s->directory is growable, no pointer may be used here */ | ||
227 | + unsigned int dir_index; | ||
228 | + enum { MODE_NORMAL,MODE_UNDEFINED,MODE_MODIFIED,MODE_DELETED,MODE_DIRECTORY } mode; | ||
229 | +} mapping_t; | ||
230 | + | ||
231 | +/* this structure is used to hold sectors which need to be written, but it's | ||
232 | + * not known yet where to write them. */ | ||
233 | + | ||
234 | +typedef struct commit_t { | ||
235 | + uint32_t cluster_num; | ||
236 | + uint8_t* buf; | ||
237 | +} commit_t; | ||
238 | + | ||
239 | +/* write support exists for fat, direntry and file contents */ | ||
240 | +typedef enum { | ||
241 | + WRITE_UNDEFINED,WRITE_FAT,WRITE_DIRENTRY,WRITE_DATA | ||
242 | +} write_action_t; | ||
243 | + | ||
244 | +/* here begins the real VVFAT driver */ | ||
245 | + | ||
246 | +typedef struct BDRVVVFATState { | ||
247 | + unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */ | ||
248 | + unsigned char first_sectors[0x40*0x200]; | ||
249 | + | ||
250 | + int fat_type; /* 16 or 32 */ | ||
251 | + array_t fat,directory,mapping; | ||
252 | + | ||
253 | + unsigned int cluster_size; | ||
254 | + unsigned int sectors_per_cluster; | ||
255 | + unsigned int sectors_per_fat; | ||
256 | + unsigned int sectors_of_root_directory; | ||
257 | + unsigned int sectors_for_directory; | ||
258 | + unsigned int faked_sectors; /* how many sectors are faked before file data */ | ||
259 | + uint32_t sector_count; /* total number of sectors of the partition */ | ||
260 | + uint32_t cluster_count; /* total number of clusters of this partition */ | ||
261 | + unsigned int first_file_mapping; /* index of the first mapping which is not a directory, but a file */ | ||
262 | + uint32_t max_fat_value; | ||
263 | + | ||
264 | + int current_fd; | ||
265 | + char current_fd_is_writable; /* =0 if read only, !=0 if read/writable */ | ||
266 | + mapping_t* current_mapping; | ||
267 | + unsigned char* cluster; | ||
268 | + unsigned int current_cluster; | ||
269 | + | ||
270 | + /* write support */ | ||
271 | + array_t commit; | ||
272 | + /* for each file, the file contents, the direntry, and the fat entries are | ||
273 | + * written, but not necessarily in that order */ | ||
274 | + write_action_t action[3]; | ||
275 | +} BDRVVVFATState; | ||
276 | + | ||
277 | + | ||
278 | +static int vvfat_probe(const uint8_t *buf, int buf_size, const char *filename) | ||
279 | +{ | ||
280 | + if (strstart(filename, "fat:", NULL) || | ||
281 | + strstart(filename, "fatrw:", NULL)) | ||
282 | + return 100; | ||
283 | + return 0; | ||
284 | +} | ||
285 | + | ||
286 | +static void init_mbr(BDRVVVFATState* s) | ||
287 | +{ | ||
288 | + /* TODO: if the files mbr.img and bootsect.img exist, use them */ | ||
289 | + mbr_t* real_mbr=(mbr_t*)s->first_sectors; | ||
290 | + partition_t* partition=&(real_mbr->partition[0]); | ||
291 | + | ||
292 | + memset(s->first_sectors,0,512); | ||
293 | + | ||
294 | + partition->attributes=0x80; /* bootable */ | ||
295 | + partition->start_head=1; | ||
296 | + partition->start_sector=1; | ||
297 | + partition->start_cylinder=0; | ||
298 | + partition->fs_type=(s->fat_type==16?0x6:0xb); /* FAT16/FAT32 */ | ||
299 | + partition->end_head=0xf; | ||
300 | + partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */; | ||
301 | + partition->end_cylinder=0xff; /* lower 8 bits of end cylinder */; | ||
302 | + partition->start_sector_long=cpu_to_le32(0x3f); | ||
303 | + partition->end_sector_long=cpu_to_le32(s->sector_count); | ||
304 | + | ||
305 | + real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa; | ||
306 | +} | ||
307 | + | ||
308 | +/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */ | ||
309 | +static inline int short2long_name(unsigned char* dest,const char* src) | ||
310 | +{ | ||
311 | + int i; | ||
312 | + for(i=0;i<129 && src[i];i++) { | ||
313 | + dest[2*i]=src[i]; | ||
314 | + dest[2*i+1]=0; | ||
315 | + } | ||
316 | + dest[2*i]=dest[2*i+1]=0; | ||
317 | + for(i=2*i+2;(i%26);i++) | ||
318 | + dest[i]=0xff; | ||
319 | + return i; | ||
320 | +} | ||
321 | + | ||
322 | +static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename) | ||
323 | +{ | ||
324 | + char buffer[258]; | ||
325 | + int length=short2long_name(buffer,filename), | ||
326 | + number_of_entries=(length+25)/26,i; | ||
327 | + direntry_t* entry; | ||
328 | + | ||
329 | + for(i=0;i<number_of_entries;i++) { | ||
330 | + entry=array_get_next(&(s->directory)); | ||
331 | + entry->attributes=0xf; | ||
332 | + entry->reserved[0]=0; | ||
333 | + entry->begin=0; | ||
334 | + entry->name[0]=(number_of_entries-i)|(i==0?0x40:0); | ||
335 | + } | ||
336 | + for(i=0;i<length;i++) { | ||
337 | + int offset=(i%26); | ||
338 | + if(offset<10) offset=1+offset; | ||
339 | + else if(offset<22) offset=14+offset-10; | ||
340 | + else offset=28+offset-22; | ||
341 | + entry=array_get(&(s->directory),s->directory.next-1-(i/26)); | ||
342 | + entry->name[offset]=buffer[i]; | ||
343 | + } | ||
344 | + return array_get(&(s->directory),s->directory.next-number_of_entries); | ||
345 | +} | ||
346 | + | ||
347 | +/* fat functions */ | ||
348 | + | ||
349 | +static inline uint8_t fat_chksum(direntry_t* entry) | ||
350 | +{ | ||
351 | + uint8_t chksum=0; | ||
352 | + int i; | ||
353 | + | ||
354 | + for(i=0;i<11;i++) | ||
355 | + chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) | ||
356 | + +(unsigned char)entry->name[i]; | ||
357 | + | ||
358 | + return chksum; | ||
359 | +} | ||
360 | + | ||
361 | +/* if return_time==0, this returns the fat_date, else the fat_time */ | ||
362 | +static uint16_t fat_datetime(time_t time,int return_time) { | ||
363 | + struct tm* t; | ||
364 | +#ifdef _WIN32 | ||
365 | + t=localtime(&time); /* this is not thread safe */ | ||
366 | +#else | ||
367 | + struct tm t1; | ||
368 | + t=&t1; | ||
369 | + localtime_r(&time,t); | ||
370 | +#endif | ||
371 | + if(return_time) | ||
372 | + return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11)); | ||
373 | + return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9)); | ||
374 | +} | ||
375 | + | ||
376 | +static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value) | ||
377 | +{ | ||
378 | + if(s->fat_type==12) { | ||
379 | + assert(0); /* TODO */ | ||
380 | + } else if(s->fat_type==16) { | ||
381 | + uint16_t* entry=array_get(&(s->fat),cluster); | ||
382 | + *entry=cpu_to_le16(value&0xffff); | ||
383 | + } else { | ||
384 | + uint32_t* entry=array_get(&(s->fat),cluster); | ||
385 | + *entry=cpu_to_le32(value); | ||
386 | + } | ||
387 | +} | ||
388 | + | ||
389 | +static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster) | ||
390 | +{ | ||
391 | + //fprintf(stderr,"want to get fat for cluster %d\n",cluster); | ||
392 | + if(s->fat_type==12) { | ||
393 | + const uint8_t* x=s->fat.pointer+cluster*3/2; | ||
394 | + return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff; | ||
395 | + } else if(s->fat_type==16) { | ||
396 | + uint16_t* entry=array_get(&(s->fat),cluster); | ||
397 | + return le16_to_cpu(*entry); | ||
398 | + } else { | ||
399 | + uint32_t* entry=array_get(&(s->fat),cluster); | ||
400 | + return le32_to_cpu(*entry); | ||
401 | + } | ||
402 | +} | ||
403 | + | ||
404 | +static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry) | ||
405 | +{ | ||
406 | + if(fat_entry>s->max_fat_value-8) | ||
407 | + return -1; | ||
408 | + return 0; | ||
409 | +} | ||
410 | + | ||
411 | +static inline void init_fat(BDRVVVFATState* s) | ||
412 | +{ | ||
413 | + int i; | ||
414 | + | ||
415 | + array_init(&(s->fat),(s->fat_type==32?4:2)); | ||
416 | + array_get(&(s->fat),s->sectors_per_fat*0x200/s->fat.item_size-1); | ||
417 | + memset(s->fat.pointer,0,s->fat.size); | ||
418 | + fat_set(s,0,0x7ffffff8); | ||
419 | + | ||
420 | + for(i=1;i<s->sectors_for_directory/s->sectors_per_cluster-1;i++) | ||
421 | + fat_set(s,i,i+1); | ||
422 | + fat_set(s,i,0x7fffffff); | ||
423 | + | ||
424 | + switch(s->fat_type) { | ||
425 | + case 12: s->max_fat_value=0xfff; break; | ||
426 | + case 16: s->max_fat_value=0xffff; break; | ||
427 | + case 32: s->max_fat_value=0xfffffff; break; | ||
428 | + default: s->max_fat_value=0; /* error... */ | ||
429 | + } | ||
430 | + | ||
431 | +} | ||
432 | + | ||
433 | +static inline int long2unix_name(unsigned char* dest,int dest_size,direntry_t* direntry_short) { | ||
434 | + int i=-1,j; | ||
435 | + int chksum=fat_chksum(direntry_short); | ||
436 | + while(1) { | ||
437 | + char* buf=(char*)(direntry_short+i); | ||
438 | + if((buf[0]&0x3f)!=-i || direntry_short[i].reserved[1]!=chksum || | ||
439 | + direntry_short[i].attributes!=0xf) { | ||
440 | + if(i<-1) | ||
441 | + return -3; | ||
442 | + /* take short name */ | ||
443 | + for(j=7;j>0 && direntry_short->name[j]==' ';j--); | ||
444 | + if(j+1>dest_size) | ||
445 | + return -1; | ||
446 | + strncpy(dest,direntry_short->name,j+1); | ||
447 | + dest+=j+1; dest_size-=j+1; | ||
448 | + for(j=2;j>=0 && direntry_short->extension[j]==' ';j--); | ||
449 | + if(j>=0) { | ||
450 | + if(j+2>dest_size) | ||
451 | + return -1; | ||
452 | + dest[0]='.'; | ||
453 | + strncpy(dest+1,direntry_short->extension,j+1); | ||
454 | + } | ||
455 | + return 0; | ||
456 | + } | ||
457 | + for(j=0;j<13;j++) { | ||
458 | + dest_size--; | ||
459 | + if(dest_size<0) | ||
460 | + return -2; | ||
461 | + dest[0]=buf[2*j+((j<5)?1:(j<11)?4:6)]; | ||
462 | + if(dest[0]==0 && (buf[0]&0x40)!=0) | ||
463 | + return 0; | ||
464 | + dest++; | ||
465 | + } | ||
466 | + /* last entry, but no trailing \0? */ | ||
467 | + if(buf[0]&0x40) | ||
468 | + return -3; | ||
469 | + i--; | ||
470 | + } | ||
471 | +} | ||
472 | + | ||
473 | +static inline direntry_t* create_short_filename(BDRVVVFATState* s,unsigned int directory_start,const char* filename,int is_dot) | ||
474 | +{ | ||
475 | + int i,long_index=s->directory.next; | ||
476 | + direntry_t* entry=0; | ||
477 | + direntry_t* entry_long=0; | ||
478 | + | ||
479 | + if(is_dot) { | ||
480 | + entry=array_get_next(&(s->directory)); | ||
481 | + memset(entry->name,0x20,11); | ||
482 | + memcpy(entry->name,filename,strlen(filename)); | ||
483 | + return entry; | ||
484 | + } | ||
485 | + | ||
486 | + for(i=1;i<8 && filename[i] && filename[i]!='.';i++); | ||
487 | + | ||
488 | + entry_long=create_long_filename(s,filename); | ||
489 | + | ||
490 | + entry=array_get_next(&(s->directory)); | ||
491 | + memset(entry->name,0x20,11); | ||
492 | + strncpy(entry->name,filename,i); | ||
493 | + | ||
494 | + if(filename[i]) { | ||
495 | + int len=strlen(filename); | ||
496 | + for(i=len;i>0 && filename[i-1]!='.';i--); | ||
497 | + if(i>0) | ||
498 | + memcpy(entry->extension,filename+i,(len-i>3?3:len-i)); | ||
499 | + } | ||
500 | + | ||
501 | + /* upcase & remove unwanted characters */ | ||
502 | + for(i=10;i>=0;i--) { | ||
503 | + if(i==10 || i==7) for(;i>1 && entry->name[i]==' ';i--); | ||
504 | + if(entry->name[i]<=' ' || entry->name[i]>0x7f | ||
505 | + || strchr("*?<>|\":/\\[];,+='",entry->name[i])) | ||
506 | + entry->name[i]='_'; | ||
507 | + else if(entry->name[i]>='a' && entry->name[i]<='z') | ||
508 | + entry->name[i]+='A'-'a'; | ||
509 | + } | ||
510 | + | ||
511 | + /* mangle duplicates */ | ||
512 | + while(1) { | ||
513 | + direntry_t* entry1=array_get(&(s->directory),directory_start); | ||
514 | + int j; | ||
515 | + | ||
516 | + for(;entry1<entry;entry1++) | ||
517 | + if(!(entry1->attributes&0xf) && !memcmp(entry1->name,entry->name,11)) | ||
518 | + break; /* found dupe */ | ||
519 | + if(entry1==entry) /* no dupe found */ | ||
520 | + break; | ||
521 | + | ||
522 | + /* use all 8 characters of name */ | ||
523 | + if(entry->name[7]==' ') { | ||
524 | + int j; | ||
525 | + for(j=6;j>0 && entry->name[j]==' ';j--) | ||
526 | + entry->name[j]='~'; | ||
527 | + } | ||
528 | + | ||
529 | + /* increment number */ | ||
530 | + for(j=7;j>0 && entry->name[j]=='9';j--) | ||
531 | + entry->name[j]='0'; | ||
532 | + if(j>0) { | ||
533 | + if(entry->name[j]<'0' || entry->name[j]>'9') | ||
534 | + entry->name[j]='0'; | ||
535 | + else | ||
536 | + entry->name[j]++; | ||
537 | + } | ||
538 | + } | ||
539 | + | ||
540 | + /* calculate checksum; propagate to long name */ | ||
541 | + if(entry_long) { | ||
542 | + uint8_t chksum=fat_chksum(entry); | ||
543 | + | ||
544 | + /* calculate anew, because realloc could have taken place */ | ||
545 | + entry_long=array_get(&(s->directory),long_index); | ||
546 | + while(entry_long<entry | ||
547 | + && entry_long->attributes==0xf) { | ||
548 | + entry_long->reserved[1]=chksum; | ||
549 | + entry_long++; | ||
550 | + } | ||
551 | + } | ||
552 | + | ||
553 | + return entry; | ||
554 | +} | ||
555 | + | ||
556 | +static int read_directory(BDRVVVFATState* s,const char* dirname, | ||
557 | + int first_cluster_of_parent) | ||
558 | +{ | ||
559 | + | ||
560 | + DIR* dir=opendir(dirname); | ||
561 | + struct dirent* entry; | ||
562 | + struct stat st; | ||
563 | + unsigned int start_of_directory=s->directory.next; | ||
564 | + /* mappings before first_file_mapping are directories */ | ||
565 | + unsigned int first_directory_mapping=s->first_file_mapping; | ||
566 | + unsigned int first_cluster=(start_of_directory/0x10/s->sectors_per_cluster); | ||
567 | + int i; | ||
568 | + | ||
569 | + if(!dir) | ||
570 | + return -1; | ||
571 | + | ||
572 | + while((entry=readdir(dir))) { | ||
573 | + unsigned int length=strlen(dirname)+2+strlen(entry->d_name); | ||
574 | + char* buffer; | ||
575 | + direntry_t* direntry; | ||
576 | + int is_dot=!strcmp(entry->d_name,"."); | ||
577 | + int is_dotdot=!strcmp(entry->d_name,".."); | ||
578 | + | ||
579 | + if(start_of_directory==1 && (is_dotdot || is_dot)) | ||
580 | + continue; | ||
581 | + | ||
582 | + buffer=(char*)malloc(length); | ||
583 | + snprintf(buffer,length,"%s/%s",dirname,entry->d_name); | ||
584 | + | ||
585 | + if(stat(buffer,&st)<0) { | ||
586 | + free(buffer); | ||
587 | + continue; | ||
588 | + } | ||
589 | + | ||
590 | + /* create directory entry for this file */ | ||
591 | + //fprintf(stderr,"create direntry at %d (cluster %d) for %s\n",s->directory.next,s->directory.next/0x10/s->sectors_per_cluster,entry->d_name); | ||
592 | + direntry=create_short_filename(s,start_of_directory,entry->d_name,is_dot||is_dotdot); | ||
593 | + direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20); | ||
594 | + direntry->reserved[0]=direntry->reserved[1]=0; | ||
595 | + direntry->ctime=fat_datetime(st.st_ctime,1); | ||
596 | + direntry->cdate=fat_datetime(st.st_ctime,0); | ||
597 | + direntry->adate=fat_datetime(st.st_atime,0); | ||
598 | + direntry->begin_hi=0; | ||
599 | + direntry->mtime=fat_datetime(st.st_mtime,1); | ||
600 | + direntry->mdate=fat_datetime(st.st_mtime,0); | ||
601 | + if(is_dotdot) | ||
602 | + direntry->begin=cpu_to_le16(first_cluster_of_parent); | ||
603 | + else if(is_dot) | ||
604 | + direntry->begin=cpu_to_le16(first_cluster); | ||
605 | + else | ||
606 | + direntry->begin=cpu_to_le16(0); /* do that later */ | ||
607 | + direntry->size=cpu_to_le32(st.st_size); | ||
608 | + | ||
609 | + /* create mapping for this file */ | ||
610 | + if(!is_dot && !is_dotdot) { | ||
611 | + if(S_ISDIR(st.st_mode)) | ||
612 | + s->current_mapping=(mapping_t*)array_insert(&(s->mapping),s->first_file_mapping++,1); | ||
613 | + else | ||
614 | + s->current_mapping=(mapping_t*)array_get_next(&(s->mapping)); | ||
615 | + s->current_mapping->begin=0; | ||
616 | + s->current_mapping->end=st.st_size; | ||
617 | + s->current_mapping->offset=0; | ||
618 | + s->current_mapping->filename=buffer; | ||
619 | + s->current_mapping->dir_index=s->directory.next-1; | ||
620 | + s->current_mapping->mode=(S_ISDIR(st.st_mode)?MODE_DIRECTORY:MODE_UNDEFINED); | ||
621 | + } | ||
622 | + } | ||
623 | + closedir(dir); | ||
624 | + | ||
625 | + /* fill with zeroes up to the end of the cluster */ | ||
626 | + while(s->directory.next%(0x10*s->sectors_per_cluster)) { | ||
627 | + direntry_t* direntry=array_get_next(&(s->directory)); | ||
628 | + memset(direntry,0,sizeof(direntry_t)); | ||
629 | + } | ||
630 | + | ||
631 | + /* reserve next cluster also (for new files) */ | ||
632 | + for(i=0;i<0x10*s->sectors_per_cluster;i++) { | ||
633 | + direntry_t* direntry=array_get_next(&(s->directory)); | ||
634 | + memset(direntry,0,sizeof(direntry_t)); | ||
635 | + } | ||
636 | + | ||
637 | + /* was it the first directory? */ | ||
638 | + if(start_of_directory==1) { | ||
639 | + mapping_t* mapping=array_insert(&(s->mapping),0,1); | ||
640 | + mapping->filename=strdup(dirname); | ||
641 | + mapping->mode=MODE_DIRECTORY; | ||
642 | + mapping->begin=0; | ||
643 | + mapping->end=1; | ||
644 | + mapping->offset=0; | ||
645 | + mapping->dir_index=0xffffffff; | ||
646 | + s->sectors_of_root_directory=s->directory.next/0x10; | ||
647 | + } | ||
648 | + | ||
649 | + /* recurse directories */ | ||
650 | + { | ||
651 | + int i; | ||
652 | + | ||
653 | + //fprintf(stderr,"iterating subdirectories of %s (first cluster %d): %d to %d\n",dirname,first_cluster,first_directory_mapping,last_directory_mapping); | ||
654 | + for(i=first_directory_mapping;i<s->first_file_mapping;i++) { | ||
655 | + mapping_t* mapping=array_get(&(s->mapping),i); | ||
656 | + direntry_t* direntry=array_get(&(s->directory),mapping->dir_index); | ||
657 | + /* the directory to be read can add more subdirectories */ | ||
658 | + int last_dir_mapping=s->first_file_mapping; | ||
659 | + | ||
660 | + assert(mapping->mode==MODE_DIRECTORY); | ||
661 | + /* first, tell the mapping where the directory will start */ | ||
662 | + mapping->begin=s->directory.next/0x10/s->sectors_per_cluster; | ||
663 | + if(i>0) { | ||
664 | + mapping[-1].end=mapping->begin; | ||
665 | + assert(mapping[-1].begin<mapping->begin); | ||
666 | + } | ||
667 | + /* then tell the direntry */ | ||
668 | + direntry->begin=cpu_to_le16(mapping->begin); | ||
669 | + //fprintf(stderr,"read directory %s (begin %d)\n",mapping->filename,(int)mapping->begin); | ||
670 | + /* then read it */ | ||
671 | + if(read_directory(s,mapping->filename,first_cluster)) | ||
672 | + return -1; | ||
673 | + | ||
674 | + if(last_dir_mapping!=s->first_file_mapping) { | ||
675 | + int diff=s->first_file_mapping-last_dir_mapping; | ||
676 | + assert(diff>0); | ||
677 | + | ||
678 | + if(last_dir_mapping!=i+1) { | ||
679 | + int count=last_dir_mapping-i-1; | ||
680 | + int to=s->first_file_mapping-count; | ||
681 | + | ||
682 | + assert(count>0); | ||
683 | + assert(to>i+1); | ||
684 | + array_roll(&(s->mapping),to,i+1,count); | ||
685 | + /* could have changed due to realloc */ | ||
686 | + mapping=array_get(&(s->mapping),i); | ||
687 | + mapping->end=mapping[1].begin; | ||
688 | + } | ||
689 | + i+=diff; | ||
690 | + } | ||
691 | + } | ||
692 | + } | ||
693 | + | ||
694 | + return 0; | ||
695 | +} | ||
696 | + | ||
697 | +static int init_directory(BDRVVVFATState* s,const char* dirname) | ||
698 | +{ | ||
699 | + bootsector_t* bootsector=(bootsector_t*)&(s->first_sectors[(s->first_sectors_number-1)*0x200]); | ||
700 | + unsigned int i; | ||
701 | + unsigned int cluster; | ||
702 | + | ||
703 | + memset(&(s->first_sectors[0]),0,0x40*0x200); | ||
704 | + | ||
705 | + /* TODO: if FAT32, this is probably wrong */ | ||
706 | + s->sectors_per_fat=0xfc; | ||
707 | + s->sectors_per_cluster=0x10; | ||
708 | + s->cluster_size=s->sectors_per_cluster*0x200; | ||
709 | + s->cluster=malloc(s->cluster_size); | ||
710 | + | ||
711 | + array_init(&(s->mapping),sizeof(mapping_t)); | ||
712 | + array_init(&(s->directory),sizeof(direntry_t)); | ||
713 | + array_init(&(s->commit),sizeof(commit_t)); | ||
714 | + | ||
715 | + /* add volume label */ | ||
716 | + { | ||
717 | + direntry_t* entry=array_get_next(&(s->directory)); | ||
718 | + entry->attributes=0x28; /* archive | volume label */ | ||
719 | + snprintf(entry->name,11,"QEMU VVFAT"); | ||
720 | + } | ||
721 | + | ||
722 | + if(read_directory(s,dirname,0)) | ||
723 | + return -1; | ||
724 | + | ||
725 | + /* make sure that the number of directory entries is multiple of 0x200/0x20 (to fit the last sector exactly) */ | ||
726 | + s->sectors_for_directory=s->directory.next/0x10; | ||
727 | + | ||
728 | + s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2+s->sectors_for_directory; | ||
729 | + s->cluster_count=(s->sector_count-s->faked_sectors)/s->sectors_per_cluster; | ||
730 | + | ||
731 | + /* Now build FAT, and write back information into directory */ | ||
732 | + init_fat(s); | ||
733 | + | ||
734 | + cluster=s->sectors_for_directory/s->sectors_per_cluster; | ||
735 | + assert(s->sectors_for_directory%s->sectors_per_cluster==0); | ||
736 | + | ||
737 | + /* set the end of the last read directory */ | ||
738 | + if(s->first_file_mapping>0) { | ||
739 | + mapping_t* mapping=array_get(&(s->mapping),s->first_file_mapping-1); | ||
740 | + mapping->end=cluster; | ||
741 | + } | ||
742 | + | ||
743 | + for(i=1;i<s->mapping.next;i++) { | ||
744 | + mapping_t* mapping=array_get(&(s->mapping),i); | ||
745 | + direntry_t* direntry=array_get(&(s->directory),mapping->dir_index); | ||
746 | + if(mapping->mode==MODE_DIRECTORY) { | ||
747 | + /* directory */ | ||
748 | + int i; | ||
749 | +#ifdef DEBUG | ||
750 | + fprintf(stderr,"assert: %s %d < %d\n",mapping->filename,(int)mapping->begin,(int)mapping->end); | ||
751 | +#endif | ||
752 | + assert(mapping->begin<mapping->end); | ||
753 | + for(i=mapping->begin;i<mapping->end-1;i++) | ||
754 | + fat_set(s,i,i+1); | ||
755 | + fat_set(s,i,0x7fffffff); | ||
756 | + } else { | ||
757 | + /* as the space is virtual, we can be sloppy about it */ | ||
758 | + unsigned int end_cluster=cluster+mapping->end/s->cluster_size; | ||
759 | + | ||
760 | + if(end_cluster>=s->cluster_count) { | ||
761 | + fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type); | ||
762 | + return -1; | ||
763 | + } | ||
764 | + mapping->begin=cluster; | ||
765 | + mapping->mode=MODE_NORMAL; | ||
766 | + mapping->offset=0; | ||
767 | + direntry->size=cpu_to_le32(mapping->end); | ||
768 | + if(direntry->size==0) { | ||
769 | + direntry->begin=0; | ||
770 | + mapping->end=cluster; | ||
771 | + continue; | ||
772 | + } | ||
773 | + | ||
774 | + direntry->begin=cpu_to_le16(cluster); | ||
775 | + mapping->end=end_cluster+1; | ||
776 | + for(;cluster<end_cluster;cluster++) | ||
777 | + fat_set(s,cluster,cluster+1); | ||
778 | + fat_set(s,cluster,0x7fffffff); | ||
779 | + cluster++; | ||
780 | + } | ||
781 | + } | ||
782 | + | ||
783 | + s->current_mapping=0; | ||
784 | + | ||
785 | + bootsector->jump[0]=0xeb; | ||
786 | + bootsector->jump[1]=0x3e; | ||
787 | + bootsector->jump[2]=0x90; | ||
788 | + memcpy(bootsector->name,"QEMU ",8); | ||
789 | + bootsector->sector_size=cpu_to_le16(0x200); | ||
790 | + bootsector->sectors_per_cluster=s->sectors_per_cluster; | ||
791 | + bootsector->reserved_sectors=cpu_to_le16(1); | ||
792 | + bootsector->number_of_fats=0x2; /* number of FATs */ | ||
793 | + bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10); | ||
794 | + bootsector->zero=0; | ||
795 | + bootsector->media_type=(s->first_sectors_number==1?0xf0:0xf8); /* media descriptor */ | ||
796 | + bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat); | ||
797 | + bootsector->sectors_per_track=cpu_to_le16(0x3f); | ||
798 | + bootsector->number_of_heads=cpu_to_le16(0x10); | ||
799 | + bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f); | ||
800 | + /* TODO: if FAT32, adjust */ | ||
801 | + bootsector->total_sectors=cpu_to_le32(s->sector_count); | ||
802 | + | ||
803 | + /* TODO: if FAT32, this is wrong */ | ||
804 | + bootsector->u.fat16.drive_number=0x80; /* assume this is hda (TODO) */ | ||
805 | + bootsector->u.fat16.current_head=0; | ||
806 | + bootsector->u.fat16.signature=0x29; | ||
807 | + bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd); | ||
808 | + | ||
809 | + memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11); | ||
810 | + memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8); | ||
811 | + bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa; | ||
812 | + | ||
813 | + return 0; | ||
814 | +} | ||
815 | + | ||
816 | +static int vvfat_open(BlockDriverState *bs, const char* dirname) | ||
817 | +{ | ||
818 | + BDRVVVFATState *s = bs->opaque; | ||
819 | + int i; | ||
820 | + | ||
821 | + /* TODO: automatically determine which FAT type */ | ||
822 | + s->fat_type=16; | ||
823 | + s->sector_count=0xec04f; | ||
824 | + | ||
825 | + s->current_cluster=0xffffffff; | ||
826 | + s->first_file_mapping=0; | ||
827 | + | ||
828 | + /* TODO: if simulating a floppy, this is 1, because there is no partition table */ | ||
829 | + s->first_sectors_number=0x40; | ||
830 | + | ||
831 | + if (strstart(dirname, "fat:", &dirname)) { | ||
832 | + /* read only is the default for safety */ | ||
833 | + bs->read_only = 1; | ||
834 | + } else if (strstart(dirname, "fatrw:", &dirname)) { | ||
835 | + /* development only for now */ | ||
836 | + bs->read_only = 0; | ||
837 | + } else { | ||
838 | + return -1; | ||
839 | + } | ||
840 | + if(init_directory(s,dirname)) | ||
841 | + return -1; | ||
842 | + | ||
843 | + if(s->first_sectors_number==0x40) | ||
844 | + init_mbr(s); | ||
845 | + | ||
846 | + /* TODO: this could be wrong for FAT32 */ | ||
847 | + bs->cyls=1023; bs->heads=15; bs->secs=63; | ||
848 | + bs->total_sectors=bs->cyls*bs->heads*bs->secs; | ||
849 | + | ||
850 | + /* write support */ | ||
851 | + for(i=0;i<3;i++) | ||
852 | + s->action[i]=WRITE_UNDEFINED; | ||
853 | + return 0; | ||
854 | +} | ||
855 | + | ||
856 | +static inline void vvfat_close_current_file(BDRVVVFATState *s) | ||
857 | +{ | ||
858 | + if(s->current_mapping) { | ||
859 | + s->current_mapping = 0; | ||
860 | + close(s->current_fd); | ||
861 | + } | ||
862 | +} | ||
863 | + | ||
864 | +/* mappings between index1 and index2-1 are supposed to be ordered | ||
865 | + * return value is the index of the last mapping for which end>cluster_num | ||
866 | + */ | ||
867 | +static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2) | ||
868 | +{ | ||
869 | + int index3=index1+1; | ||
870 | + //fprintf(stderr,"find_aux: cluster_num=%d, index1=%d,index2=%d\n",cluster_num,index1,index2); | ||
871 | + while(1) { | ||
872 | + mapping_t* mapping; | ||
873 | + index3=(index1+index2)/2; | ||
874 | + mapping=array_get(&(s->mapping),index3); | ||
875 | + //fprintf(stderr,"index3: %d = (%d+%d)/2, end: %d\n",index3,index1,index2,(int)mapping->end); | ||
876 | + if(mapping->end>cluster_num) { | ||
877 | + assert(index2!=index3 || index2==0); | ||
878 | + if(index2==index3) | ||
879 | + return index2; | ||
880 | + index2=index3; | ||
881 | + } else { | ||
882 | + if(index1==index3) | ||
883 | + return index2; | ||
884 | + index1=index3; | ||
885 | + } | ||
886 | + assert(index1<=index2); | ||
887 | + } | ||
888 | +} | ||
889 | + | ||
890 | +static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num) | ||
891 | +{ | ||
892 | + int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next); | ||
893 | + mapping_t* mapping; | ||
894 | + if(index>=s->mapping.next) | ||
895 | + return 0; | ||
896 | + mapping=array_get(&(s->mapping),index); | ||
897 | + if(mapping->begin>cluster_num) | ||
898 | + return 0; | ||
899 | + return mapping; | ||
900 | +} | ||
901 | + | ||
902 | +static int open_file(BDRVVVFATState* s,mapping_t* mapping,int flags) | ||
903 | +{ | ||
904 | + if(!mapping) | ||
905 | + return -1; | ||
906 | + assert(flags==O_RDONLY || flags==O_RDWR); | ||
907 | + if(!s->current_mapping || | ||
908 | + strcmp(s->current_mapping->filename,mapping->filename) || | ||
909 | + (flags==O_RDWR && !s->current_fd_is_writable)) { | ||
910 | + /* open file */ | ||
911 | + int fd = open(mapping->filename, flags | O_BINARY | O_LARGEFILE); | ||
912 | + if(fd<0) | ||
913 | + return -1; | ||
914 | + vvfat_close_current_file(s); | ||
915 | + s->current_fd = fd; | ||
916 | + s->current_fd_is_writable = (flags==O_RDWR?-1:0); | ||
917 | + s->current_mapping = mapping; | ||
918 | + } | ||
919 | + return 0; | ||
920 | +} | ||
921 | + | ||
922 | +static inline int read_cluster(BDRVVVFATState *s,int cluster_num) | ||
923 | +{ | ||
924 | + if(s->current_cluster != cluster_num) { | ||
925 | + int result=0; | ||
926 | + off_t offset; | ||
927 | + if(!s->current_mapping | ||
928 | + || s->current_mapping->begin>cluster_num | ||
929 | + || s->current_mapping->end<=cluster_num) { | ||
930 | + /* binary search of mappings for file */ | ||
931 | + mapping_t* mapping=find_mapping_for_cluster(s,cluster_num); | ||
932 | + if(open_file(s,mapping,O_RDONLY)) | ||
933 | + return -2; | ||
934 | + } | ||
935 | + | ||
936 | + offset=s->cluster_size*(cluster_num-s->current_mapping->begin+s->current_mapping->offset); | ||
937 | + if(lseek(s->current_fd, offset, SEEK_SET)!=offset) | ||
938 | + return -3; | ||
939 | + result=read(s->current_fd,s->cluster,s->cluster_size); | ||
940 | + if(result<0) { | ||
941 | + s->current_cluster = -1; | ||
942 | + return -1; | ||
943 | + } | ||
944 | + s->current_cluster = cluster_num; | ||
945 | + } | ||
946 | + return 0; | ||
947 | +} | ||
948 | + | ||
949 | +static int vvfat_read(BlockDriverState *bs, int64_t sector_num, | ||
950 | + uint8_t *buf, int nb_sectors) | ||
951 | +{ | ||
952 | + BDRVVVFATState *s = bs->opaque; | ||
953 | + int i; | ||
954 | + | ||
955 | + // fprintf(stderr,"vvfat_read: sector %d+%d\n",(int)sector_num,nb_sectors); | ||
956 | + | ||
957 | + for(i=0;i<nb_sectors;i++,sector_num++) { | ||
958 | + if(sector_num<s->faked_sectors) { | ||
959 | + if(sector_num<s->first_sectors_number) | ||
960 | + memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200); | ||
961 | + else if(sector_num-s->first_sectors_number<s->sectors_per_fat) | ||
962 | + memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200); | ||
963 | + else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat) | ||
964 | + memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200); | ||
965 | + else if(sector_num-s->first_sectors_number-s->sectors_per_fat*2<s->sectors_for_directory) | ||
966 | + memcpy(buf+i*0x200,&(s->directory.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat*2)*0x200]),0x200); | ||
967 | + } else { | ||
968 | + uint32_t sector=sector_num-s->first_sectors_number-s->sectors_per_fat*2, | ||
969 | + sector_offset_in_cluster=(sector%s->sectors_per_cluster), | ||
970 | + cluster_num=sector/s->sectors_per_cluster; | ||
971 | + if(read_cluster(s, cluster_num) != 0) { | ||
972 | + //fprintf(stderr,"failed to read cluster %d\n",(int)cluster_num); | ||
973 | + // TODO: strict: return -1; | ||
974 | + memset(buf+i*0x200,0,0x200); | ||
975 | + continue; | ||
976 | + } | ||
977 | + memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200); | ||
978 | + } | ||
979 | + } | ||
980 | + return 0; | ||
981 | +} | ||
982 | + | ||
983 | +static void print_direntry(direntry_t* direntry) | ||
984 | +{ | ||
985 | + if(!direntry) | ||
986 | + return; | ||
987 | + if(direntry->attributes==0xf) { | ||
988 | + unsigned char* c=(unsigned char*)direntry; | ||
989 | + int i; | ||
990 | + for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2) | ||
991 | + fputc(c[i],stderr); | ||
992 | + for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2) | ||
993 | + fputc(c[i],stderr); | ||
994 | + for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2) | ||
995 | + fputc(c[i],stderr); | ||
996 | + fputc('\n',stderr); | ||
997 | + } else { | ||
998 | + int i; | ||
999 | + for(i=0;i<11;i++) | ||
1000 | + fputc(direntry->name[i],stderr); | ||
1001 | + fprintf(stderr,"attributes=0x%02x begin=%d size=%d\n", | ||
1002 | + direntry->attributes, | ||
1003 | + direntry->begin,direntry->size); | ||
1004 | + } | ||
1005 | +} | ||
1006 | + | ||
1007 | +static void print_changed_sector(BlockDriverState *bs,int64_t sector_num,const uint8_t *buf) | ||
1008 | +{ | ||
1009 | + BDRVVVFATState *s = bs->opaque; | ||
1010 | + | ||
1011 | + if(sector_num<s->first_sectors_number) | ||
1012 | + return; | ||
1013 | + if(sector_num<s->first_sectors_number+s->sectors_per_fat*2) { | ||
1014 | + int first=((sector_num-s->first_sectors_number)%s->sectors_per_fat); | ||
1015 | + int first_fat_entry=first*0x200/2; | ||
1016 | + int i; | ||
1017 | + | ||
1018 | + fprintf(stderr, "fat:\n"); | ||
1019 | + for(i=0;i<0x200;i+=2) { | ||
1020 | + uint16_t* f=array_get(&(s->fat),first_fat_entry+i/2); | ||
1021 | + if(memcmp(buf+i,f,2)) | ||
1022 | + fprintf(stderr,"%d(%d->%d) ",first_fat_entry+i/2,*f,*(uint16_t*)(buf+i)); | ||
1023 | + } | ||
1024 | + fprintf(stderr, "\n"); | ||
1025 | + } else if(sector_num<s->faked_sectors) { | ||
1026 | + direntry_t* d=(direntry_t*)buf; | ||
1027 | + int i; | ||
1028 | + fprintf(stderr, "directory:\n"); | ||
1029 | + for(i=0;i<0x200/sizeof(direntry_t);i++) { | ||
1030 | + direntry_t* d_old=(direntry_t*)(s->directory.pointer+0x200*(sector_num-s->first_sectors_number-s->sectors_per_fat*2)+i*sizeof(direntry_t)); | ||
1031 | + if(memcmp(d+i,d_old,sizeof(direntry_t))) { | ||
1032 | + fprintf(stderr, "old: "); print_direntry(d_old); | ||
1033 | + fprintf(stderr, "new: "); print_direntry(d+i); | ||
1034 | + fprintf(stderr, "\n"); | ||
1035 | + } | ||
1036 | + } | ||
1037 | + } else { | ||
1038 | + int sec=(sector_num-s->first_sectors_number-2*s->sectors_per_fat); | ||
1039 | + fprintf(stderr, "\tcluster: %d(+%d sectors)\n",sec/s->sectors_per_cluster,sec%s->sectors_per_cluster); | ||
1040 | + } | ||
1041 | +} | ||
1042 | + | ||
1043 | +char direntry_is_free(const direntry_t* direntry) | ||
1044 | +{ | ||
1045 | + return direntry->name[0]==0 || direntry->name[0]==0xe5; | ||
1046 | +} | ||
1047 | + | ||
1048 | +/* TODO: use this everywhere */ | ||
1049 | +static inline uint32_t begin_of_direntry(direntry_t* direntry) | ||
1050 | +{ | ||
1051 | + return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16); | ||
1052 | +} | ||
1053 | + | ||
1054 | +int consistency_check1(BDRVVVFATState *s) { | ||
1055 | + /* check all mappings */ | ||
1056 | + int i; | ||
1057 | + for(i=0;i<s->mapping.next;i++) { | ||
1058 | + mapping_t* mapping=array_get(&(s->mapping),i); | ||
1059 | + int j; | ||
1060 | + for(j=mapping->begin;j<mapping->end-1;j++) | ||
1061 | + assert(fat_get(s,j)==j+1); | ||
1062 | + assert(fat_get(s,j)==(0x7fffffff&s->max_fat_value)); | ||
1063 | + } | ||
1064 | + return 0; | ||
1065 | +} | ||
1066 | + | ||
1067 | +int consistency_check2(BDRVVVFATState *s) { | ||
1068 | + /* check fat entries: consecutive fat entries should be mapped in one mapping */ | ||
1069 | + int i; | ||
1070 | + /* TODO: i=0 (mappings for direntries have to be sorted) */ | ||
1071 | + for(i=s->sectors_for_directory/s->sectors_per_cluster;i<s->fat.next-1;i++) { | ||
1072 | + uint32_t j=fat_get(s,i); | ||
1073 | + if(j!=i+1 && j!=0 && !fat_eof(s,j)) { | ||
1074 | + mapping_t* mapping=find_mapping_for_cluster(s,i+1); | ||
1075 | + assert(mapping->begin==i+1); | ||
1076 | + } | ||
1077 | + } | ||
1078 | + return 0; | ||
1079 | +} | ||
1080 | + | ||
1081 | +int consistency_check3(BDRVVVFATState *s) { | ||
1082 | + /* check that for each file there is exactly one mapping per cluster */ | ||
1083 | + int i,count_non_next=0; | ||
1084 | + for(i=0;i<s->mapping.next;i++) { | ||
1085 | + mapping_t* mapping=array_get(&(s->mapping),i); | ||
1086 | + /* TODO: when directories are correctly adapted, add them here */ | ||
1087 | + assert(mapping->begin<mapping->end); | ||
1088 | + if(mapping->mode==MODE_NORMAL) { | ||
1089 | + int j,count=0,count_next=0; | ||
1090 | + for(j=0;j<s->mapping.next;j++) { | ||
1091 | + mapping_t* other=array_get(&(s->mapping),j); | ||
1092 | + if(mapping->begin<other->end&&mapping->end>other->begin) | ||
1093 | + count++; | ||
1094 | + if(mapping->end==other->begin) | ||
1095 | + count_next++; | ||
1096 | + } | ||
1097 | + assert(count==1); /* no overlapping mappings */ | ||
1098 | + assert(count_next==1 || count_next==0); /* every mapping except the last one has a successor */ | ||
1099 | + if(!count_next) | ||
1100 | + count_non_next++; | ||
1101 | + } | ||
1102 | + } | ||
1103 | + assert(count_non_next==1); /* only one last mapping */ | ||
1104 | + return 0; | ||
1105 | +} | ||
1106 | + | ||
1107 | +static inline commit_t* commit_get_next(BDRVVVFATState* s) | ||
1108 | +{ | ||
1109 | + commit_t* commit=array_get_next(&(s->commit)); | ||
1110 | + if((commit->buf=malloc(s->cluster_size))==0) { | ||
1111 | + /* out of memory */ | ||
1112 | + s->commit.next--; | ||
1113 | + return 0; | ||
1114 | + } | ||
1115 | + return commit; | ||
1116 | +} | ||
1117 | + | ||
1118 | +int commit_remove(BDRVVVFATState* s,commit_t* commit) | ||
1119 | +{ | ||
1120 | + int index=commit-(commit_t*)s->commit.pointer; | ||
1121 | + free(commit->buf); | ||
1122 | + if(array_roll(&(s->commit),s->commit.next-1,index,1)) | ||
1123 | + return -1; | ||
1124 | + s->commit.next--; | ||
1125 | + return 0; | ||
1126 | +} | ||
1127 | + | ||
1128 | +/* TODO: the plan for write support: | ||
1129 | + * | ||
1130 | + * it seems that the direntries are written first, then the data is committed | ||
1131 | + * to the free sectors, then fat 1 is updated, then fat2. | ||
1132 | + * | ||
1133 | + * Plan: when sectors are written, do the following: | ||
1134 | + * | ||
1135 | + * - if they are in a directory, check if the entry has changed. if yes, | ||
1136 | + * look what has changed (different strategies for name, begin & size). | ||
1137 | + * | ||
1138 | + * if it is new (old entry is only 0's or has E5 at the start), create it, | ||
1139 | + * and also create mapping, but in a special mode "undefined" (TODO), | ||
1140 | + * because we cannot know which clusters belong to it yet. | ||
1141 | + * | ||
1142 | + * if it is zeroed, or has E5 at the start, look if has just moved. If yes, | ||
1143 | + * copy the entry to the new position. If no, delete the file. | ||
1144 | + * | ||
1145 | + * - if they are in data, and the cluster is undefined, add it to the commit | ||
1146 | + * list. if the cluster is defined (find_mapping), then write it into the | ||
1147 | + * corresponding file. | ||
1148 | + * | ||
1149 | + * If it is the last cluster (TODO: add a function | ||
1150 | + * fat_get(s,cluster); ), make sure not to write a complete cluster_size. | ||
1151 | + * | ||
1152 | + * If the data is in current_cluster, update s->cluster. | ||
1153 | + * | ||
1154 | + * - if they are in fat 1, update mappings, look in the commit list | ||
1155 | + * (assertions!) and if the cluster is now known (or changed from undefined | ||
1156 | + * state to defined state, like when begin or size changed in a direntry), | ||
1157 | + * write it. | ||
1158 | + * | ||
1159 | + * - if they are in fat 2, make sure they match with current fat. | ||
1160 | + * | ||
1161 | + */ | ||
1162 | + | ||
1163 | +void mapping_modify_from_direntry(BDRVVVFATState* s,mapping_t* mapping,direntry_t* direntry) | ||
1164 | +{ | ||
1165 | + int begin=le16_to_cpu(direntry->begin), | ||
1166 | + end=begin+le32_to_cpu(direntry->size)/s->cluster_size+1, | ||
1167 | + i; | ||
1168 | + mapping->mode = MODE_MODIFIED; | ||
1169 | + /* TODO: what if begin==0 (size==0)? */ | ||
1170 | + mapping->begin = begin; | ||
1171 | + /* TODO: why not just mapping->end = begin+1 ? */ | ||
1172 | + for(i=begin+1;i<end && (fat_get(s,i)==0 || fat_get(s,i)==i+1);i++); | ||
1173 | + mapping->end = i; | ||
1174 | +} | ||
1175 | + | ||
1176 | +mapping_t* find_mapping_for_direntry(BDRVVVFATState* s,direntry_t* direntry) | ||
1177 | +{ | ||
1178 | + int i; | ||
1179 | + int dir_index=direntry-((direntry_t*)s->directory.pointer); | ||
1180 | + | ||
1181 | + /* TODO: support allocation for new clusters for directories (new/larger directory */ | ||
1182 | + assert(dir_index<0x200/0x20*s->sectors_for_directory); | ||
1183 | + | ||
1184 | + for(i=0;i<s->mapping.next;i++) { | ||
1185 | + mapping_t* mapping=array_get(&(s->mapping),i); | ||
1186 | + if(mapping->dir_index==dir_index && mapping->offset==0 && | ||
1187 | + mapping->mode!=MODE_UNDEFINED) | ||
1188 | + return mapping; | ||
1189 | + } | ||
1190 | + return 0; | ||
1191 | +} | ||
1192 | + | ||
1193 | +static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num) | ||
1194 | +{ | ||
1195 | + return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)/s->sectors_per_cluster; | ||
1196 | +} | ||
1197 | + | ||
1198 | +static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num) | ||
1199 | +{ | ||
1200 | + return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster; | ||
1201 | +} | ||
1202 | + | ||
1203 | +static commit_t* get_commit_for_cluster(BDRVVVFATState* s,uint32_t cluster_num) | ||
1204 | +{ | ||
1205 | + int i; | ||
1206 | + for(i=0;i<s->commit.next;i++) { | ||
1207 | + commit_t* commit=array_get(&(s->commit),i); | ||
1208 | + if(commit->cluster_num==cluster_num) | ||
1209 | + return commit; | ||
1210 | + } | ||
1211 | + return 0; | ||
1212 | +} | ||
1213 | + | ||
1214 | +static inline commit_t* create_or_get_commit_for_sector(BDRVVVFATState* s,off_t sector_num) | ||
1215 | +{ | ||
1216 | + int i; | ||
1217 | + commit_t* commit; | ||
1218 | + uint32_t cluster_num=sector2cluster(s,sector_num); | ||
1219 | + | ||
1220 | + for(i=0;i<s->commit.next;i++) { | ||
1221 | + commit=array_get(&(s->commit),i); | ||
1222 | + if(commit->cluster_num==cluster_num) | ||
1223 | + return commit; | ||
1224 | + } | ||
1225 | + | ||
1226 | + commit=commit_get_next(s); | ||
1227 | + commit->cluster_num=cluster_num; | ||
1228 | + /* we can ignore read errors here */ | ||
1229 | + read_cluster(s,cluster_num); | ||
1230 | + memcpy(commit->buf,s->cluster,s->cluster_size); | ||
1231 | + return commit; | ||
1232 | +} | ||
1233 | + | ||
1234 | +static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping) | ||
1235 | +{ | ||
1236 | + if(mapping->mode==MODE_UNDEFINED) | ||
1237 | + return 0; | ||
1238 | + if(mapping->dir_index>=0x200/0x20*s->sectors_for_directory) | ||
1239 | + return 0; | ||
1240 | + return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index); | ||
1241 | +} | ||
1242 | + | ||
1243 | +static void print_mappings(BDRVVVFATState* s) | ||
1244 | +{ | ||
1245 | + int i; | ||
1246 | + fprintf(stderr,"mapping:\n"); | ||
1247 | + for(i=0;i<s->mapping.next;i++) { | ||
1248 | + mapping_t* m=array_get(&(s->mapping),i); | ||
1249 | + direntry_t* d=get_direntry_for_mapping(s,m); | ||
1250 | + fprintf(stderr,"%02d %d-%d (%d) %s (dir: %d)",i,(int)m->begin,(int)m->end,(int)m->offset,m->filename,m->dir_index); | ||
1251 | + print_direntry(d); | ||
1252 | + fprintf(stderr,"\n"); | ||
1253 | + } | ||
1254 | + fprintf(stderr,"mappings end.\n"); | ||
1255 | +} | ||
1256 | + | ||
1257 | +/* TODO: statify all functions */ | ||
1258 | + | ||
1259 | +/* This function is only meant for file contents. | ||
1260 | + * It will return an error if used for other sectors. */ | ||
1261 | +static int write_cluster(BDRVVVFATState* s,uint32_t cluster_num,const uint8_t* buf) | ||
1262 | +{ | ||
1263 | + /* sector_offset is the sector_num relative to the first cluster */ | ||
1264 | + mapping_t* mapping=find_mapping_for_cluster(s,cluster_num); | ||
1265 | + direntry_t* direntry; | ||
1266 | + int next_cluster,write_size,last_cluster; | ||
1267 | + off_t offset; | ||
1268 | + | ||
1269 | + /* if this cluster is free, return error */ | ||
1270 | + next_cluster=fat_get(s,cluster_num); | ||
1271 | + if(next_cluster<2) | ||
1272 | + return -1; | ||
1273 | + | ||
1274 | + /* TODO: MODE_DIRECTORY */ | ||
1275 | + if(!mapping || mapping->mode==MODE_UNDEFINED || mapping->mode==MODE_DIRECTORY) | ||
1276 | + return -1; | ||
1277 | + direntry=get_direntry_for_mapping(s,mapping); | ||
1278 | + if(!direntry) | ||
1279 | + return -2; | ||
1280 | + | ||
1281 | + /* get size to write */ | ||
1282 | + last_cluster=fat_eof(s,next_cluster); | ||
1283 | + write_size=!last_cluster?s->cluster_size: | ||
1284 | + (le32_to_cpu(direntry->size)%s->cluster_size); | ||
1285 | + if(write_size<=0) | ||
1286 | + return 0; | ||
1287 | + //fprintf(stderr,"next_cluster: %d (%d), write_size: %d, %d, %d\n",next_cluster,s->max_fat_value-8,write_size,direntry->size,s->cluster_size); | ||
1288 | + | ||
1289 | + if(open_file(s,mapping,O_RDWR)) | ||
1290 | + return -4; | ||
1291 | + | ||
1292 | + offset=(cluster_num-mapping->begin+mapping->offset)*s->cluster_size; | ||
1293 | + if(lseek(s->current_fd,offset,SEEK_SET)!=offset) | ||
1294 | + return -3; | ||
1295 | + if(write(s->current_fd,buf,write_size)!=write_size) { | ||
1296 | + lseek(s->current_fd,0,SEEK_END); | ||
1297 | + vvfat_close_current_file(s); | ||
1298 | + return -2; | ||
1299 | + } | ||
1300 | + | ||
1301 | + /* seek to end of file, so it doesn't get truncated */ | ||
1302 | + if(!last_cluster) | ||
1303 | + lseek(s->current_fd,0,SEEK_END); | ||
1304 | + else { | ||
1305 | + ftruncate(s->current_fd,le32_to_cpu(direntry->size)); | ||
1306 | + vvfat_close_current_file(s); | ||
1307 | + } | ||
1308 | + | ||
1309 | + /* update s->cluster if necessary */ | ||
1310 | + if(cluster_num==s->current_cluster && s->cluster!=buf) | ||
1311 | + memcpy(s->cluster,buf,s->cluster_size); | ||
1312 | + | ||
1313 | + return 0; | ||
1314 | +} | ||
1315 | + | ||
1316 | +/* this function returns !=0 on error */ | ||
1317 | +int mapping_is_consistent(BDRVVVFATState* s,mapping_t* mapping) | ||
1318 | +{ | ||
1319 | + direntry_t* direntry=get_direntry_for_mapping(s,mapping); | ||
1320 | + uint32_t cluster_count=0; | ||
1321 | + int commit_count=0; /* number of commits for this file (we also write incomplete files; think "append") */ | ||
1322 | + //fprintf(stderr,"check direntry for %s\n",mapping->filename); | ||
1323 | + while(mapping) { | ||
1324 | + int i; | ||
1325 | + assert(mapping->begin<mapping->end); | ||
1326 | + for(i=mapping->begin;i<mapping->end-1;i++) { | ||
1327 | + if(i<=0 || fat_get(s,i)!=i+1) { | ||
1328 | + /*fprintf(stderr,"the fat mapping of %d is not %d, but %d\n", | ||
1329 | + i,i+1,fat_get(s,i));*/ | ||
1330 | + return -1; | ||
1331 | + } | ||
1332 | + if(get_commit_for_cluster(s,i)) | ||
1333 | + commit_count++; | ||
1334 | + } | ||
1335 | + if(get_commit_for_cluster(s,i)) | ||
1336 | + commit_count++; | ||
1337 | + | ||
1338 | + cluster_count+=mapping->end-mapping->begin; | ||
1339 | + | ||
1340 | + i=fat_get(s,mapping->end-1); | ||
1341 | + if(fat_eof(s,i)) | ||
1342 | + break; | ||
1343 | + | ||
1344 | + mapping=find_mapping_for_cluster(s,i); | ||
1345 | + if(!mapping) { | ||
1346 | + //fprintf(stderr,"No mapping found for %d\n",i); | ||
1347 | + print_mappings(s); | ||
1348 | + return -2; | ||
1349 | + } | ||
1350 | + } | ||
1351 | + | ||
1352 | + if(cluster_count!=(le32_to_cpu(direntry->size)+s->cluster_size-1)/s->cluster_size) { | ||
1353 | + //fprintf(stderr,"cluster_count is %d, but size is %d\n",cluster_count,le32_to_cpu(direntry->size)); | ||
1354 | + return -3; | ||
1355 | + } | ||
1356 | + | ||
1357 | + if(commit_count==0) | ||
1358 | + return -4; | ||
1359 | + | ||
1360 | + //fprintf(stderr,"okay\n"); | ||
1361 | + return 0; | ||
1362 | +} | ||
1363 | + | ||
1364 | +/* TODO: remember what comes third, and what's first in this OS: | ||
1365 | + * FAT, direntry or data. | ||
1366 | + * If the last written sector is either last in cluster or sector_num+nb_sectors-1, | ||
1367 | + * - commit every cluster for this file if mapping_is_consistent()==0 | ||
1368 | + * - if the last written sector is first_action, and last_action=third_action, clear commit | ||
1369 | + */ | ||
1370 | + | ||
1371 | +static int commit_cluster_aux(BDRVVVFATState* s,commit_t* commit) | ||
1372 | +{ | ||
1373 | + int result=write_cluster(s,commit->cluster_num,commit->buf); | ||
1374 | + return result; | ||
1375 | +} | ||
1376 | + | ||
1377 | + | ||
1378 | +static int commit_cluster(BDRVVVFATState* s,uint32_t cluster_num) | ||
1379 | +{ | ||
1380 | + commit_t* commit; | ||
1381 | + | ||
1382 | + /* commit the sectors of this cluster */ | ||
1383 | + commit=get_commit_for_cluster(s,cluster_num); | ||
1384 | + if(commit) | ||
1385 | + return commit_cluster_aux(s,commit); | ||
1386 | + return 0; | ||
1387 | +} | ||
1388 | + | ||
1389 | +/* this function checks the consistency for the direntry which belongs to | ||
1390 | + * the mapping. if everything is found consistent, the data is committed. | ||
1391 | + * this returns 0 if no error occurred (even if inconsistencies were found) */ | ||
1392 | +static inline int commit_data_if_consistent(BDRVVVFATState* s,mapping_t* mapping,write_action_t action) | ||
1393 | +{ | ||
1394 | + direntry_t* direntry; | ||
1395 | + | ||
1396 | + if(!mapping) | ||
1397 | + return 0; | ||
1398 | + | ||
1399 | + //fprintf(stderr,"7\n"); | ||
1400 | +#define d(x) fprintf(stderr,#x "\n") | ||
1401 | + direntry=get_direntry_for_mapping(s,mapping); | ||
1402 | + | ||
1403 | + //d(8); | ||
1404 | + | ||
1405 | + assert(action==WRITE_FAT || action==WRITE_DIRENTRY || action==WRITE_DATA); | ||
1406 | + | ||
1407 | + //d(9); | ||
1408 | + //fprintf(stderr,"mapping: 0x%x s=0x%x\n",(uint32_t)mapping,(uint32_t)s); | ||
1409 | + /*fprintf(stderr,"commit? file=%s, action=%s\n", | ||
1410 | + mapping->filename,action==WRITE_FAT?"fat":action==WRITE_DIRENTRY?"direntry":"data");*/ | ||
1411 | + | ||
1412 | + //d(10); | ||
1413 | + if(s->action[2]==WRITE_UNDEFINED) { | ||
1414 | + int i; | ||
1415 | + for(i=2;i>0 && s->action[i-1]==WRITE_UNDEFINED;i--); | ||
1416 | + if(i>0 && action!=s->action[i-1]) | ||
1417 | + s->action[i]=action; | ||
1418 | + assert(i<2 || s->action[0]!=s->action[2]); | ||
1419 | + } | ||
1420 | + //d(11); | ||
1421 | + | ||
1422 | + if(mapping_is_consistent(s,mapping)==0) { | ||
1423 | + uint32_t cluster_num=begin_of_direntry(direntry); | ||
1424 | + off_t remaining_bytes=le32_to_cpu(direntry->size); | ||
1425 | + //fprintf(stderr,"the data for %s was found consistent\n",mapping->filename); | ||
1426 | + while(remaining_bytes>0) { | ||
1427 | + commit_t* commit=get_commit_for_cluster(s,cluster_num); | ||
1428 | + if(!commit) | ||
1429 | + continue; | ||
1430 | + | ||
1431 | + //fprintf(stderr,"commit_cluster %d (%d), remaining: %d\n",cluster_num,s->max_fat_value-15,(int)remaining_bytes); | ||
1432 | + assert(cluster_num>1); | ||
1433 | + assert(cluster_num<s->max_fat_value-15); | ||
1434 | + if(commit_cluster(s,cluster_num)) { | ||
1435 | + fprintf(stderr,"error committing cluster %d\n",cluster_num); | ||
1436 | + return -1; | ||
1437 | + } | ||
1438 | + cluster_num=fat_get(s,cluster_num); | ||
1439 | + remaining_bytes-=s->cluster_size; | ||
1440 | + /* TODO: if(action==s->action[2]) { | ||
1441 | + commit_t* commit=get_commit_for_cluster(s,cluster_num); | ||
1442 | + commit_remove(s,commit); | ||
1443 | + } */ | ||
1444 | + } | ||
1445 | + } | ||
1446 | + //print_mappings(s); | ||
1447 | + //fprintf(stderr,"finish vvfat_write\n"); | ||
1448 | + return 0; | ||
1449 | +} | ||
1450 | + | ||
1451 | +static int vvfat_write(BlockDriverState *bs, int64_t sector_num, | ||
1452 | + const uint8_t *buf, int nb_sectors) | ||
1453 | +{ | ||
1454 | + BDRVVVFATState *s = bs->opaque; | ||
1455 | + int i; | ||
1456 | + | ||
1457 | + /* fprintf(stderr,"vvfat_write %d+%d (%s)\n",(int)sector_num,nb_sectors, | ||
1458 | + (sector_num>=s->faked_sectors?"data": | ||
1459 | + (sector_num>=s->first_sectors_number+2*s->sectors_per_fat?"directory": | ||
1460 | + (sector_num>=s->first_sectors_number+s->sectors_per_fat?"fat 2": | ||
1461 | + (sector_num>=s->first_sectors_number?"fat 1":"boot sector"))))); */ | ||
1462 | + | ||
1463 | + for(i=0;i<nb_sectors;i++,sector_num++,buf+=0x200) { | ||
1464 | + print_changed_sector(bs,sector_num,buf); | ||
1465 | + | ||
1466 | + if(sector_num<s->first_sectors_number) { | ||
1467 | + /* change the bootsector or partition table? no! */ | ||
1468 | + return -1; | ||
1469 | + } else if(sector_num<s->first_sectors_number+s->sectors_per_fat) { | ||
1470 | + /* FAT 1 */ | ||
1471 | + int fat_entries_per_cluster=s->cluster_size*8/s->fat_type; | ||
1472 | + int first_cluster=(sector_num-s->first_sectors_number)*fat_entries_per_cluster,i; | ||
1473 | + mapping_t* mapping=0; | ||
1474 | + | ||
1475 | + /* write back */ | ||
1476 | + memcpy(s->fat.pointer+0x200*(sector_num-s->first_sectors_number), | ||
1477 | + buf,0x200); | ||
1478 | + | ||
1479 | + /* for each changed FAT entry, */ | ||
1480 | + for(i=0;i<fat_entries_per_cluster;i++) { | ||
1481 | + int new_value; | ||
1482 | + | ||
1483 | + /* TODO: MODE_DIRENTRY */ | ||
1484 | + if(first_cluster+i<s->sectors_for_directory/s->sectors_per_cluster) | ||
1485 | + continue; | ||
1486 | + | ||
1487 | + new_value=fat_get(s,first_cluster+i); | ||
1488 | + | ||
1489 | + /* check the current fat entry */ | ||
1490 | + if(new_value<2 || (new_value>=s->max_fat_value-0xf && !fat_eof(s,new_value))) { | ||
1491 | + /* free, reserved or bad cluster */ | ||
1492 | + mapping=find_mapping_for_cluster(s,first_cluster+i); | ||
1493 | + //assert(!mapping || mapping->mode==MODE_DELETED); | ||
1494 | + if(mapping && mapping->mode==MODE_DELETED && | ||
1495 | + first_cluster+i+1==mapping->end) | ||
1496 | + array_remove(&(s->mapping),mapping-(mapping_t*)s->mapping.pointer); | ||
1497 | + mapping=0; | ||
1498 | + continue; | ||
1499 | + } | ||
1500 | + | ||
1501 | + /* get the mapping for the current entry */ | ||
1502 | + if(!mapping || mapping->begin>new_value || mapping->end<=new_value) { | ||
1503 | + mapping=find_mapping_for_cluster(s,first_cluster+i); | ||
1504 | + } | ||
1505 | + | ||
1506 | + print_mappings(s); | ||
1507 | + fprintf(stderr,"fat_get(%d)=%d\n",first_cluster+i,new_value); | ||
1508 | + /* TODO: what if there's no mapping? this is valid. */ | ||
1509 | + /* TODO: refactor the rest of this clause so it can be called when the direntry changes, too */ | ||
1510 | + assert(mapping); | ||
1511 | + | ||
1512 | + if(new_value>1 && new_value<s->max_fat_value-0xf) { | ||
1513 | + /* the cluster new_value points to is valid */ | ||
1514 | + | ||
1515 | + if(first_cluster+i+1==new_value) { | ||
1516 | + /* consecutive cluster */ | ||
1517 | + if(mapping->end<=new_value) | ||
1518 | + mapping->end=new_value+1; | ||
1519 | + } else { | ||
1520 | + mapping_t* next_mapping; | ||
1521 | + | ||
1522 | + /* the current mapping ends here */ | ||
1523 | + mapping->end=first_cluster+i+1; | ||
1524 | + | ||
1525 | + /* the next mapping */ | ||
1526 | + next_mapping=find_mapping_for_cluster(s,new_value); | ||
1527 | + if(next_mapping) { | ||
1528 | + assert(mapping!=next_mapping); | ||
1529 | + /* assert next mapping's filename is the same */ | ||
1530 | + assert(next_mapping->filename==mapping->filename); | ||
1531 | + assert(next_mapping->dir_index==mapping->dir_index); | ||
1532 | + /* assert next mapping is MODIFIED or UNDEFINED */ | ||
1533 | + assert(next_mapping->mode==MODE_MODIFIED || next_mapping->mode==MODE_UNDEFINED); | ||
1534 | + } else { | ||
1535 | + int index=find_mapping_for_cluster_aux(s,new_value,0,s->mapping.next); | ||
1536 | + next_mapping=array_insert(&(s->mapping),index,1); | ||
1537 | + next_mapping->filename=mapping->filename; | ||
1538 | + next_mapping->dir_index=mapping->dir_index; | ||
1539 | + next_mapping->mode=MODE_MODIFIED; | ||
1540 | + next_mapping->begin=0; | ||
1541 | + } | ||
1542 | + /* adjust offset of next mapping */ | ||
1543 | + next_mapping->offset=mapping->offset+mapping->end-mapping->begin; | ||
1544 | + /* set begin and possible end */ | ||
1545 | + if(next_mapping->begin!=new_value) { | ||
1546 | + next_mapping->begin=new_value; | ||
1547 | + next_mapping->end=new_value+1; | ||
1548 | + } | ||
1549 | + if(commit_data_if_consistent(s,mapping,WRITE_FAT)) | ||
1550 | + return -4; | ||
1551 | + mapping=0; | ||
1552 | + } | ||
1553 | + } else if(fat_eof(s,new_value)) { | ||
1554 | + /* the last cluster of the file */ | ||
1555 | + mapping->end=first_cluster+i+1; | ||
1556 | + if(commit_data_if_consistent(s,mapping,WRITE_FAT)) | ||
1557 | + return -4; | ||
1558 | + mapping=0; | ||
1559 | + } | ||
1560 | + } | ||
1561 | + } else if(sector_num<s->first_sectors_number+2*s->sectors_per_fat) { | ||
1562 | + /* FAT 2: check if it is the same as FAT 1 */ | ||
1563 | + if(memcmp(array_get(&(s->fat),sector_num-s->first_sectors_number),buf,0x200)) | ||
1564 | + return -1; /* mismatch */ | ||
1565 | + } else if(sector_num<s->faked_sectors) { | ||
1566 | + /* direntry */ | ||
1567 | + /* - if they are in a directory, check if the entry has changed. | ||
1568 | + * if yes, look what has changed (different strategies for name, | ||
1569 | + * begin & size). | ||
1570 | + * | ||
1571 | + * if it is new (old entry is only 0's or has E5 at the start), | ||
1572 | + * create it, and also create mapping, but in a special mode | ||
1573 | + * "undefined", because we cannot know which clusters belong | ||
1574 | + * to it yet. | ||
1575 | + * | ||
1576 | + * if it is zeroed, or has E5 at the start, look if has just | ||
1577 | + * moved. If yes, copy the entry to the new position. If no, | ||
1578 | + * delete the file. | ||
1579 | + */ | ||
1580 | + mapping_t* dir_mapping=find_mapping_for_cluster(s,sector2cluster(s,sector_num)); | ||
1581 | + direntry_t *original=array_get(&(s->directory),sector_num-s->first_sectors_number-2*s->sectors_per_fat); | ||
1582 | + direntry_t *new_=(direntry_t*)buf; | ||
1583 | + int first_dir_index=(sector_num-s->first_sectors_number-2*s->sectors_per_fat)*0x200/0x20; | ||
1584 | + int j; | ||
1585 | + | ||
1586 | +#if 0 | ||
1587 | + fprintf(stderr,"direntry: consistency check\n"); | ||
1588 | + | ||
1589 | + if(s->commit.next==0) { | ||
1590 | + consistency_check1(s); | ||
1591 | + consistency_check2(s); | ||
1592 | + consistency_check3(s); | ||
1593 | + } | ||
1594 | +#endif | ||
1595 | + | ||
1596 | + assert(sizeof(direntry_t)==0x20); | ||
1597 | + | ||
1598 | + for(j=0;j<0x200/0x20;j++) { | ||
1599 | + //fprintf(stderr,"compare direntry %d: 0x%x,0x%x\n",j,(uint32_t)original+j,(uint32_t)new_+j); | ||
1600 | + if(memcmp(original+j,new_+j,sizeof(direntry_t))) { | ||
1601 | + //fprintf(stderr,"different\n"); | ||
1602 | + /* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */ | ||
1603 | + if(direntry_is_free(original+j)) { | ||
1604 | + mapping_t* mapping; | ||
1605 | + char buffer[4096]; | ||
1606 | + int fd,i; | ||
1607 | + | ||
1608 | + if(new_[j].attributes==0xf) | ||
1609 | + continue; /* long entry */ | ||
1610 | + | ||
1611 | + print_mappings(s); | ||
1612 | + //fprintf(stderr,"sector: %d cluster: %d\n",(int)sector_num,(int)sector2cluster(s,sector_num)); | ||
1613 | + | ||
1614 | + /* construct absolute path */ | ||
1615 | + strncpy(buffer,dir_mapping->filename,4096); | ||
1616 | + i=strlen(buffer); | ||
1617 | + if(i+2>=4096) | ||
1618 | + return -1; | ||
1619 | + buffer[i]='/'; | ||
1620 | + if(long2unix_name(buffer+i+1,4096-i-1,new_+j)) | ||
1621 | + return -2; | ||
1622 | + | ||
1623 | + /* new file/directory */ | ||
1624 | + if(new_[j].attributes&0x10) { | ||
1625 | +#ifdef _WIN32 | ||
1626 | +#define SEVENFIVEFIVE | ||
1627 | +#else | ||
1628 | +#define SEVENFIVEFIVE ,0755 | ||
1629 | +#endif | ||
1630 | + if(mkdir(buffer SEVENFIVEFIVE)) | ||
1631 | + return -3; | ||
1632 | + /* TODO: map direntry.begin as directory, together with new array_t direntries */ | ||
1633 | + assert(0); | ||
1634 | + } else { | ||
1635 | + fd=open(buffer,O_CREAT|O_EXCL,0644); | ||
1636 | + if(!fd) | ||
1637 | + return -3; | ||
1638 | + close(fd); | ||
1639 | + } | ||
1640 | + | ||
1641 | + /* create mapping */ | ||
1642 | + i=find_mapping_for_cluster_aux(s,begin_of_direntry(new_+j),0,s->mapping.next); | ||
1643 | + mapping=array_insert(&(s->mapping),i,1); | ||
1644 | + mapping->filename=strdup(buffer); | ||
1645 | + mapping->offset=0; | ||
1646 | + /* back pointer to direntry */ | ||
1647 | + mapping->dir_index=first_dir_index+j; | ||
1648 | + /* set mode to modified */ | ||
1649 | + mapping->mode=MODE_MODIFIED; | ||
1650 | + /* set begin to direntry.begin */ | ||
1651 | + mapping->begin=begin_of_direntry(new_+j); | ||
1652 | + /* set end to begin+1 */ | ||
1653 | + mapping->end=mapping->begin+1; | ||
1654 | + /* commit file contents */ | ||
1655 | + if(commit_data_if_consistent(s,mapping,WRITE_DIRENTRY)) { | ||
1656 | + fprintf(stderr,"error committing file contents for new file %s!\n",buffer); | ||
1657 | + return -4; | ||
1658 | + } | ||
1659 | + } else if(direntry_is_free(new_+j)) { | ||
1660 | + assert(0); | ||
1661 | + /* TODO: delete file */ | ||
1662 | + /* TODO: write direntry */ | ||
1663 | + /* TODO: modify mapping: set mode=deleted */ | ||
1664 | + } else { | ||
1665 | + /* modified file */ | ||
1666 | + mapping_t* mapping=0; | ||
1667 | + /* if direntry.begin has changed, | ||
1668 | + * set mode to modified, | ||
1669 | + * adapt begin, | ||
1670 | + * adapt end */ | ||
1671 | + /* TODO: handle rename */ | ||
1672 | + assert(!memcmp(new_[j].name,original[j].name,11)); | ||
1673 | + //fprintf(stderr,"1\n"); | ||
1674 | + if(new_[j].begin!=original[j].begin || new_[j].size/s->cluster_size!=original[j].size/s->cluster_size) { | ||
1675 | + //fprintf(stderr,"2\n"); | ||
1676 | + mapping = find_mapping_for_direntry(s,original+j); | ||
1677 | + //fprintf(stderr,"3\n"); | ||
1678 | + if(!mapping) /* this should never happen! */ | ||
1679 | + return -2; | ||
1680 | + mapping_modify_from_direntry(s,mapping,new_+j); | ||
1681 | + //fprintf(stderr,"4\n"); | ||
1682 | + if(commit_data_if_consistent(s,mapping,WRITE_DIRENTRY)) { | ||
1683 | + fprintf(stderr,"big error\n"); | ||
1684 | + return -4; | ||
1685 | + } | ||
1686 | + } | ||
1687 | + /* TODO: handle modified times and other attributes */ | ||
1688 | + | ||
1689 | + //fprintf(stderr,"5: mapping=0x%x, s=0x%x, s->mapping.pointer=0x%x\n",(uint32_t)mapping,(uint32_t)s,(uint32_t)s->mapping.pointer); | ||
1690 | + //fprintf(stderr,"6\n"); | ||
1691 | + } | ||
1692 | + } | ||
1693 | + } | ||
1694 | + /* write back direntries */ | ||
1695 | + memcpy(original,new_,0x200); | ||
1696 | + } else { | ||
1697 | + /* data */ | ||
1698 | + off_t sector=sector_num-s->first_sectors_number-2*s->sectors_per_fat; | ||
1699 | + off_t cluster=sector/s->sectors_per_cluster; | ||
1700 | + mapping_t* mapping=find_mapping_for_cluster(s,cluster); | ||
1701 | + if(mapping && mapping->mode==MODE_DELETED) | ||
1702 | + return -3; /* this is an error: no writes to these clusters before committed */ | ||
1703 | + { | ||
1704 | + /* as of yet, undefined: put into commits */ | ||
1705 | + commit_t* commit=create_or_get_commit_for_sector(s,sector_num); | ||
1706 | + | ||
1707 | + if(!commit) | ||
1708 | + return -1; /* out of memory */ | ||
1709 | + memcpy(commit->buf+0x200*sector_offset_in_cluster(s,sector_num),buf,0x200); | ||
1710 | + | ||
1711 | + //fprintf(stderr,"mapping: 0x%x\n",(uint32_t)mapping); | ||
1712 | + if(commit_data_if_consistent(s,mapping,WRITE_DATA)) | ||
1713 | + return -4; | ||
1714 | + } | ||
1715 | + } | ||
1716 | + } | ||
1717 | + return 0; | ||
1718 | +} | ||
1719 | + | ||
1720 | +static void vvfat_close(BlockDriverState *bs) | ||
1721 | +{ | ||
1722 | + BDRVVVFATState *s = bs->opaque; | ||
1723 | + | ||
1724 | + vvfat_close_current_file(s); | ||
1725 | + array_free(&(s->fat)); | ||
1726 | + array_free(&(s->directory)); | ||
1727 | + array_free(&(s->mapping)); | ||
1728 | + if(s->cluster) | ||
1729 | + free(s->cluster); | ||
1730 | +} | ||
1731 | + | ||
1732 | +BlockDriver bdrv_vvfat = { | ||
1733 | + "vvfat", | ||
1734 | + sizeof(BDRVVVFATState), | ||
1735 | + vvfat_probe, | ||
1736 | + vvfat_open, | ||
1737 | + vvfat_read, | ||
1738 | + vvfat_write, | ||
1739 | + vvfat_close, | ||
1740 | +}; | ||
1741 | + | ||
1742 | + |
qemu-img.c
@@ -658,9 +658,10 @@ static int img_info(int argc, char **argv) | @@ -658,9 +658,10 @@ static int img_info(int argc, char **argv) | ||
658 | get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); | 658 | get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); |
659 | allocated_size = get_allocated_file_size(filename); | 659 | allocated_size = get_allocated_file_size(filename); |
660 | if (allocated_size < 0) | 660 | if (allocated_size < 0) |
661 | - error("Could not get file size '%s'", filename); | ||
662 | - get_human_readable_size(dsize_buf, sizeof(dsize_buf), | ||
663 | - allocated_size); | 661 | + sprintf(dsize_buf, "unavailable"); |
662 | + else | ||
663 | + get_human_readable_size(dsize_buf, sizeof(dsize_buf), | ||
664 | + allocated_size); | ||
664 | printf("image: %s\n" | 665 | printf("image: %s\n" |
665 | "file format: %s\n" | 666 | "file format: %s\n" |
666 | "virtual size: %s (%lld bytes)\n" | 667 | "virtual size: %s (%lld bytes)\n" |
vl.h
@@ -384,6 +384,7 @@ extern BlockDriver bdrv_cloop; | @@ -384,6 +384,7 @@ extern BlockDriver bdrv_cloop; | ||
384 | extern BlockDriver bdrv_dmg; | 384 | extern BlockDriver bdrv_dmg; |
385 | extern BlockDriver bdrv_bochs; | 385 | extern BlockDriver bdrv_bochs; |
386 | extern BlockDriver bdrv_vpc; | 386 | extern BlockDriver bdrv_vpc; |
387 | +extern BlockDriver bdrv_vvfat; | ||
387 | 388 | ||
388 | void bdrv_init(void); | 389 | void bdrv_init(void); |
389 | BlockDriver *bdrv_find_format(const char *format_name); | 390 | BlockDriver *bdrv_find_format(const char *format_name); |