xref: /qemu/block/vvfat.c (revision e0ee3a8f)
17ad9be64SKevin Wolf /* vim:set shiftwidth=4 ts=4: */
2019d6b8fSAnthony Liguori /*
3019d6b8fSAnthony Liguori  * QEMU Block driver for virtual VFAT (shadows a local directory)
4019d6b8fSAnthony Liguori  *
5019d6b8fSAnthony Liguori  * Copyright (c) 2004,2005 Johannes E. Schindelin
6019d6b8fSAnthony Liguori  *
7019d6b8fSAnthony Liguori  * Permission is hereby granted, free of charge, to any person obtaining a copy
8019d6b8fSAnthony Liguori  * of this software and associated documentation files (the "Software"), to deal
9019d6b8fSAnthony Liguori  * in the Software without restriction, including without limitation the rights
10019d6b8fSAnthony Liguori  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11019d6b8fSAnthony Liguori  * copies of the Software, and to permit persons to whom the Software is
12019d6b8fSAnthony Liguori  * furnished to do so, subject to the following conditions:
13019d6b8fSAnthony Liguori  *
14019d6b8fSAnthony Liguori  * The above copyright notice and this permission notice shall be included in
15019d6b8fSAnthony Liguori  * all copies or substantial portions of the Software.
16019d6b8fSAnthony Liguori  *
17019d6b8fSAnthony Liguori  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18019d6b8fSAnthony Liguori  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19019d6b8fSAnthony Liguori  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20019d6b8fSAnthony Liguori  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21019d6b8fSAnthony Liguori  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22019d6b8fSAnthony Liguori  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23019d6b8fSAnthony Liguori  * THE SOFTWARE.
24019d6b8fSAnthony Liguori  */
25452fcdbcSMarkus Armbruster 
2680c71a24SPeter Maydell #include "qemu/osdep.h"
27019d6b8fSAnthony Liguori #include <dirent.h>
28c2632994SBin Meng #include <glib/gstdio.h>
29da34e65cSMarkus Armbruster #include "qapi/error.h"
30e2c1c34fSMarkus Armbruster #include "block/block-io.h"
31737e150eSPaolo Bonzini #include "block/block_int.h"
32609f45eaSMax Reitz #include "block/qdict.h"
331de7afc9SPaolo Bonzini #include "qemu/module.h"
34922a01a0SMarkus Armbruster #include "qemu/option.h"
3558369e22SPaolo Bonzini #include "qemu/bswap.h"
36795c40b8SJuan Quintela #include "migration/blocker.h"
37452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h"
38d49b6836SMarkus Armbruster #include "qapi/qmp/qstring.h"
39856dfd8aSMarkus Armbruster #include "qemu/ctype.h"
40f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
412ab4b135SAlistair Francis #include "qemu/error-report.h"
42019d6b8fSAnthony Liguori 
43019d6b8fSAnthony Liguori #ifndef S_IWGRP
44019d6b8fSAnthony Liguori #define S_IWGRP 0
45019d6b8fSAnthony Liguori #endif
46019d6b8fSAnthony Liguori #ifndef S_IWOTH
47019d6b8fSAnthony Liguori #define S_IWOTH 0
48019d6b8fSAnthony Liguori #endif
49019d6b8fSAnthony Liguori 
50019d6b8fSAnthony Liguori /* TODO: add ":bootsector=blabla.img:" */
51019d6b8fSAnthony Liguori /* LATER TODO: add automatic boot sector generation from
52019d6b8fSAnthony Liguori     BOOTEASY.ASM and Ranish Partition Manager
53019d6b8fSAnthony Liguori     Note that DOS assumes the system files to be the first files in the
54019d6b8fSAnthony Liguori     file system (test if the boot sector still relies on that fact)! */
55019d6b8fSAnthony Liguori /* MAYBE TODO: write block-visofs.c */
56019d6b8fSAnthony Liguori /* TODO: call try_commit() only after a timeout */
57019d6b8fSAnthony Liguori 
58019d6b8fSAnthony Liguori /* #define DEBUG */
59019d6b8fSAnthony Liguori 
60019d6b8fSAnthony Liguori #ifdef DEBUG
61019d6b8fSAnthony Liguori 
62019d6b8fSAnthony Liguori #define DLOG(a) a
63019d6b8fSAnthony Liguori 
64019d6b8fSAnthony Liguori static void checkpoint(void);
65019d6b8fSAnthony Liguori 
66019d6b8fSAnthony Liguori #else
67019d6b8fSAnthony Liguori 
68019d6b8fSAnthony Liguori #define DLOG(a)
69019d6b8fSAnthony Liguori 
70019d6b8fSAnthony Liguori #endif
71019d6b8fSAnthony Liguori 
7263d261cbSHervé Poussineau /* bootsector OEM name. see related compatibility problems at:
7363d261cbSHervé Poussineau  * https://jdebp.eu/FGA/volume-boot-block-oem-name-field.html
7463d261cbSHervé Poussineau  * http://seasip.info/Misc/oemid.html
7563d261cbSHervé Poussineau  */
7663d261cbSHervé Poussineau #define BOOTSECTOR_OEM_NAME "MSWIN4.1"
7763d261cbSHervé Poussineau 
788c4517fdSHervé Poussineau #define DIR_DELETED 0xe5
798c4517fdSHervé Poussineau #define DIR_KANJI DIR_DELETED
808c4517fdSHervé Poussineau #define DIR_KANJI_FAKE 0x05
818c4517fdSHervé Poussineau #define DIR_FREE 0x00
828c4517fdSHervé Poussineau 
83019d6b8fSAnthony Liguori /* dynamic array functions */
84c227f099SAnthony Liguori typedef struct array_t {
85019d6b8fSAnthony Liguori     char* pointer;
86019d6b8fSAnthony Liguori     unsigned int size,next,item_size;
87c227f099SAnthony Liguori } array_t;
88019d6b8fSAnthony Liguori 
array_init(array_t * array,unsigned int item_size)89c227f099SAnthony Liguori static inline void array_init(array_t* array,unsigned int item_size)
90019d6b8fSAnthony Liguori {
91019d6b8fSAnthony Liguori     array->pointer = NULL;
92019d6b8fSAnthony Liguori     array->size=0;
93019d6b8fSAnthony Liguori     array->next=0;
94019d6b8fSAnthony Liguori     array->item_size=item_size;
95019d6b8fSAnthony Liguori }
96019d6b8fSAnthony Liguori 
array_free(array_t * array)97c227f099SAnthony Liguori static inline void array_free(array_t* array)
98019d6b8fSAnthony Liguori {
99ce137829SStefan Weil     g_free(array->pointer);
100019d6b8fSAnthony Liguori     array->size=array->next=0;
101019d6b8fSAnthony Liguori }
102019d6b8fSAnthony Liguori 
103019d6b8fSAnthony Liguori /* does not automatically grow */
array_get(array_t * array,unsigned int index)104c227f099SAnthony Liguori static inline void* array_get(array_t* array,unsigned int index) {
105019d6b8fSAnthony Liguori     assert(index < array->next);
1068d9401c2SLiam Merwick     assert(array->pointer);
107019d6b8fSAnthony Liguori     return array->pointer + index * array->item_size;
108019d6b8fSAnthony Liguori }
109019d6b8fSAnthony Liguori 
array_ensure_allocated(array_t * array,int index)1108d9401c2SLiam Merwick static inline void array_ensure_allocated(array_t *array, int index)
111019d6b8fSAnthony Liguori {
112019d6b8fSAnthony Liguori     if((index + 1) * array->item_size > array->size) {
113019d6b8fSAnthony Liguori         int new_size = (index + 32) * array->item_size;
1147267c094SAnthony Liguori         array->pointer = g_realloc(array->pointer, new_size);
1158d9401c2SLiam Merwick         assert(array->pointer);
116f80256b7SHervé Poussineau         memset(array->pointer + array->size, 0, new_size - array->size);
117019d6b8fSAnthony Liguori         array->size = new_size;
118019d6b8fSAnthony Liguori         array->next = index + 1;
119019d6b8fSAnthony Liguori     }
120019d6b8fSAnthony Liguori }
121019d6b8fSAnthony Liguori 
array_get_next(array_t * array)122c227f099SAnthony Liguori static inline void* array_get_next(array_t* array) {
123019d6b8fSAnthony Liguori     unsigned int next = array->next;
124019d6b8fSAnthony Liguori 
1258d9401c2SLiam Merwick     array_ensure_allocated(array, next);
126019d6b8fSAnthony Liguori     array->next = next + 1;
1279be38598SEduardo Habkost     return array_get(array, next);
128019d6b8fSAnthony Liguori }
129019d6b8fSAnthony Liguori 
array_insert(array_t * array,unsigned int index,unsigned int count)130c227f099SAnthony Liguori static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
131019d6b8fSAnthony Liguori     if((array->next+count)*array->item_size>array->size) {
132019d6b8fSAnthony Liguori         int increment=count*array->item_size;
1337267c094SAnthony Liguori         array->pointer=g_realloc(array->pointer,array->size+increment);
134019d6b8fSAnthony Liguori         if(!array->pointer)
135019d6b8fSAnthony Liguori             return NULL;
136019d6b8fSAnthony Liguori         array->size+=increment;
137019d6b8fSAnthony Liguori     }
138019d6b8fSAnthony Liguori     memmove(array->pointer+(index+count)*array->item_size,
139019d6b8fSAnthony Liguori                 array->pointer+index*array->item_size,
140019d6b8fSAnthony Liguori                 (array->next-index)*array->item_size);
141019d6b8fSAnthony Liguori     array->next+=count;
142019d6b8fSAnthony Liguori     return array->pointer+index*array->item_size;
143019d6b8fSAnthony Liguori }
144019d6b8fSAnthony Liguori 
array_remove_slice(array_t * array,int index,int count)145c227f099SAnthony Liguori static inline int array_remove_slice(array_t* array,int index, int count)
146019d6b8fSAnthony Liguori {
147019d6b8fSAnthony Liguori     assert(index >=0);
148019d6b8fSAnthony Liguori     assert(count > 0);
149019d6b8fSAnthony Liguori     assert(index + count <= array->next);
1503dfa23b9SKevin Wolf 
1513dfa23b9SKevin Wolf     memmove(array->pointer + index * array->item_size,
1523dfa23b9SKevin Wolf             array->pointer + (index + count) * array->item_size,
1533dfa23b9SKevin Wolf             (array->next - index - count) * array->item_size);
1543dfa23b9SKevin Wolf 
155019d6b8fSAnthony Liguori     array->next -= count;
156019d6b8fSAnthony Liguori     return 0;
157019d6b8fSAnthony Liguori }
158019d6b8fSAnthony Liguori 
array_remove(array_t * array,int index)159c227f099SAnthony Liguori static int array_remove(array_t* array,int index)
160019d6b8fSAnthony Liguori {
161019d6b8fSAnthony Liguori     return array_remove_slice(array, index, 1);
162019d6b8fSAnthony Liguori }
163019d6b8fSAnthony Liguori 
164019d6b8fSAnthony Liguori /* return the index for a given member */
array_index(array_t * array,void * pointer)165c227f099SAnthony Liguori static int array_index(array_t* array, void* pointer)
166019d6b8fSAnthony Liguori {
167019d6b8fSAnthony Liguori     size_t offset = (char*)pointer - array->pointer;
168019d6b8fSAnthony Liguori     assert((offset % array->item_size) == 0);
169019d6b8fSAnthony Liguori     assert(offset/array->item_size < array->next);
170019d6b8fSAnthony Liguori     return offset/array->item_size;
171019d6b8fSAnthony Liguori }
172019d6b8fSAnthony Liguori 
173019d6b8fSAnthony Liguori /* These structures are used to fake a disk and the VFAT filesystem.
174541dc0d4SStefan Weil  * For this reason we need to use QEMU_PACKED. */
175019d6b8fSAnthony Liguori 
176c227f099SAnthony Liguori typedef struct bootsector_t {
177019d6b8fSAnthony Liguori     uint8_t jump[3];
178019d6b8fSAnthony Liguori     uint8_t name[8];
179019d6b8fSAnthony Liguori     uint16_t sector_size;
180019d6b8fSAnthony Liguori     uint8_t sectors_per_cluster;
181019d6b8fSAnthony Liguori     uint16_t reserved_sectors;
182019d6b8fSAnthony Liguori     uint8_t number_of_fats;
183019d6b8fSAnthony Liguori     uint16_t root_entries;
184019d6b8fSAnthony Liguori     uint16_t total_sectors16;
185019d6b8fSAnthony Liguori     uint8_t media_type;
186019d6b8fSAnthony Liguori     uint16_t sectors_per_fat;
187019d6b8fSAnthony Liguori     uint16_t sectors_per_track;
188019d6b8fSAnthony Liguori     uint16_t number_of_heads;
189019d6b8fSAnthony Liguori     uint32_t hidden_sectors;
190019d6b8fSAnthony Liguori     uint32_t total_sectors;
191019d6b8fSAnthony Liguori     union {
192019d6b8fSAnthony Liguori         struct {
193019d6b8fSAnthony Liguori             uint8_t drive_number;
19492e28d82SHervé Poussineau             uint8_t reserved1;
195019d6b8fSAnthony Liguori             uint8_t signature;
196019d6b8fSAnthony Liguori             uint32_t id;
197019d6b8fSAnthony Liguori             uint8_t volume_label[11];
19892e28d82SHervé Poussineau             uint8_t fat_type[8];
19992e28d82SHervé Poussineau             uint8_t ignored[0x1c0];
200541dc0d4SStefan Weil         } QEMU_PACKED fat16;
201019d6b8fSAnthony Liguori         struct {
202019d6b8fSAnthony Liguori             uint32_t sectors_per_fat;
203019d6b8fSAnthony Liguori             uint16_t flags;
204019d6b8fSAnthony Liguori             uint8_t major,minor;
20592e28d82SHervé Poussineau             uint32_t first_cluster_of_root_dir;
206019d6b8fSAnthony Liguori             uint16_t info_sector;
207019d6b8fSAnthony Liguori             uint16_t backup_boot_sector;
20892e28d82SHervé Poussineau             uint8_t reserved[12];
20992e28d82SHervé Poussineau             uint8_t drive_number;
21092e28d82SHervé Poussineau             uint8_t reserved1;
21192e28d82SHervé Poussineau             uint8_t signature;
21292e28d82SHervé Poussineau             uint32_t id;
21392e28d82SHervé Poussineau             uint8_t volume_label[11];
21492e28d82SHervé Poussineau             uint8_t fat_type[8];
21592e28d82SHervé Poussineau             uint8_t ignored[0x1a4];
216541dc0d4SStefan Weil         } QEMU_PACKED fat32;
217019d6b8fSAnthony Liguori     } u;
218019d6b8fSAnthony Liguori     uint8_t magic[2];
219541dc0d4SStefan Weil } QEMU_PACKED bootsector_t;
220019d6b8fSAnthony Liguori 
221019d6b8fSAnthony Liguori typedef struct {
222019d6b8fSAnthony Liguori     uint8_t head;
223019d6b8fSAnthony Liguori     uint8_t sector;
224019d6b8fSAnthony Liguori     uint8_t cylinder;
225c227f099SAnthony Liguori } mbr_chs_t;
226019d6b8fSAnthony Liguori 
227c227f099SAnthony Liguori typedef struct partition_t {
228019d6b8fSAnthony Liguori     uint8_t attributes; /* 0x80 = bootable */
229c227f099SAnthony Liguori     mbr_chs_t start_CHS;
230019d6b8fSAnthony Liguori     uint8_t   fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
231c227f099SAnthony Liguori     mbr_chs_t end_CHS;
232019d6b8fSAnthony Liguori     uint32_t start_sector_long;
233019d6b8fSAnthony Liguori     uint32_t length_sector_long;
234541dc0d4SStefan Weil } QEMU_PACKED partition_t;
235019d6b8fSAnthony Liguori 
236c227f099SAnthony Liguori typedef struct mbr_t {
237019d6b8fSAnthony Liguori     uint8_t ignored[0x1b8];
238019d6b8fSAnthony Liguori     uint32_t nt_id;
239019d6b8fSAnthony Liguori     uint8_t ignored2[2];
240c227f099SAnthony Liguori     partition_t partition[4];
241019d6b8fSAnthony Liguori     uint8_t magic[2];
242541dc0d4SStefan Weil } QEMU_PACKED mbr_t;
243019d6b8fSAnthony Liguori 
244c227f099SAnthony Liguori typedef struct direntry_t {
245f671d173SStefan Weil     uint8_t name[8 + 3];
246019d6b8fSAnthony Liguori     uint8_t attributes;
247019d6b8fSAnthony Liguori     uint8_t reserved[2];
248019d6b8fSAnthony Liguori     uint16_t ctime;
249019d6b8fSAnthony Liguori     uint16_t cdate;
250019d6b8fSAnthony Liguori     uint16_t adate;
251019d6b8fSAnthony Liguori     uint16_t begin_hi;
252019d6b8fSAnthony Liguori     uint16_t mtime;
253019d6b8fSAnthony Liguori     uint16_t mdate;
254019d6b8fSAnthony Liguori     uint16_t begin;
255019d6b8fSAnthony Liguori     uint32_t size;
256541dc0d4SStefan Weil } QEMU_PACKED direntry_t;
257019d6b8fSAnthony Liguori 
258019d6b8fSAnthony Liguori /* this structure are used to transparently access the files */
259019d6b8fSAnthony Liguori 
260c227f099SAnthony Liguori typedef struct mapping_t {
261019d6b8fSAnthony Liguori     /* begin is the first cluster, end is the last+1 */
262019d6b8fSAnthony Liguori     uint32_t begin,end;
263019d6b8fSAnthony Liguori     /* as s->directory is growable, no pointer may be used here */
264019d6b8fSAnthony Liguori     unsigned int dir_index;
265019d6b8fSAnthony Liguori     /* the clusters of a file may be in any order; this points to the first */
266019d6b8fSAnthony Liguori     int first_mapping_index;
267019d6b8fSAnthony Liguori     union {
268019d6b8fSAnthony Liguori         /* offset is
269019d6b8fSAnthony Liguori          * - the offset in the file (in clusters) for a file, or
270ad05b318SHervé Poussineau          * - the next cluster of the directory for a directory
271019d6b8fSAnthony Liguori          */
272019d6b8fSAnthony Liguori         struct {
273019d6b8fSAnthony Liguori             uint32_t offset;
274019d6b8fSAnthony Liguori         } file;
275019d6b8fSAnthony Liguori         struct {
276019d6b8fSAnthony Liguori             int parent_mapping_index;
277019d6b8fSAnthony Liguori             int first_dir_index;
278019d6b8fSAnthony Liguori         } dir;
279019d6b8fSAnthony Liguori     } info;
280019d6b8fSAnthony Liguori     /* path contains the full path, i.e. it always starts with s->path */
281019d6b8fSAnthony Liguori     char* path;
282019d6b8fSAnthony Liguori 
283ad05b318SHervé Poussineau     enum {
284ad05b318SHervé Poussineau         MODE_UNDEFINED = 0,
285ad05b318SHervé Poussineau         MODE_NORMAL = 1,
286ad05b318SHervé Poussineau         MODE_MODIFIED = 2,
287ad05b318SHervé Poussineau         MODE_DIRECTORY = 4,
288ad05b318SHervé Poussineau         MODE_DELETED = 8,
289ad05b318SHervé Poussineau     } mode;
290019d6b8fSAnthony Liguori     int read_only;
291c227f099SAnthony Liguori } mapping_t;
292019d6b8fSAnthony Liguori 
293019d6b8fSAnthony Liguori #ifdef DEBUG
294c227f099SAnthony Liguori static void print_direntry(const struct direntry_t*);
295c227f099SAnthony Liguori static void print_mapping(const struct mapping_t* mapping);
296019d6b8fSAnthony Liguori #endif
297019d6b8fSAnthony Liguori 
298019d6b8fSAnthony Liguori /* here begins the real VVFAT driver */
299019d6b8fSAnthony Liguori 
300019d6b8fSAnthony Liguori typedef struct BDRVVVFATState {
301848c66e8SPaolo Bonzini     CoMutex lock;
302019d6b8fSAnthony Liguori     BlockDriverState* bs; /* pointer to parent */
303019d6b8fSAnthony Liguori     unsigned char first_sectors[0x40*0x200];
304019d6b8fSAnthony Liguori 
305019d6b8fSAnthony Liguori     int fat_type; /* 16 or 32 */
306c227f099SAnthony Liguori     array_t fat,directory,mapping;
307d5941ddaSWolfgang Bumiller     char volume_label[11];
308019d6b8fSAnthony Liguori 
3094dc705dcSHervé Poussineau     uint32_t offset_to_bootsector; /* 0 for floppy, 0x3f for disk */
3104dc705dcSHervé Poussineau 
311019d6b8fSAnthony Liguori     unsigned int cluster_size;
312019d6b8fSAnthony Liguori     unsigned int sectors_per_cluster;
313019d6b8fSAnthony Liguori     unsigned int sectors_per_fat;
314019d6b8fSAnthony Liguori     uint32_t last_cluster_of_root_directory;
3156817efeaSHervé Poussineau     /* how many entries are available in root directory (0 for FAT32) */
3166817efeaSHervé Poussineau     uint16_t root_entries;
317019d6b8fSAnthony Liguori     uint32_t sector_count; /* total number of sectors of the partition */
318019d6b8fSAnthony Liguori     uint32_t cluster_count; /* total number of clusters of this partition */
319019d6b8fSAnthony Liguori     uint32_t max_fat_value;
3204dc705dcSHervé Poussineau     uint32_t offset_to_fat;
3214dc705dcSHervé Poussineau     uint32_t offset_to_root_dir;
322019d6b8fSAnthony Liguori 
323019d6b8fSAnthony Liguori     int current_fd;
324c227f099SAnthony Liguori     mapping_t* current_mapping;
325019d6b8fSAnthony Liguori     unsigned char* cluster; /* points to current cluster */
326019d6b8fSAnthony Liguori     unsigned char* cluster_buffer; /* points to a buffer to hold temp data */
327019d6b8fSAnthony Liguori     unsigned int current_cluster;
328019d6b8fSAnthony Liguori 
329019d6b8fSAnthony Liguori     /* write support */
330019d6b8fSAnthony Liguori     char* qcow_filename;
331eecc7747SKevin Wolf     BdrvChild* qcow;
332019d6b8fSAnthony Liguori     void* fat2;
333019d6b8fSAnthony Liguori     char* used_clusters;
334c227f099SAnthony Liguori     array_t commits;
335019d6b8fSAnthony Liguori     const char* path;
336019d6b8fSAnthony Liguori     int downcase_short_names;
3373397f0cbSKevin Wolf 
3383397f0cbSKevin Wolf     Error *migration_blocker;
339019d6b8fSAnthony Liguori } BDRVVVFATState;
340019d6b8fSAnthony Liguori 
341019d6b8fSAnthony Liguori /* take the sector position spos and convert it to Cylinder/Head/Sector position
342019d6b8fSAnthony Liguori  * if the position is outside the specified geometry, fill maximum value for CHS
343019d6b8fSAnthony Liguori  * and return 1 to signal overflow.
344019d6b8fSAnthony Liguori  */
sector2CHS(mbr_chs_t * chs,int spos,int cyls,int heads,int secs)3454480e0f9SMarkus Armbruster static int sector2CHS(mbr_chs_t *chs, int spos, int cyls, int heads, int secs)
3464480e0f9SMarkus Armbruster {
347019d6b8fSAnthony Liguori     int head,sector;
3484480e0f9SMarkus Armbruster     sector   = spos % secs;  spos /= secs;
3494480e0f9SMarkus Armbruster     head     = spos % heads; spos /= heads;
3504480e0f9SMarkus Armbruster     if (spos >= cyls) {
351019d6b8fSAnthony Liguori         /* Overflow,
352019d6b8fSAnthony Liguori         it happens if 32bit sector positions are used, while CHS is only 24bit.
353019d6b8fSAnthony Liguori         Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
354019d6b8fSAnthony Liguori         chs->head     = 0xFF;
355019d6b8fSAnthony Liguori         chs->sector   = 0xFF;
356019d6b8fSAnthony Liguori         chs->cylinder = 0xFF;
357019d6b8fSAnthony Liguori         return 1;
358019d6b8fSAnthony Liguori     }
359019d6b8fSAnthony Liguori     chs->head     = (uint8_t)head;
360019d6b8fSAnthony Liguori     chs->sector   = (uint8_t)( (sector+1) | ((spos>>8)<<6) );
361019d6b8fSAnthony Liguori     chs->cylinder = (uint8_t)spos;
362019d6b8fSAnthony Liguori     return 0;
363019d6b8fSAnthony Liguori }
364019d6b8fSAnthony Liguori 
init_mbr(BDRVVVFATState * s,int cyls,int heads,int secs)3654480e0f9SMarkus Armbruster static void init_mbr(BDRVVVFATState *s, int cyls, int heads, int secs)
366019d6b8fSAnthony Liguori {
367019d6b8fSAnthony Liguori     /* TODO: if the files mbr.img and bootsect.img exist, use them */
368c227f099SAnthony Liguori     mbr_t* real_mbr=(mbr_t*)s->first_sectors;
369c227f099SAnthony Liguori     partition_t* partition = &(real_mbr->partition[0]);
370019d6b8fSAnthony Liguori     int lba;
371019d6b8fSAnthony Liguori 
372019d6b8fSAnthony Liguori     memset(s->first_sectors,0,512);
373019d6b8fSAnthony Liguori 
374019d6b8fSAnthony Liguori     /* Win NT Disk Signature */
375019d6b8fSAnthony Liguori     real_mbr->nt_id= cpu_to_le32(0xbe1afdfa);
376019d6b8fSAnthony Liguori 
377019d6b8fSAnthony Liguori     partition->attributes=0x80; /* bootable */
378019d6b8fSAnthony Liguori 
379019d6b8fSAnthony Liguori     /* LBA is used when partition is outside the CHS geometry */
3804dc705dcSHervé Poussineau     lba  = sector2CHS(&partition->start_CHS, s->offset_to_bootsector,
3814480e0f9SMarkus Armbruster                      cyls, heads, secs);
3824480e0f9SMarkus Armbruster     lba |= sector2CHS(&partition->end_CHS,   s->bs->total_sectors - 1,
3834480e0f9SMarkus Armbruster                      cyls, heads, secs);
384019d6b8fSAnthony Liguori 
385019d6b8fSAnthony Liguori     /*LBA partitions are identified only by start/length_sector_long not by CHS*/
3864dc705dcSHervé Poussineau     partition->start_sector_long  = cpu_to_le32(s->offset_to_bootsector);
387f91cbefeSMarkus Armbruster     partition->length_sector_long = cpu_to_le32(s->bs->total_sectors
3884dc705dcSHervé Poussineau                                                 - s->offset_to_bootsector);
389019d6b8fSAnthony Liguori 
390019d6b8fSAnthony Liguori     /* FAT12/FAT16/FAT32 */
391019d6b8fSAnthony Liguori     /* DOS uses different types when partition is LBA,
392019d6b8fSAnthony Liguori        probably to prevent older versions from using CHS on them */
393019d6b8fSAnthony Liguori     partition->fs_type = s->fat_type == 12 ? 0x1 :
394019d6b8fSAnthony Liguori                          s->fat_type == 16 ? (lba ? 0xe : 0x06) :
3955f5b29dfSHervé Poussineau                        /*s->fat_type == 32*/ (lba ? 0xc : 0x0b);
396019d6b8fSAnthony Liguori 
397019d6b8fSAnthony Liguori     real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
398019d6b8fSAnthony Liguori }
399019d6b8fSAnthony Liguori 
400019d6b8fSAnthony Liguori /* direntry functions */
401019d6b8fSAnthony Liguori 
create_long_filename(BDRVVVFATState * s,const char * filename)40209ec4119SHervé Poussineau static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename)
403019d6b8fSAnthony Liguori {
40409ec4119SHervé Poussineau     int number_of_entries, i;
40509ec4119SHervé Poussineau     glong length;
40609ec4119SHervé Poussineau     direntry_t *entry;
40709ec4119SHervé Poussineau 
40809ec4119SHervé Poussineau     gunichar2 *longname = g_utf8_to_utf16(filename, -1, NULL, &length, NULL);
40909ec4119SHervé Poussineau     if (!longname) {
41009ec4119SHervé Poussineau         fprintf(stderr, "vvfat: invalid UTF-8 name: %s\n", filename);
41109ec4119SHervé Poussineau         return NULL;
412019d6b8fSAnthony Liguori     }
413019d6b8fSAnthony Liguori 
41478ee96deSMarc-André Lureau     number_of_entries = DIV_ROUND_UP(length * 2, 26);
415019d6b8fSAnthony Liguori 
416019d6b8fSAnthony Liguori     for(i=0;i<number_of_entries;i++) {
417019d6b8fSAnthony Liguori         entry=array_get_next(&(s->directory));
418019d6b8fSAnthony Liguori         entry->attributes=0xf;
419019d6b8fSAnthony Liguori         entry->reserved[0]=0;
420019d6b8fSAnthony Liguori         entry->begin=0;
421019d6b8fSAnthony Liguori         entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
422019d6b8fSAnthony Liguori     }
423019d6b8fSAnthony Liguori     for(i=0;i<26*number_of_entries;i++) {
424019d6b8fSAnthony Liguori         int offset=(i%26);
425019d6b8fSAnthony Liguori         if(offset<10) offset=1+offset;
426019d6b8fSAnthony Liguori         else if(offset<22) offset=14+offset-10;
427019d6b8fSAnthony Liguori         else offset=28+offset-22;
428019d6b8fSAnthony Liguori         entry=array_get(&(s->directory),s->directory.next-1-(i/26));
42909ec4119SHervé Poussineau         if (i >= 2 * length + 2) {
43009ec4119SHervé Poussineau             entry->name[offset] = 0xff;
43109ec4119SHervé Poussineau         } else if (i % 2 == 0) {
43209ec4119SHervé Poussineau             entry->name[offset] = longname[i / 2] & 0xff;
43309ec4119SHervé Poussineau         } else {
43409ec4119SHervé Poussineau             entry->name[offset] = longname[i / 2] >> 8;
435019d6b8fSAnthony Liguori         }
43609ec4119SHervé Poussineau     }
43709ec4119SHervé Poussineau     g_free(longname);
438019d6b8fSAnthony Liguori     return array_get(&(s->directory),s->directory.next-number_of_entries);
439019d6b8fSAnthony Liguori }
440019d6b8fSAnthony Liguori 
is_free(const direntry_t * direntry)441c227f099SAnthony Liguori static char is_free(const direntry_t* direntry)
442019d6b8fSAnthony Liguori {
4438c4517fdSHervé Poussineau     return direntry->name[0] == DIR_DELETED || direntry->name[0] == DIR_FREE;
444019d6b8fSAnthony Liguori }
445019d6b8fSAnthony Liguori 
is_volume_label(const direntry_t * direntry)446c227f099SAnthony Liguori static char is_volume_label(const direntry_t* direntry)
447019d6b8fSAnthony Liguori {
448019d6b8fSAnthony Liguori     return direntry->attributes == 0x28;
449019d6b8fSAnthony Liguori }
450019d6b8fSAnthony Liguori 
is_long_name(const direntry_t * direntry)451c227f099SAnthony Liguori static char is_long_name(const direntry_t* direntry)
452019d6b8fSAnthony Liguori {
453019d6b8fSAnthony Liguori     return direntry->attributes == 0xf;
454019d6b8fSAnthony Liguori }
455019d6b8fSAnthony Liguori 
is_short_name(const direntry_t * direntry)456c227f099SAnthony Liguori static char is_short_name(const direntry_t* direntry)
457019d6b8fSAnthony Liguori {
458019d6b8fSAnthony Liguori     return !is_volume_label(direntry) && !is_long_name(direntry)
459019d6b8fSAnthony Liguori         && !is_free(direntry);
460019d6b8fSAnthony Liguori }
461019d6b8fSAnthony Liguori 
is_directory(const direntry_t * direntry)462c227f099SAnthony Liguori static char is_directory(const direntry_t* direntry)
463019d6b8fSAnthony Liguori {
4648c4517fdSHervé Poussineau     return direntry->attributes & 0x10 && direntry->name[0] != DIR_DELETED;
465019d6b8fSAnthony Liguori }
466019d6b8fSAnthony Liguori 
is_dot(const direntry_t * direntry)467c227f099SAnthony Liguori static inline char is_dot(const direntry_t* direntry)
468019d6b8fSAnthony Liguori {
469019d6b8fSAnthony Liguori     return is_short_name(direntry) && direntry->name[0] == '.';
470019d6b8fSAnthony Liguori }
471019d6b8fSAnthony Liguori 
is_file(const direntry_t * direntry)472c227f099SAnthony Liguori static char is_file(const direntry_t* direntry)
473019d6b8fSAnthony Liguori {
474019d6b8fSAnthony Liguori     return is_short_name(direntry) && !is_directory(direntry);
475019d6b8fSAnthony Liguori }
476019d6b8fSAnthony Liguori 
begin_of_direntry(const direntry_t * direntry)477c227f099SAnthony Liguori static inline uint32_t begin_of_direntry(const direntry_t* direntry)
478019d6b8fSAnthony Liguori {
479019d6b8fSAnthony Liguori     return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
480019d6b8fSAnthony Liguori }
481019d6b8fSAnthony Liguori 
filesize_of_direntry(const direntry_t * direntry)482c227f099SAnthony Liguori static inline uint32_t filesize_of_direntry(const direntry_t* direntry)
483019d6b8fSAnthony Liguori {
484019d6b8fSAnthony Liguori     return le32_to_cpu(direntry->size);
485019d6b8fSAnthony Liguori }
486019d6b8fSAnthony Liguori 
set_begin_of_direntry(direntry_t * direntry,uint32_t begin)487c227f099SAnthony Liguori static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
488019d6b8fSAnthony Liguori {
489019d6b8fSAnthony Liguori     direntry->begin = cpu_to_le16(begin & 0xffff);
490019d6b8fSAnthony Liguori     direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
491019d6b8fSAnthony Liguori }
492019d6b8fSAnthony Liguori 
valid_filename(const unsigned char * name)493c79e243eSKevin Wolf static bool valid_filename(const unsigned char *name)
494c79e243eSKevin Wolf {
495c79e243eSKevin Wolf     unsigned char c;
496c79e243eSKevin Wolf     if (!strcmp((const char*)name, ".") || !strcmp((const char*)name, "..")) {
497c79e243eSKevin Wolf         return false;
498c79e243eSKevin Wolf     }
499c79e243eSKevin Wolf     for (; (c = *name); name++) {
500c79e243eSKevin Wolf         if (!((c >= '0' && c <= '9') ||
501c79e243eSKevin Wolf               (c >= 'A' && c <= 'Z') ||
502c79e243eSKevin Wolf               (c >= 'a' && c <= 'z') ||
503c79e243eSKevin Wolf               c > 127 ||
504c79e243eSKevin Wolf               strchr(" $%'-_@~`!(){}^#&.+,;=[]", c) != NULL))
505c79e243eSKevin Wolf         {
506c79e243eSKevin Wolf             return false;
507c79e243eSKevin Wolf         }
508c79e243eSKevin Wolf     }
509c79e243eSKevin Wolf     return true;
510c79e243eSKevin Wolf }
511c79e243eSKevin Wolf 
to_valid_short_char(gunichar c)5120c36111fSHervé Poussineau static uint8_t to_valid_short_char(gunichar c)
5130c36111fSHervé Poussineau {
5140c36111fSHervé Poussineau     c = g_unichar_toupper(c);
5150c36111fSHervé Poussineau     if ((c >= '0' && c <= '9') ||
5160c36111fSHervé Poussineau         (c >= 'A' && c <= 'Z') ||
517c79e243eSKevin Wolf         strchr("$%'-_@~`!(){}^#&", c) != NULL) {
5180c36111fSHervé Poussineau         return c;
5190c36111fSHervé Poussineau     } else {
5200c36111fSHervé Poussineau         return 0;
5210c36111fSHervé Poussineau     }
5220c36111fSHervé Poussineau }
5230c36111fSHervé Poussineau 
create_short_filename(BDRVVVFATState * s,const char * filename,unsigned int directory_start)5240c36111fSHervé Poussineau static direntry_t *create_short_filename(BDRVVVFATState *s,
525339cebccSHervé Poussineau                                          const char *filename,
526339cebccSHervé Poussineau                                          unsigned int directory_start)
5270c36111fSHervé Poussineau {
528339cebccSHervé Poussineau     int i, j = 0;
5290c36111fSHervé Poussineau     direntry_t *entry = array_get_next(&(s->directory));
5300c36111fSHervé Poussineau     const gchar *p, *last_dot = NULL;
5310c36111fSHervé Poussineau     gunichar c;
5320c36111fSHervé Poussineau     bool lossy_conversion = false;
5337c8730d4SMax Reitz     char tail[8];
5340c36111fSHervé Poussineau 
5350c36111fSHervé Poussineau     if (!entry) {
5360c36111fSHervé Poussineau         return NULL;
5370c36111fSHervé Poussineau     }
5380c36111fSHervé Poussineau     memset(entry->name, 0x20, sizeof(entry->name));
5390c36111fSHervé Poussineau 
5400c36111fSHervé Poussineau     /* copy filename and search last dot */
5410c36111fSHervé Poussineau     for (p = filename; ; p = g_utf8_next_char(p)) {
5420c36111fSHervé Poussineau         c = g_utf8_get_char(p);
5430c36111fSHervé Poussineau         if (c == '\0') {
5440c36111fSHervé Poussineau             break;
5450c36111fSHervé Poussineau         } else if (c == '.') {
5460c36111fSHervé Poussineau             if (j == 0) {
5470c36111fSHervé Poussineau                 /* '.' at start of filename */
5480c36111fSHervé Poussineau                 lossy_conversion = true;
5490c36111fSHervé Poussineau             } else {
5500c36111fSHervé Poussineau                 if (last_dot) {
5510c36111fSHervé Poussineau                     lossy_conversion = true;
5520c36111fSHervé Poussineau                 }
5530c36111fSHervé Poussineau                 last_dot = p;
5540c36111fSHervé Poussineau             }
5550c36111fSHervé Poussineau         } else if (!last_dot) {
5560c36111fSHervé Poussineau             /* first part of the name; copy it */
5570c36111fSHervé Poussineau             uint8_t v = to_valid_short_char(c);
5580c36111fSHervé Poussineau             if (j < 8 && v) {
5590c36111fSHervé Poussineau                 entry->name[j++] = v;
5600c36111fSHervé Poussineau             } else {
5610c36111fSHervé Poussineau                 lossy_conversion = true;
5620c36111fSHervé Poussineau             }
5630c36111fSHervé Poussineau         }
5640c36111fSHervé Poussineau     }
5650c36111fSHervé Poussineau 
5660c36111fSHervé Poussineau     /* copy extension (if any) */
5670c36111fSHervé Poussineau     if (last_dot) {
5680c36111fSHervé Poussineau         j = 0;
5690c36111fSHervé Poussineau         for (p = g_utf8_next_char(last_dot); ; p = g_utf8_next_char(p)) {
5700c36111fSHervé Poussineau             c = g_utf8_get_char(p);
5710c36111fSHervé Poussineau             if (c == '\0') {
5720c36111fSHervé Poussineau                 break;
5730c36111fSHervé Poussineau             } else {
5740c36111fSHervé Poussineau                 /* extension; copy it */
5750c36111fSHervé Poussineau                 uint8_t v = to_valid_short_char(c);
5760c36111fSHervé Poussineau                 if (j < 3 && v) {
5770c36111fSHervé Poussineau                     entry->name[8 + (j++)] = v;
5780c36111fSHervé Poussineau                 } else {
5790c36111fSHervé Poussineau                     lossy_conversion = true;
5800c36111fSHervé Poussineau                 }
5810c36111fSHervé Poussineau             }
5820c36111fSHervé Poussineau         }
5830c36111fSHervé Poussineau     }
584339cebccSHervé Poussineau 
5858c4517fdSHervé Poussineau     if (entry->name[0] == DIR_KANJI) {
5868c4517fdSHervé Poussineau         entry->name[0] = DIR_KANJI_FAKE;
58778f002c9SHervé Poussineau     }
58878f002c9SHervé Poussineau 
589339cebccSHervé Poussineau     /* numeric-tail generation */
590339cebccSHervé Poussineau     for (j = 0; j < 8; j++) {
591339cebccSHervé Poussineau         if (entry->name[j] == ' ') {
592339cebccSHervé Poussineau             break;
593339cebccSHervé Poussineau         }
594339cebccSHervé Poussineau     }
595339cebccSHervé Poussineau     for (i = lossy_conversion ? 1 : 0; i < 999999; i++) {
596339cebccSHervé Poussineau         direntry_t *entry1;
597339cebccSHervé Poussineau         if (i > 0) {
5987c8730d4SMax Reitz             int len = snprintf(tail, sizeof(tail), "~%u", (unsigned)i);
5997c8730d4SMax Reitz             assert(len <= 7);
600339cebccSHervé Poussineau             memcpy(entry->name + MIN(j, 8 - len), tail, len);
601339cebccSHervé Poussineau         }
602339cebccSHervé Poussineau         for (entry1 = array_get(&(s->directory), directory_start);
603339cebccSHervé Poussineau              entry1 < entry; entry1++) {
604339cebccSHervé Poussineau             if (!is_long_name(entry1) &&
605339cebccSHervé Poussineau                 !memcmp(entry1->name, entry->name, 11)) {
606339cebccSHervé Poussineau                 break; /* found dupe */
607339cebccSHervé Poussineau             }
608339cebccSHervé Poussineau         }
609339cebccSHervé Poussineau         if (entry1 == entry) {
610339cebccSHervé Poussineau             /* no dupe found */
6110c36111fSHervé Poussineau             return entry;
6120c36111fSHervé Poussineau         }
613339cebccSHervé Poussineau     }
614339cebccSHervé Poussineau     return NULL;
615339cebccSHervé Poussineau }
6160c36111fSHervé Poussineau 
617019d6b8fSAnthony Liguori /* fat functions */
618019d6b8fSAnthony Liguori 
fat_chksum(const direntry_t * entry)619c227f099SAnthony Liguori static inline uint8_t fat_chksum(const direntry_t* entry)
620019d6b8fSAnthony Liguori {
621019d6b8fSAnthony Liguori     uint8_t chksum=0;
622019d6b8fSAnthony Liguori     int i;
623019d6b8fSAnthony Liguori 
624f671d173SStefan Weil     for (i = 0; i < ARRAY_SIZE(entry->name); i++) {
625f671d173SStefan Weil         chksum = (((chksum & 0xfe) >> 1) |
626f671d173SStefan Weil                   ((chksum & 0x01) ? 0x80 : 0)) + entry->name[i];
627019d6b8fSAnthony Liguori     }
628019d6b8fSAnthony Liguori 
629019d6b8fSAnthony Liguori     return chksum;
630019d6b8fSAnthony Liguori }
631019d6b8fSAnthony Liguori 
632019d6b8fSAnthony Liguori /* if return_time==0, this returns the fat_date, else the fat_time */
fat_datetime(time_t time,int return_time)633019d6b8fSAnthony Liguori static uint16_t fat_datetime(time_t time,int return_time) {
634019d6b8fSAnthony Liguori     struct tm* t;
635019d6b8fSAnthony Liguori     struct tm t1;
636019d6b8fSAnthony Liguori     t = &t1;
637019d6b8fSAnthony Liguori     localtime_r(&time,t);
638019d6b8fSAnthony Liguori     if(return_time)
639019d6b8fSAnthony Liguori         return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
640019d6b8fSAnthony Liguori     return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
641019d6b8fSAnthony Liguori }
642019d6b8fSAnthony Liguori 
fat_set(BDRVVVFATState * s,unsigned int cluster,uint32_t value)643019d6b8fSAnthony Liguori static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
644019d6b8fSAnthony Liguori {
645019d6b8fSAnthony Liguori     if(s->fat_type==32) {
646019d6b8fSAnthony Liguori         uint32_t* entry=array_get(&(s->fat),cluster);
647019d6b8fSAnthony Liguori         *entry=cpu_to_le32(value);
648019d6b8fSAnthony Liguori     } else if(s->fat_type==16) {
649019d6b8fSAnthony Liguori         uint16_t* entry=array_get(&(s->fat),cluster);
650019d6b8fSAnthony Liguori         *entry=cpu_to_le16(value&0xffff);
651019d6b8fSAnthony Liguori     } else {
652019d6b8fSAnthony Liguori         int offset = (cluster*3/2);
653019d6b8fSAnthony Liguori         unsigned char* p = array_get(&(s->fat), offset);
654019d6b8fSAnthony Liguori         switch (cluster&1) {
655019d6b8fSAnthony Liguori         case 0:
656019d6b8fSAnthony Liguori                 p[0] = value&0xff;
657019d6b8fSAnthony Liguori                 p[1] = (p[1]&0xf0) | ((value>>8)&0xf);
658019d6b8fSAnthony Liguori                 break;
659019d6b8fSAnthony Liguori         case 1:
660019d6b8fSAnthony Liguori                 p[0] = (p[0]&0xf) | ((value&0xf)<<4);
661019d6b8fSAnthony Liguori                 p[1] = (value>>4);
662019d6b8fSAnthony Liguori                 break;
663019d6b8fSAnthony Liguori         }
664019d6b8fSAnthony Liguori     }
665019d6b8fSAnthony Liguori }
666019d6b8fSAnthony Liguori 
fat_get(BDRVVVFATState * s,unsigned int cluster)667019d6b8fSAnthony Liguori static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
668019d6b8fSAnthony Liguori {
669019d6b8fSAnthony Liguori     if(s->fat_type==32) {
670019d6b8fSAnthony Liguori         uint32_t* entry=array_get(&(s->fat),cluster);
671019d6b8fSAnthony Liguori         return le32_to_cpu(*entry);
672019d6b8fSAnthony Liguori     } else if(s->fat_type==16) {
673019d6b8fSAnthony Liguori         uint16_t* entry=array_get(&(s->fat),cluster);
674019d6b8fSAnthony Liguori         return le16_to_cpu(*entry);
675019d6b8fSAnthony Liguori     } else {
676019d6b8fSAnthony Liguori         const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2;
677019d6b8fSAnthony Liguori         return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
678019d6b8fSAnthony Liguori     }
679019d6b8fSAnthony Liguori }
680019d6b8fSAnthony Liguori 
fat_eof(BDRVVVFATState * s,uint32_t fat_entry)681019d6b8fSAnthony Liguori static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
682019d6b8fSAnthony Liguori {
683019d6b8fSAnthony Liguori     if(fat_entry>s->max_fat_value-8)
684019d6b8fSAnthony Liguori         return -1;
685019d6b8fSAnthony Liguori     return 0;
686019d6b8fSAnthony Liguori }
687019d6b8fSAnthony Liguori 
init_fat(BDRVVVFATState * s)688019d6b8fSAnthony Liguori static inline void init_fat(BDRVVVFATState* s)
689019d6b8fSAnthony Liguori {
690019d6b8fSAnthony Liguori     if (s->fat_type == 12) {
691019d6b8fSAnthony Liguori         array_init(&(s->fat),1);
692019d6b8fSAnthony Liguori         array_ensure_allocated(&(s->fat),
693019d6b8fSAnthony Liguori                 s->sectors_per_fat * 0x200 * 3 / 2 - 1);
694019d6b8fSAnthony Liguori     } else {
695019d6b8fSAnthony Liguori         array_init(&(s->fat),(s->fat_type==32?4:2));
696019d6b8fSAnthony Liguori         array_ensure_allocated(&(s->fat),
697019d6b8fSAnthony Liguori                 s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
698019d6b8fSAnthony Liguori     }
699019d6b8fSAnthony Liguori     memset(s->fat.pointer,0,s->fat.size);
700019d6b8fSAnthony Liguori 
701019d6b8fSAnthony Liguori     switch(s->fat_type) {
702019d6b8fSAnthony Liguori         case 12: s->max_fat_value=0xfff; break;
703019d6b8fSAnthony Liguori         case 16: s->max_fat_value=0xffff; break;
704019d6b8fSAnthony Liguori         case 32: s->max_fat_value=0x0fffffff; break;
705019d6b8fSAnthony Liguori         default: s->max_fat_value=0; /* error... */
706019d6b8fSAnthony Liguori     }
707019d6b8fSAnthony Liguori 
708019d6b8fSAnthony Liguori }
709019d6b8fSAnthony Liguori 
create_short_and_long_name(BDRVVVFATState * s,unsigned int directory_start,const char * filename,int is_dot)710c227f099SAnthony Liguori static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
711019d6b8fSAnthony Liguori         unsigned int directory_start, const char* filename, int is_dot)
712019d6b8fSAnthony Liguori {
7130c36111fSHervé Poussineau     int long_index = s->directory.next;
714c227f099SAnthony Liguori     direntry_t* entry = NULL;
715c227f099SAnthony Liguori     direntry_t* entry_long = NULL;
716019d6b8fSAnthony Liguori 
717019d6b8fSAnthony Liguori     if(is_dot) {
718019d6b8fSAnthony Liguori         entry=array_get_next(&(s->directory));
719f671d173SStefan Weil         memset(entry->name, 0x20, sizeof(entry->name));
720019d6b8fSAnthony Liguori         memcpy(entry->name,filename,strlen(filename));
721019d6b8fSAnthony Liguori         return entry;
722019d6b8fSAnthony Liguori     }
723019d6b8fSAnthony Liguori 
724019d6b8fSAnthony Liguori     entry_long=create_long_filename(s,filename);
725339cebccSHervé Poussineau     entry = create_short_filename(s, filename, directory_start);
726019d6b8fSAnthony Liguori 
727019d6b8fSAnthony Liguori     /* calculate checksum; propagate to long name */
728019d6b8fSAnthony Liguori     if(entry_long) {
729019d6b8fSAnthony Liguori         uint8_t chksum=fat_chksum(entry);
730019d6b8fSAnthony Liguori 
731019d6b8fSAnthony Liguori         /* calculate anew, because realloc could have taken place */
732019d6b8fSAnthony Liguori         entry_long=array_get(&(s->directory),long_index);
733019d6b8fSAnthony Liguori         while(entry_long<entry && is_long_name(entry_long)) {
734019d6b8fSAnthony Liguori             entry_long->reserved[1]=chksum;
735019d6b8fSAnthony Liguori             entry_long++;
736019d6b8fSAnthony Liguori         }
737019d6b8fSAnthony Liguori     }
738019d6b8fSAnthony Liguori 
739019d6b8fSAnthony Liguori     return entry;
740019d6b8fSAnthony Liguori }
741019d6b8fSAnthony Liguori 
742019d6b8fSAnthony Liguori /*
743019d6b8fSAnthony Liguori  * Read a directory. (the index of the corresponding mapping must be passed).
744019d6b8fSAnthony Liguori  */
read_directory(BDRVVVFATState * s,int mapping_index)745019d6b8fSAnthony Liguori static int read_directory(BDRVVVFATState* s, int mapping_index)
746019d6b8fSAnthony Liguori {
747c227f099SAnthony Liguori     mapping_t* mapping = array_get(&(s->mapping), mapping_index);
748c227f099SAnthony Liguori     direntry_t* direntry;
749019d6b8fSAnthony Liguori     const char* dirname = mapping->path;
750019d6b8fSAnthony Liguori     int first_cluster = mapping->begin;
751019d6b8fSAnthony Liguori     int parent_index = mapping->info.dir.parent_mapping_index;
752c227f099SAnthony Liguori     mapping_t* parent_mapping = (mapping_t*)
753019d6b8fSAnthony Liguori         (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : NULL);
754019d6b8fSAnthony Liguori     int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
755019d6b8fSAnthony Liguori 
756019d6b8fSAnthony Liguori     DIR* dir=opendir(dirname);
757019d6b8fSAnthony Liguori     struct dirent* entry;
758019d6b8fSAnthony Liguori     int i;
759019d6b8fSAnthony Liguori 
760019d6b8fSAnthony Liguori     assert(mapping->mode & MODE_DIRECTORY);
761019d6b8fSAnthony Liguori 
762019d6b8fSAnthony Liguori     if(!dir) {
763019d6b8fSAnthony Liguori         mapping->end = mapping->begin;
764019d6b8fSAnthony Liguori         return -1;
765019d6b8fSAnthony Liguori     }
766019d6b8fSAnthony Liguori 
767019d6b8fSAnthony Liguori     i = mapping->info.dir.first_dir_index =
768019d6b8fSAnthony Liguori             first_cluster == 0 ? 0 : s->directory.next;
769019d6b8fSAnthony Liguori 
770f82d92bbSHervé Poussineau     if (first_cluster != 0) {
771f82d92bbSHervé Poussineau         /* create the top entries of a subdirectory */
772f82d92bbSHervé Poussineau         (void)create_short_and_long_name(s, i, ".", 1);
773f82d92bbSHervé Poussineau         (void)create_short_and_long_name(s, i, "..", 1);
774f82d92bbSHervé Poussineau     }
775f82d92bbSHervé Poussineau 
776019d6b8fSAnthony Liguori     /* actually read the directory, and allocate the mappings */
777019d6b8fSAnthony Liguori     while((entry=readdir(dir))) {
778019d6b8fSAnthony Liguori         unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
779019d6b8fSAnthony Liguori         char* buffer;
780019d6b8fSAnthony Liguori         struct stat st;
781019d6b8fSAnthony Liguori         int is_dot=!strcmp(entry->d_name,".");
782019d6b8fSAnthony Liguori         int is_dotdot=!strcmp(entry->d_name,"..");
783019d6b8fSAnthony Liguori 
7846817efeaSHervé Poussineau         if (first_cluster == 0 && s->directory.next >= s->root_entries - 1) {
7856817efeaSHervé Poussineau             fprintf(stderr, "Too many entries in root directory\n");
7866817efeaSHervé Poussineau             closedir(dir);
7876817efeaSHervé Poussineau             return -2;
7886817efeaSHervé Poussineau         }
7896817efeaSHervé Poussineau 
790019d6b8fSAnthony Liguori         if(first_cluster == 0 && (is_dotdot || is_dot))
791019d6b8fSAnthony Liguori             continue;
792019d6b8fSAnthony Liguori 
793d4df3dbcSMarkus Armbruster         buffer = g_malloc(length);
794019d6b8fSAnthony Liguori         snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
795019d6b8fSAnthony Liguori 
796019d6b8fSAnthony Liguori         if(stat(buffer,&st)<0) {
797ce137829SStefan Weil             g_free(buffer);
798019d6b8fSAnthony Liguori             continue;
799019d6b8fSAnthony Liguori         }
800019d6b8fSAnthony Liguori 
801019d6b8fSAnthony Liguori         /* create directory entry for this file */
802f82d92bbSHervé Poussineau         if (!is_dot && !is_dotdot) {
803f82d92bbSHervé Poussineau             direntry = create_short_and_long_name(s, i, entry->d_name, 0);
804f82d92bbSHervé Poussineau         } else {
805f82d92bbSHervé Poussineau             direntry = array_get(&(s->directory), is_dot ? i : i + 1);
806f82d92bbSHervé Poussineau         }
807019d6b8fSAnthony Liguori         direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
808019d6b8fSAnthony Liguori         direntry->reserved[0]=direntry->reserved[1]=0;
809019d6b8fSAnthony Liguori         direntry->ctime=fat_datetime(st.st_ctime,1);
810019d6b8fSAnthony Liguori         direntry->cdate=fat_datetime(st.st_ctime,0);
811019d6b8fSAnthony Liguori         direntry->adate=fat_datetime(st.st_atime,0);
812019d6b8fSAnthony Liguori         direntry->begin_hi=0;
813019d6b8fSAnthony Liguori         direntry->mtime=fat_datetime(st.st_mtime,1);
814019d6b8fSAnthony Liguori         direntry->mdate=fat_datetime(st.st_mtime,0);
815019d6b8fSAnthony Liguori         if(is_dotdot)
816019d6b8fSAnthony Liguori             set_begin_of_direntry(direntry, first_cluster_of_parent);
817019d6b8fSAnthony Liguori         else if(is_dot)
818019d6b8fSAnthony Liguori             set_begin_of_direntry(direntry, first_cluster);
819019d6b8fSAnthony Liguori         else
820019d6b8fSAnthony Liguori             direntry->begin=0; /* do that later */
821019d6b8fSAnthony Liguori         if (st.st_size > 0x7fffffff) {
822019d6b8fSAnthony Liguori             fprintf(stderr, "File %s is larger than 2GB\n", buffer);
823ce137829SStefan Weil             g_free(buffer);
82408089edcSBlue Swirl             closedir(dir);
825019d6b8fSAnthony Liguori             return -2;
826019d6b8fSAnthony Liguori         }
827019d6b8fSAnthony Liguori         direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
828019d6b8fSAnthony Liguori 
829019d6b8fSAnthony Liguori         /* create mapping for this file */
830019d6b8fSAnthony Liguori         if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
831d4df3dbcSMarkus Armbruster             s->current_mapping = array_get_next(&(s->mapping));
832019d6b8fSAnthony Liguori             s->current_mapping->begin=0;
833019d6b8fSAnthony Liguori             s->current_mapping->end=st.st_size;
834019d6b8fSAnthony Liguori             /*
835019d6b8fSAnthony Liguori              * we get the direntry of the most recent direntry, which
836019d6b8fSAnthony Liguori              * contains the short name and all the relevant information.
837019d6b8fSAnthony Liguori              */
838019d6b8fSAnthony Liguori             s->current_mapping->dir_index=s->directory.next-1;
839019d6b8fSAnthony Liguori             s->current_mapping->first_mapping_index = -1;
840019d6b8fSAnthony Liguori             if (S_ISDIR(st.st_mode)) {
841019d6b8fSAnthony Liguori                 s->current_mapping->mode = MODE_DIRECTORY;
842019d6b8fSAnthony Liguori                 s->current_mapping->info.dir.parent_mapping_index =
843019d6b8fSAnthony Liguori                     mapping_index;
844019d6b8fSAnthony Liguori             } else {
845019d6b8fSAnthony Liguori                 s->current_mapping->mode = MODE_UNDEFINED;
846019d6b8fSAnthony Liguori                 s->current_mapping->info.file.offset = 0;
847019d6b8fSAnthony Liguori             }
848019d6b8fSAnthony Liguori             s->current_mapping->path=buffer;
849019d6b8fSAnthony Liguori             s->current_mapping->read_only =
850019d6b8fSAnthony Liguori                 (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
851b122c3b6SMarkus Armbruster         } else {
852b122c3b6SMarkus Armbruster             g_free(buffer);
853019d6b8fSAnthony Liguori         }
854019d6b8fSAnthony Liguori     }
855019d6b8fSAnthony Liguori     closedir(dir);
856019d6b8fSAnthony Liguori 
857019d6b8fSAnthony Liguori     /* fill with zeroes up to the end of the cluster */
858019d6b8fSAnthony Liguori     while(s->directory.next%(0x10*s->sectors_per_cluster)) {
859fb2575f9SMarkus Armbruster         direntry = array_get_next(&(s->directory));
860c227f099SAnthony Liguori         memset(direntry,0,sizeof(direntry_t));
861019d6b8fSAnthony Liguori     }
862019d6b8fSAnthony Liguori 
8636817efeaSHervé Poussineau     if (s->fat_type != 32 &&
8646817efeaSHervé Poussineau         mapping_index == 0 &&
8656817efeaSHervé Poussineau         s->directory.next < s->root_entries) {
866019d6b8fSAnthony Liguori         /* root directory */
867019d6b8fSAnthony Liguori         int cur = s->directory.next;
8686817efeaSHervé Poussineau         array_ensure_allocated(&(s->directory), s->root_entries - 1);
8696817efeaSHervé Poussineau         s->directory.next = s->root_entries;
870019d6b8fSAnthony Liguori         memset(array_get(&(s->directory), cur), 0,
8716817efeaSHervé Poussineau                 (s->root_entries - cur) * sizeof(direntry_t));
872019d6b8fSAnthony Liguori     }
873019d6b8fSAnthony Liguori 
8745f5b29dfSHervé Poussineau     /* re-get the mapping, since s->mapping was possibly realloc()ed */
875d4df3dbcSMarkus Armbruster     mapping = array_get(&(s->mapping), mapping_index);
876019d6b8fSAnthony Liguori     first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
877019d6b8fSAnthony Liguori         * 0x20 / s->cluster_size;
878019d6b8fSAnthony Liguori     mapping->end = first_cluster;
879019d6b8fSAnthony Liguori 
880d4df3dbcSMarkus Armbruster     direntry = array_get(&(s->directory), mapping->dir_index);
881019d6b8fSAnthony Liguori     set_begin_of_direntry(direntry, mapping->begin);
882019d6b8fSAnthony Liguori 
883019d6b8fSAnthony Liguori     return 0;
884019d6b8fSAnthony Liguori }
885019d6b8fSAnthony Liguori 
sector2cluster(BDRVVVFATState * s,off_t sector_num)886b9b8860dSKevin Wolf static inline int32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
887019d6b8fSAnthony Liguori {
8884dc705dcSHervé Poussineau     return (sector_num - s->offset_to_root_dir) / s->sectors_per_cluster;
889019d6b8fSAnthony Liguori }
890019d6b8fSAnthony Liguori 
cluster2sector(BDRVVVFATState * s,uint32_t cluster_num)891019d6b8fSAnthony Liguori static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
892019d6b8fSAnthony Liguori {
8934dc705dcSHervé Poussineau     return s->offset_to_root_dir + s->sectors_per_cluster * cluster_num;
894019d6b8fSAnthony Liguori }
895019d6b8fSAnthony Liguori 
init_directories(BDRVVVFATState * s,const char * dirname,int heads,int secs,Error ** errp)896019d6b8fSAnthony Liguori static int init_directories(BDRVVVFATState* s,
897d11c8917SMarkus Armbruster                             const char *dirname, int heads, int secs,
898d11c8917SMarkus Armbruster                             Error **errp)
899019d6b8fSAnthony Liguori {
900c227f099SAnthony Liguori     bootsector_t* bootsector;
901c227f099SAnthony Liguori     mapping_t* mapping;
902019d6b8fSAnthony Liguori     unsigned int i;
903019d6b8fSAnthony Liguori     unsigned int cluster;
904019d6b8fSAnthony Liguori 
905019d6b8fSAnthony Liguori     memset(&(s->first_sectors[0]),0,0x40*0x200);
906019d6b8fSAnthony Liguori 
907019d6b8fSAnthony Liguori     s->cluster_size=s->sectors_per_cluster*0x200;
9087267c094SAnthony Liguori     s->cluster_buffer=g_malloc(s->cluster_size);
909019d6b8fSAnthony Liguori 
910019d6b8fSAnthony Liguori     /*
911019d6b8fSAnthony Liguori      * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
912019d6b8fSAnthony Liguori      * where sc is sector_count,
913019d6b8fSAnthony Liguori      * spf is sectors_per_fat,
914019d6b8fSAnthony Liguori      * spc is sectors_per_clusters, and
915019d6b8fSAnthony Liguori      * fat_type = 12, 16 or 32.
916019d6b8fSAnthony Liguori      */
917019d6b8fSAnthony Liguori     i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
918019d6b8fSAnthony Liguori     s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
919019d6b8fSAnthony Liguori 
9204dc705dcSHervé Poussineau     s->offset_to_fat = s->offset_to_bootsector + 1;
9214dc705dcSHervé Poussineau     s->offset_to_root_dir = s->offset_to_fat + s->sectors_per_fat * 2;
9224dc705dcSHervé Poussineau 
923c227f099SAnthony Liguori     array_init(&(s->mapping),sizeof(mapping_t));
924c227f099SAnthony Liguori     array_init(&(s->directory),sizeof(direntry_t));
925019d6b8fSAnthony Liguori 
926019d6b8fSAnthony Liguori     /* add volume label */
927019d6b8fSAnthony Liguori     {
928c227f099SAnthony Liguori         direntry_t* entry=array_get_next(&(s->directory));
929019d6b8fSAnthony Liguori         entry->attributes=0x28; /* archive | volume label */
930d5941ddaSWolfgang Bumiller         memcpy(entry->name, s->volume_label, sizeof(entry->name));
931019d6b8fSAnthony Liguori     }
932019d6b8fSAnthony Liguori 
933019d6b8fSAnthony Liguori     /* Now build FAT, and write back information into directory */
934019d6b8fSAnthony Liguori     init_fat(s);
935019d6b8fSAnthony Liguori 
9366817efeaSHervé Poussineau     /* TODO: if there are more entries, bootsector has to be adjusted! */
9376817efeaSHervé Poussineau     s->root_entries = 0x02 * 0x10 * s->sectors_per_cluster;
938019d6b8fSAnthony Liguori     s->cluster_count=sector2cluster(s, s->sector_count);
939019d6b8fSAnthony Liguori 
940019d6b8fSAnthony Liguori     mapping = array_get_next(&(s->mapping));
941019d6b8fSAnthony Liguori     mapping->begin = 0;
942019d6b8fSAnthony Liguori     mapping->dir_index = 0;
943019d6b8fSAnthony Liguori     mapping->info.dir.parent_mapping_index = -1;
944019d6b8fSAnthony Liguori     mapping->first_mapping_index = -1;
9457267c094SAnthony Liguori     mapping->path = g_strdup(dirname);
946019d6b8fSAnthony Liguori     i = strlen(mapping->path);
947019d6b8fSAnthony Liguori     if (i > 0 && mapping->path[i - 1] == '/')
948019d6b8fSAnthony Liguori         mapping->path[i - 1] = '\0';
949019d6b8fSAnthony Liguori     mapping->mode = MODE_DIRECTORY;
950019d6b8fSAnthony Liguori     mapping->read_only = 0;
951019d6b8fSAnthony Liguori     s->path = mapping->path;
952019d6b8fSAnthony Liguori 
953019d6b8fSAnthony Liguori     for (i = 0, cluster = 0; i < s->mapping.next; i++) {
954019d6b8fSAnthony Liguori         /* MS-DOS expects the FAT to be 0 for the root directory
955019d6b8fSAnthony Liguori          * (except for the media byte). */
956019d6b8fSAnthony Liguori         /* LATER TODO: still true for FAT32? */
957019d6b8fSAnthony Liguori         int fix_fat = (i != 0);
958019d6b8fSAnthony Liguori         mapping = array_get(&(s->mapping), i);
959019d6b8fSAnthony Liguori 
960019d6b8fSAnthony Liguori         if (mapping->mode & MODE_DIRECTORY) {
961a2b83a51SThomas Huth             char *path = mapping->path;
962019d6b8fSAnthony Liguori             mapping->begin = cluster;
963019d6b8fSAnthony Liguori             if(read_directory(s, i)) {
964a2b83a51SThomas Huth                 error_setg(errp, "Could not read directory %s", path);
965019d6b8fSAnthony Liguori                 return -1;
966019d6b8fSAnthony Liguori             }
967019d6b8fSAnthony Liguori             mapping = array_get(&(s->mapping), i);
968019d6b8fSAnthony Liguori         } else {
969019d6b8fSAnthony Liguori             assert(mapping->mode == MODE_UNDEFINED);
970019d6b8fSAnthony Liguori             mapping->mode=MODE_NORMAL;
971019d6b8fSAnthony Liguori             mapping->begin = cluster;
972019d6b8fSAnthony Liguori             if (mapping->end > 0) {
973c227f099SAnthony Liguori                 direntry_t* direntry = array_get(&(s->directory),
974019d6b8fSAnthony Liguori                         mapping->dir_index);
975019d6b8fSAnthony Liguori 
976019d6b8fSAnthony Liguori                 mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
977019d6b8fSAnthony Liguori                 set_begin_of_direntry(direntry, mapping->begin);
978019d6b8fSAnthony Liguori             } else {
979019d6b8fSAnthony Liguori                 mapping->end = cluster + 1;
980019d6b8fSAnthony Liguori                 fix_fat = 0;
981019d6b8fSAnthony Liguori             }
982019d6b8fSAnthony Liguori         }
983019d6b8fSAnthony Liguori 
984019d6b8fSAnthony Liguori         assert(mapping->begin < mapping->end);
985019d6b8fSAnthony Liguori 
986019d6b8fSAnthony Liguori         /* next free cluster */
987019d6b8fSAnthony Liguori         cluster = mapping->end;
988019d6b8fSAnthony Liguori 
989019d6b8fSAnthony Liguori         if(cluster > s->cluster_count) {
990d11c8917SMarkus Armbruster             error_setg(errp,
991d11c8917SMarkus Armbruster                        "Directory does not fit in FAT%d (capacity %.2f MB)",
992d71cff42SPaolo Bonzini                        s->fat_type, s->sector_count / 2000.0);
993d11c8917SMarkus Armbruster             return -1;
994019d6b8fSAnthony Liguori         }
995019d6b8fSAnthony Liguori 
996019d6b8fSAnthony Liguori         /* fix fat for entry */
997019d6b8fSAnthony Liguori         if (fix_fat) {
998019d6b8fSAnthony Liguori             int j;
999019d6b8fSAnthony Liguori             for(j = mapping->begin; j < mapping->end - 1; j++)
1000019d6b8fSAnthony Liguori                 fat_set(s, j, j+1);
1001019d6b8fSAnthony Liguori             fat_set(s, mapping->end - 1, s->max_fat_value);
1002019d6b8fSAnthony Liguori         }
1003019d6b8fSAnthony Liguori     }
1004019d6b8fSAnthony Liguori 
1005019d6b8fSAnthony Liguori     mapping = array_get(&(s->mapping), 0);
1006019d6b8fSAnthony Liguori     s->last_cluster_of_root_directory = mapping->end;
1007019d6b8fSAnthony Liguori 
1008019d6b8fSAnthony Liguori     /* the FAT signature */
1009019d6b8fSAnthony Liguori     fat_set(s,0,s->max_fat_value);
1010019d6b8fSAnthony Liguori     fat_set(s,1,s->max_fat_value);
1011019d6b8fSAnthony Liguori 
1012019d6b8fSAnthony Liguori     s->current_mapping = NULL;
1013019d6b8fSAnthony Liguori 
10144dc705dcSHervé Poussineau     bootsector = (bootsector_t *)(s->first_sectors
10154dc705dcSHervé Poussineau                                   + s->offset_to_bootsector * 0x200);
1016019d6b8fSAnthony Liguori     bootsector->jump[0]=0xeb;
1017019d6b8fSAnthony Liguori     bootsector->jump[1]=0x3e;
1018019d6b8fSAnthony Liguori     bootsector->jump[2]=0x90;
101963d261cbSHervé Poussineau     memcpy(bootsector->name, BOOTSECTOR_OEM_NAME, 8);
1020019d6b8fSAnthony Liguori     bootsector->sector_size=cpu_to_le16(0x200);
1021019d6b8fSAnthony Liguori     bootsector->sectors_per_cluster=s->sectors_per_cluster;
1022019d6b8fSAnthony Liguori     bootsector->reserved_sectors=cpu_to_le16(1);
1023019d6b8fSAnthony Liguori     bootsector->number_of_fats=0x2; /* number of FATs */
10246817efeaSHervé Poussineau     bootsector->root_entries = cpu_to_le16(s->root_entries);
1025019d6b8fSAnthony Liguori     bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
10264dc705dcSHervé Poussineau     /* media descriptor: hard disk=0xf8, floppy=0xf0 */
10274dc705dcSHervé Poussineau     bootsector->media_type = (s->offset_to_bootsector > 0 ? 0xf8 : 0xf0);
1028019d6b8fSAnthony Liguori     s->fat.pointer[0] = bootsector->media_type;
1029019d6b8fSAnthony Liguori     bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
10304480e0f9SMarkus Armbruster     bootsector->sectors_per_track = cpu_to_le16(secs);
10314480e0f9SMarkus Armbruster     bootsector->number_of_heads = cpu_to_le16(heads);
10324dc705dcSHervé Poussineau     bootsector->hidden_sectors = cpu_to_le32(s->offset_to_bootsector);
1033019d6b8fSAnthony Liguori     bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
1034019d6b8fSAnthony Liguori 
1035019d6b8fSAnthony Liguori     /* LATER TODO: if FAT32, this is wrong */
10364dc705dcSHervé Poussineau     /* drive_number: fda=0, hda=0x80 */
10374dc705dcSHervé Poussineau     bootsector->u.fat16.drive_number = s->offset_to_bootsector == 0 ? 0 : 0x80;
1038019d6b8fSAnthony Liguori     bootsector->u.fat16.signature=0x29;
1039019d6b8fSAnthony Liguori     bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
1040019d6b8fSAnthony Liguori 
1041d5941ddaSWolfgang Bumiller     memcpy(bootsector->u.fat16.volume_label, s->volume_label,
1042d5941ddaSWolfgang Bumiller            sizeof(bootsector->u.fat16.volume_label));
104392e28d82SHervé Poussineau     memcpy(bootsector->u.fat16.fat_type,
104492e28d82SHervé Poussineau            s->fat_type == 12 ? "FAT12   " : "FAT16   ", 8);
1045019d6b8fSAnthony Liguori     bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
1046019d6b8fSAnthony Liguori 
1047019d6b8fSAnthony Liguori     return 0;
1048019d6b8fSAnthony Liguori }
1049019d6b8fSAnthony Liguori 
1050019d6b8fSAnthony Liguori #ifdef DEBUG
1051019d6b8fSAnthony Liguori static BDRVVVFATState *vvv = NULL;
1052019d6b8fSAnthony Liguori #endif
1053019d6b8fSAnthony Liguori 
1054eecc7747SKevin Wolf static int enable_write_target(BlockDriverState *bs, Error **errp);
1055eab76d58SPaolo Bonzini static int coroutine_fn is_consistent(BDRVVVFATState *s);
1056019d6b8fSAnthony Liguori 
10577ad9be64SKevin Wolf static QemuOptsList runtime_opts = {
10587ad9be64SKevin Wolf     .name = "vvfat",
10597ad9be64SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
10607ad9be64SKevin Wolf     .desc = {
10617ad9be64SKevin Wolf         {
10627ad9be64SKevin Wolf             .name = "dir",
10637ad9be64SKevin Wolf             .type = QEMU_OPT_STRING,
10647ad9be64SKevin Wolf             .help = "Host directory to map to the vvfat device",
10657ad9be64SKevin Wolf         },
10667ad9be64SKevin Wolf         {
10677ad9be64SKevin Wolf             .name = "fat-type",
10687ad9be64SKevin Wolf             .type = QEMU_OPT_NUMBER,
10697ad9be64SKevin Wolf             .help = "FAT type (12, 16 or 32)",
10707ad9be64SKevin Wolf         },
10717ad9be64SKevin Wolf         {
10727ad9be64SKevin Wolf             .name = "floppy",
10737ad9be64SKevin Wolf             .type = QEMU_OPT_BOOL,
10747ad9be64SKevin Wolf             .help = "Create a floppy rather than a hard disk image",
10757ad9be64SKevin Wolf         },
10767ad9be64SKevin Wolf         {
1077d5941ddaSWolfgang Bumiller             .name = "label",
1078d5941ddaSWolfgang Bumiller             .type = QEMU_OPT_STRING,
1079d5941ddaSWolfgang Bumiller             .help = "Use a volume label other than QEMU VVFAT",
1080d5941ddaSWolfgang Bumiller         },
1081d5941ddaSWolfgang Bumiller         {
10827ad9be64SKevin Wolf             .name = "rw",
10837ad9be64SKevin Wolf             .type = QEMU_OPT_BOOL,
10847ad9be64SKevin Wolf             .help = "Make the image writable",
10857ad9be64SKevin Wolf         },
10867ad9be64SKevin Wolf         { /* end of list */ }
10877ad9be64SKevin Wolf     },
10887ad9be64SKevin Wolf };
10897ad9be64SKevin Wolf 
vvfat_parse_filename(const char * filename,QDict * options,Error ** errp)10907ad9be64SKevin Wolf static void vvfat_parse_filename(const char *filename, QDict *options,
10917ad9be64SKevin Wolf                                  Error **errp)
10927ad9be64SKevin Wolf {
10937ad9be64SKevin Wolf     int fat_type = 0;
10947ad9be64SKevin Wolf     bool floppy = false;
10957ad9be64SKevin Wolf     bool rw = false;
10967ad9be64SKevin Wolf     int i;
10977ad9be64SKevin Wolf 
10987ad9be64SKevin Wolf     if (!strstart(filename, "fat:", NULL)) {
10997ad9be64SKevin Wolf         error_setg(errp, "File name string must start with 'fat:'");
11007ad9be64SKevin Wolf         return;
11017ad9be64SKevin Wolf     }
11027ad9be64SKevin Wolf 
11037ad9be64SKevin Wolf     /* Parse options */
11047ad9be64SKevin Wolf     if (strstr(filename, ":32:")) {
11057ad9be64SKevin Wolf         fat_type = 32;
11067ad9be64SKevin Wolf     } else if (strstr(filename, ":16:")) {
11077ad9be64SKevin Wolf         fat_type = 16;
11087ad9be64SKevin Wolf     } else if (strstr(filename, ":12:")) {
11097ad9be64SKevin Wolf         fat_type = 12;
11107ad9be64SKevin Wolf     }
11117ad9be64SKevin Wolf 
11127ad9be64SKevin Wolf     if (strstr(filename, ":floppy:")) {
11137ad9be64SKevin Wolf         floppy = true;
11147ad9be64SKevin Wolf     }
11157ad9be64SKevin Wolf 
11167ad9be64SKevin Wolf     if (strstr(filename, ":rw:")) {
11177ad9be64SKevin Wolf         rw = true;
11187ad9be64SKevin Wolf     }
11197ad9be64SKevin Wolf 
11207ad9be64SKevin Wolf     /* Get the directory name without options */
11217ad9be64SKevin Wolf     i = strrchr(filename, ':') - filename;
11227ad9be64SKevin Wolf     assert(i >= 3);
11237ad9be64SKevin Wolf     if (filename[i - 2] == ':' && qemu_isalpha(filename[i - 1])) {
11247ad9be64SKevin Wolf         /* workaround for DOS drive names */
11257ad9be64SKevin Wolf         filename += i - 1;
11267ad9be64SKevin Wolf     } else {
11277ad9be64SKevin Wolf         filename += i + 1;
11287ad9be64SKevin Wolf     }
11297ad9be64SKevin Wolf 
11307ad9be64SKevin Wolf     /* Fill in the options QDict */
113146f5ac20SEric Blake     qdict_put_str(options, "dir", filename);
113246f5ac20SEric Blake     qdict_put_int(options, "fat-type", fat_type);
113346f5ac20SEric Blake     qdict_put_bool(options, "floppy", floppy);
113446f5ac20SEric Blake     qdict_put_bool(options, "rw", rw);
11357ad9be64SKevin Wolf }
11367ad9be64SKevin Wolf 
vvfat_open(BlockDriverState * bs,QDict * options,int flags,Error ** errp)1137015a1036SMax Reitz static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
1138015a1036SMax Reitz                       Error **errp)
1139019d6b8fSAnthony Liguori {
1140019d6b8fSAnthony Liguori     BDRVVVFATState *s = bs->opaque;
11417ad9be64SKevin Wolf     int cyls, heads, secs;
11427ad9be64SKevin Wolf     bool floppy;
1143d5941ddaSWolfgang Bumiller     const char *dirname, *label;
11447ad9be64SKevin Wolf     QemuOpts *opts;
11457ad9be64SKevin Wolf     int ret;
1146019d6b8fSAnthony Liguori 
11474026f1c4SKevin Wolf     GRAPH_RDLOCK_GUARD_MAINLOOP();
11484026f1c4SKevin Wolf 
1149019d6b8fSAnthony Liguori #ifdef DEBUG
1150019d6b8fSAnthony Liguori     vvv = s;
1151019d6b8fSAnthony Liguori #endif
1152019d6b8fSAnthony Liguori 
115387ea75d5SPeter Crosthwaite     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
1154af175e85SMarkus Armbruster     if (!qemu_opts_absorb_qdict(opts, options, errp)) {
11557ad9be64SKevin Wolf         ret = -EINVAL;
11567ad9be64SKevin Wolf         goto fail;
1157273e4e03SPaolo Bonzini     }
1158273e4e03SPaolo Bonzini 
11597ad9be64SKevin Wolf     dirname = qemu_opt_get(opts, "dir");
11607ad9be64SKevin Wolf     if (!dirname) {
1161c0f92b52SPaolo Bonzini         error_setg(errp, "vvfat block driver requires a 'dir' option");
11627ad9be64SKevin Wolf         ret = -EINVAL;
11637ad9be64SKevin Wolf         goto fail;
11647ad9be64SKevin Wolf     }
11657ad9be64SKevin Wolf 
11667ad9be64SKevin Wolf     s->fat_type = qemu_opt_get_number(opts, "fat-type", 0);
11677ad9be64SKevin Wolf     floppy = qemu_opt_get_bool(opts, "floppy", false);
11687ad9be64SKevin Wolf 
1169d5941ddaSWolfgang Bumiller     memset(s->volume_label, ' ', sizeof(s->volume_label));
1170d5941ddaSWolfgang Bumiller     label = qemu_opt_get(opts, "label");
1171d5941ddaSWolfgang Bumiller     if (label) {
1172d5941ddaSWolfgang Bumiller         size_t label_length = strlen(label);
1173d5941ddaSWolfgang Bumiller         if (label_length > 11) {
1174d5941ddaSWolfgang Bumiller             error_setg(errp, "vvfat label cannot be longer than 11 bytes");
1175d5941ddaSWolfgang Bumiller             ret = -EINVAL;
1176d5941ddaSWolfgang Bumiller             goto fail;
1177d5941ddaSWolfgang Bumiller         }
1178d5941ddaSWolfgang Bumiller         memcpy(s->volume_label, label, label_length);
1179d208c50dSKevin Wolf     } else {
1180d208c50dSKevin Wolf         memcpy(s->volume_label, "QEMU VVFAT", 10);
1181d5941ddaSWolfgang Bumiller     }
1182d5941ddaSWolfgang Bumiller 
11837ad9be64SKevin Wolf     if (floppy) {
1184273e4e03SPaolo Bonzini         /* 1.44MB or 2.88MB floppy.  2.88MB can be FAT12 (default) or FAT16. */
1185273e4e03SPaolo Bonzini         if (!s->fat_type) {
1186273e4e03SPaolo Bonzini             s->fat_type = 12;
11874480e0f9SMarkus Armbruster             secs = 36;
1188273e4e03SPaolo Bonzini             s->sectors_per_cluster = 2;
1189273e4e03SPaolo Bonzini         } else {
11904480e0f9SMarkus Armbruster             secs = s->fat_type == 12 ? 18 : 36;
1191273e4e03SPaolo Bonzini             s->sectors_per_cluster = 1;
1192273e4e03SPaolo Bonzini         }
11934480e0f9SMarkus Armbruster         cyls = 80;
11944480e0f9SMarkus Armbruster         heads = 2;
1195273e4e03SPaolo Bonzini     } else {
1196273e4e03SPaolo Bonzini         /* 32MB or 504MB disk*/
1197273e4e03SPaolo Bonzini         if (!s->fat_type) {
1198273e4e03SPaolo Bonzini             s->fat_type = 16;
1199273e4e03SPaolo Bonzini         }
12004dc705dcSHervé Poussineau         s->offset_to_bootsector = 0x3f;
12014480e0f9SMarkus Armbruster         cyls = s->fat_type == 12 ? 64 : 1024;
12024480e0f9SMarkus Armbruster         heads = 16;
12034480e0f9SMarkus Armbruster         secs = 63;
1204019d6b8fSAnthony Liguori     }
12057ad9be64SKevin Wolf 
12067ad9be64SKevin Wolf     switch (s->fat_type) {
12077ad9be64SKevin Wolf     case 32:
1208b62e39b4SAlistair Francis         warn_report("FAT32 has not been tested. You are welcome to do so!");
12097ad9be64SKevin Wolf         break;
12107ad9be64SKevin Wolf     case 16:
12117ad9be64SKevin Wolf     case 12:
12127ad9be64SKevin Wolf         break;
12137ad9be64SKevin Wolf     default:
1214c0f92b52SPaolo Bonzini         error_setg(errp, "Valid FAT types are only 12, 16 and 32");
12157ad9be64SKevin Wolf         ret = -EINVAL;
12167ad9be64SKevin Wolf         goto fail;
12177ad9be64SKevin Wolf     }
12187ad9be64SKevin Wolf 
12197ad9be64SKevin Wolf 
12207ad9be64SKevin Wolf     s->bs = bs;
12217ad9be64SKevin Wolf 
12227ad9be64SKevin Wolf     /* LATER TODO: if FAT32, adjust */
12237ad9be64SKevin Wolf     s->sectors_per_cluster=0x10;
12247ad9be64SKevin Wolf 
12257ad9be64SKevin Wolf     s->current_cluster=0xffffffff;
12267ad9be64SKevin Wolf 
1227eecc7747SKevin Wolf     s->qcow = NULL;
12287ad9be64SKevin Wolf     s->qcow_filename = NULL;
12297ad9be64SKevin Wolf     s->fat2 = NULL;
12307ad9be64SKevin Wolf     s->downcase_short_names = 1;
12317ad9be64SKevin Wolf 
12323e31b4e1SThomas Huth     DLOG(fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
12333e31b4e1SThomas Huth                  dirname, cyls, heads, secs));
1234019d6b8fSAnthony Liguori 
12354dc705dcSHervé Poussineau     s->sector_count = cyls * heads * secs - s->offset_to_bootsector;
12362db9b9e9SKevin Wolf     bs->total_sectors = cyls * heads * secs;
12375a742b55SPaolo Bonzini 
12387ad9be64SKevin Wolf     if (qemu_opt_get_bool(opts, "rw", false)) {
1239e2b8247aSJeff Cody         if (!bdrv_is_read_only(bs)) {
1240eecc7747SKevin Wolf             ret = enable_write_target(bs, errp);
124178f27bd0SFam Zheng             if (ret < 0) {
12427ad9be64SKevin Wolf                 goto fail;
12437ad9be64SKevin Wolf             }
1244e2b8247aSJeff Cody         } else {
1245e2b8247aSJeff Cody             ret = -EPERM;
1246e2b8247aSJeff Cody             error_setg(errp,
1247e2b8247aSJeff Cody                        "Unable to set VVFAT to 'rw' when drive is read-only");
1248e2b8247aSJeff Cody             goto fail;
1249e2b8247aSJeff Cody         }
1250eaa2410fSKevin Wolf     } else {
1251eaa2410fSKevin Wolf         ret = bdrv_apply_auto_read_only(bs, NULL, errp);
1252e2b8247aSJeff Cody         if (ret < 0) {
1253e2b8247aSJeff Cody             goto fail;
1254e2b8247aSJeff Cody         }
1255019d6b8fSAnthony Liguori     }
1256019d6b8fSAnthony Liguori 
1257d11c8917SMarkus Armbruster     if (init_directories(s, dirname, heads, secs, errp)) {
12587ad9be64SKevin Wolf         ret = -EIO;
12597ad9be64SKevin Wolf         goto fail;
12604480e0f9SMarkus Armbruster     }
1261019d6b8fSAnthony Liguori 
12624dc705dcSHervé Poussineau     s->sector_count = s->offset_to_root_dir
12634dc705dcSHervé Poussineau                     + s->sectors_per_cluster * s->cluster_count;
1264019d6b8fSAnthony Liguori 
12653397f0cbSKevin Wolf     /* Disable migration when vvfat is used rw */
12663397f0cbSKevin Wolf     if (s->qcow) {
126781e5f78aSAlberto Garcia         error_setg(&s->migration_blocker,
126881e5f78aSAlberto Garcia                    "The vvfat (rw) format used by node '%s' "
126981e5f78aSAlberto Garcia                    "does not support live migration",
127081e5f78aSAlberto Garcia                    bdrv_get_device_or_node_name(bs));
1271e0ee3a8fSSteve Sistare         ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
1272386f6c07SMarkus Armbruster         if (ret < 0) {
1273fe44dc91SAshijeet Acharya             goto fail;
12743397f0cbSKevin Wolf         }
1275fe44dc91SAshijeet Acharya     }
1276fe44dc91SAshijeet Acharya 
12774dc705dcSHervé Poussineau     if (s->offset_to_bootsector > 0) {
1278fe44dc91SAshijeet Acharya         init_mbr(s, cyls, heads, secs);
1279fe44dc91SAshijeet Acharya     }
1280fe44dc91SAshijeet Acharya 
1281fe44dc91SAshijeet Acharya     qemu_co_mutex_init(&s->lock);
12823397f0cbSKevin Wolf 
128322c36b75SDaniella Lee     qemu_opts_del(opts);
128422c36b75SDaniella Lee 
128522c36b75SDaniella Lee     return 0;
128622c36b75SDaniella Lee 
12877ad9be64SKevin Wolf fail:
128822c36b75SDaniella Lee     g_free(s->qcow_filename);
128922c36b75SDaniella Lee     s->qcow_filename = NULL;
129022c36b75SDaniella Lee     g_free(s->cluster_buffer);
129122c36b75SDaniella Lee     s->cluster_buffer = NULL;
129222c36b75SDaniella Lee     g_free(s->used_clusters);
129322c36b75SDaniella Lee     s->used_clusters = NULL;
129422c36b75SDaniella Lee 
12957ad9be64SKevin Wolf     qemu_opts_del(opts);
12967ad9be64SKevin Wolf     return ret;
1297019d6b8fSAnthony Liguori }
1298019d6b8fSAnthony Liguori 
vvfat_refresh_limits(BlockDriverState * bs,Error ** errp)1299a6506481SEric Blake static void vvfat_refresh_limits(BlockDriverState *bs, Error **errp)
1300a6506481SEric Blake {
1301a5b8dd2cSEric Blake     bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */
1302a6506481SEric Blake }
1303a6506481SEric Blake 
vvfat_close_current_file(BDRVVVFATState * s)1304019d6b8fSAnthony Liguori static inline void vvfat_close_current_file(BDRVVVFATState *s)
1305019d6b8fSAnthony Liguori {
1306019d6b8fSAnthony Liguori     if(s->current_mapping) {
1307019d6b8fSAnthony Liguori         s->current_mapping = NULL;
1308019d6b8fSAnthony Liguori         if (s->current_fd) {
13092e1e79daSCorey Bryant                 qemu_close(s->current_fd);
1310019d6b8fSAnthony Liguori                 s->current_fd = 0;
1311019d6b8fSAnthony Liguori         }
1312019d6b8fSAnthony Liguori     }
1313019d6b8fSAnthony Liguori     s->current_cluster = -1;
1314019d6b8fSAnthony Liguori }
1315019d6b8fSAnthony Liguori 
1316019d6b8fSAnthony Liguori /* mappings between index1 and index2-1 are supposed to be ordered
1317019d6b8fSAnthony Liguori  * return value is the index of the last mapping for which end>cluster_num
1318019d6b8fSAnthony Liguori  */
find_mapping_for_cluster_aux(BDRVVVFATState * s,int cluster_num,int index1,int index2)1319019d6b8fSAnthony Liguori static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
1320019d6b8fSAnthony Liguori {
1321019d6b8fSAnthony Liguori     while(1) {
132288bf7950SBlue Swirl         int index3;
1323c227f099SAnthony Liguori         mapping_t* mapping;
1324019d6b8fSAnthony Liguori         index3=(index1+index2)/2;
1325019d6b8fSAnthony Liguori         mapping=array_get(&(s->mapping),index3);
1326019d6b8fSAnthony Liguori         assert(mapping->begin < mapping->end);
1327019d6b8fSAnthony Liguori         if(mapping->begin>=cluster_num) {
1328019d6b8fSAnthony Liguori             assert(index2!=index3 || index2==0);
1329019d6b8fSAnthony Liguori             if(index2==index3)
1330019d6b8fSAnthony Liguori                 return index1;
1331019d6b8fSAnthony Liguori             index2=index3;
1332019d6b8fSAnthony Liguori         } else {
1333019d6b8fSAnthony Liguori             if(index1==index3)
1334019d6b8fSAnthony Liguori                 return mapping->end<=cluster_num ? index2 : index1;
1335019d6b8fSAnthony Liguori             index1=index3;
1336019d6b8fSAnthony Liguori         }
1337019d6b8fSAnthony Liguori         assert(index1<=index2);
1338019d6b8fSAnthony Liguori         DLOG(mapping=array_get(&(s->mapping),index1);
1339019d6b8fSAnthony Liguori         assert(mapping->begin<=cluster_num);
1340019d6b8fSAnthony Liguori         assert(index2 >= s->mapping.next ||
1341019d6b8fSAnthony Liguori                 ((mapping = array_get(&(s->mapping),index2)) &&
1342019d6b8fSAnthony Liguori                 mapping->end>cluster_num)));
1343019d6b8fSAnthony Liguori     }
1344019d6b8fSAnthony Liguori }
1345019d6b8fSAnthony Liguori 
find_mapping_for_cluster(BDRVVVFATState * s,int cluster_num)1346c227f099SAnthony Liguori static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
1347019d6b8fSAnthony Liguori {
1348019d6b8fSAnthony Liguori     int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
1349c227f099SAnthony Liguori     mapping_t* mapping;
1350019d6b8fSAnthony Liguori     if(index>=s->mapping.next)
1351019d6b8fSAnthony Liguori         return NULL;
1352019d6b8fSAnthony Liguori     mapping=array_get(&(s->mapping),index);
1353019d6b8fSAnthony Liguori     if(mapping->begin>cluster_num)
1354019d6b8fSAnthony Liguori         return NULL;
1355019d6b8fSAnthony Liguori     assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
1356019d6b8fSAnthony Liguori     return mapping;
1357019d6b8fSAnthony Liguori }
1358019d6b8fSAnthony Liguori 
open_file(BDRVVVFATState * s,mapping_t * mapping)1359c227f099SAnthony Liguori static int open_file(BDRVVVFATState* s,mapping_t* mapping)
1360019d6b8fSAnthony Liguori {
1361019d6b8fSAnthony Liguori     if(!mapping)
1362019d6b8fSAnthony Liguori         return -1;
1363019d6b8fSAnthony Liguori     if(!s->current_mapping ||
1364019d6b8fSAnthony Liguori             strcmp(s->current_mapping->path,mapping->path)) {
1365019d6b8fSAnthony Liguori         /* open file */
1366448058aaSDaniel P. Berrangé         int fd = qemu_open_old(mapping->path,
1367448058aaSDaniel P. Berrangé                                O_RDONLY | O_BINARY | O_LARGEFILE);
1368019d6b8fSAnthony Liguori         if(fd<0)
1369019d6b8fSAnthony Liguori             return -1;
1370019d6b8fSAnthony Liguori         vvfat_close_current_file(s);
1371019d6b8fSAnthony Liguori         s->current_fd = fd;
1372019d6b8fSAnthony Liguori         s->current_mapping = mapping;
1373019d6b8fSAnthony Liguori     }
1374019d6b8fSAnthony Liguori     return 0;
1375019d6b8fSAnthony Liguori }
1376019d6b8fSAnthony Liguori 
read_cluster(BDRVVVFATState * s,int cluster_num)1377019d6b8fSAnthony Liguori static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
1378019d6b8fSAnthony Liguori {
1379019d6b8fSAnthony Liguori     if(s->current_cluster != cluster_num) {
1380019d6b8fSAnthony Liguori         int result=0;
1381019d6b8fSAnthony Liguori         off_t offset;
1382019d6b8fSAnthony Liguori         assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
1383019d6b8fSAnthony Liguori         if(!s->current_mapping
1384019d6b8fSAnthony Liguori                 || s->current_mapping->begin>cluster_num
1385019d6b8fSAnthony Liguori                 || s->current_mapping->end<=cluster_num) {
1386019d6b8fSAnthony Liguori             /* binary search of mappings for file */
1387c227f099SAnthony Liguori             mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
1388019d6b8fSAnthony Liguori 
1389019d6b8fSAnthony Liguori             assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
1390019d6b8fSAnthony Liguori 
1391019d6b8fSAnthony Liguori             if (mapping && mapping->mode & MODE_DIRECTORY) {
1392019d6b8fSAnthony Liguori                 vvfat_close_current_file(s);
1393019d6b8fSAnthony Liguori                 s->current_mapping = mapping;
1394019d6b8fSAnthony Liguori read_cluster_directory:
1395019d6b8fSAnthony Liguori                 offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
1396019d6b8fSAnthony Liguori                 s->cluster = (unsigned char*)s->directory.pointer+offset
1397019d6b8fSAnthony Liguori                         + 0x20*s->current_mapping->info.dir.first_dir_index;
1398019d6b8fSAnthony Liguori                 assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
1399019d6b8fSAnthony Liguori                 assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
1400019d6b8fSAnthony Liguori                 s->current_cluster = cluster_num;
1401019d6b8fSAnthony Liguori                 return 0;
1402019d6b8fSAnthony Liguori             }
1403019d6b8fSAnthony Liguori 
1404019d6b8fSAnthony Liguori             if(open_file(s,mapping))
1405019d6b8fSAnthony Liguori                 return -2;
1406019d6b8fSAnthony Liguori         } else if (s->current_mapping->mode & MODE_DIRECTORY)
1407019d6b8fSAnthony Liguori             goto read_cluster_directory;
1408019d6b8fSAnthony Liguori 
1409019d6b8fSAnthony Liguori         assert(s->current_fd);
1410019d6b8fSAnthony Liguori 
1411019d6b8fSAnthony Liguori         offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
1412019d6b8fSAnthony Liguori         if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
1413019d6b8fSAnthony Liguori             return -3;
1414019d6b8fSAnthony Liguori         s->cluster=s->cluster_buffer;
1415019d6b8fSAnthony Liguori         result=read(s->current_fd,s->cluster,s->cluster_size);
1416019d6b8fSAnthony Liguori         if(result<0) {
1417019d6b8fSAnthony Liguori             s->current_cluster = -1;
1418019d6b8fSAnthony Liguori             return -1;
1419019d6b8fSAnthony Liguori         }
1420019d6b8fSAnthony Liguori         s->current_cluster = cluster_num;
1421019d6b8fSAnthony Liguori     }
1422019d6b8fSAnthony Liguori     return 0;
1423019d6b8fSAnthony Liguori }
1424019d6b8fSAnthony Liguori 
1425019d6b8fSAnthony Liguori #ifdef DEBUG
print_direntry(const direntry_t * direntry)1426c227f099SAnthony Liguori static void print_direntry(const direntry_t* direntry)
1427019d6b8fSAnthony Liguori {
1428019d6b8fSAnthony Liguori     int j = 0;
1429019d6b8fSAnthony Liguori     char buffer[1024];
1430019d6b8fSAnthony Liguori 
14313e89cb04SKevin Wolf     fprintf(stderr, "direntry %p: ", direntry);
1432019d6b8fSAnthony Liguori     if(!direntry)
1433019d6b8fSAnthony Liguori         return;
1434019d6b8fSAnthony Liguori     if(is_long_name(direntry)) {
1435019d6b8fSAnthony Liguori         unsigned char* c=(unsigned char*)direntry;
1436019d6b8fSAnthony Liguori         int i;
1437019d6b8fSAnthony Liguori         for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
1438019d6b8fSAnthony Liguori #define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;}
1439019d6b8fSAnthony Liguori             ADD_CHAR(c[i]);
1440019d6b8fSAnthony Liguori         for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
1441019d6b8fSAnthony Liguori             ADD_CHAR(c[i]);
1442019d6b8fSAnthony Liguori         for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
1443019d6b8fSAnthony Liguori             ADD_CHAR(c[i]);
1444019d6b8fSAnthony Liguori         buffer[j] = 0;
1445019d6b8fSAnthony Liguori         fprintf(stderr, "%s\n", buffer);
1446019d6b8fSAnthony Liguori     } else {
1447019d6b8fSAnthony Liguori         int i;
1448019d6b8fSAnthony Liguori         for(i=0;i<11;i++)
1449019d6b8fSAnthony Liguori             ADD_CHAR(direntry->name[i]);
1450019d6b8fSAnthony Liguori         buffer[j] = 0;
1451c9eb2f3eSAlexChen         fprintf(stderr, "%s attributes=0x%02x begin=%u size=%u\n",
1452019d6b8fSAnthony Liguori                 buffer,
1453019d6b8fSAnthony Liguori                 direntry->attributes,
1454019d6b8fSAnthony Liguori                 begin_of_direntry(direntry),le32_to_cpu(direntry->size));
1455019d6b8fSAnthony Liguori     }
1456019d6b8fSAnthony Liguori }
1457019d6b8fSAnthony Liguori 
print_mapping(const mapping_t * mapping)1458c227f099SAnthony Liguori static void print_mapping(const mapping_t* mapping)
1459019d6b8fSAnthony Liguori {
1460c9eb2f3eSAlexChen     fprintf(stderr, "mapping (%p): begin, end = %u, %u, dir_index = %u, "
14613e89cb04SKevin Wolf         "first_mapping_index = %d, name = %s, mode = 0x%x, " ,
14623e89cb04SKevin Wolf         mapping, mapping->begin, mapping->end, mapping->dir_index,
14633e89cb04SKevin Wolf         mapping->first_mapping_index, mapping->path, mapping->mode);
14643e89cb04SKevin Wolf 
1465019d6b8fSAnthony Liguori     if (mapping->mode & MODE_DIRECTORY)
1466019d6b8fSAnthony Liguori         fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
1467019d6b8fSAnthony Liguori     else
1468c9eb2f3eSAlexChen         fprintf(stderr, "offset = %u\n", mapping->info.file.offset);
1469019d6b8fSAnthony Liguori }
1470019d6b8fSAnthony Liguori #endif
1471019d6b8fSAnthony Liguori 
1472eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
vvfat_read(BlockDriverState * bs,int64_t sector_num,uint8_t * buf,int nb_sectors)1473eab76d58SPaolo Bonzini vvfat_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors)
1474019d6b8fSAnthony Liguori {
1475019d6b8fSAnthony Liguori     BDRVVVFATState *s = bs->opaque;
1476019d6b8fSAnthony Liguori     int i;
1477019d6b8fSAnthony Liguori 
1478019d6b8fSAnthony Liguori     for(i=0;i<nb_sectors;i++,sector_num++) {
1479e654bfe4SPaolo Bonzini         if (sector_num >= bs->total_sectors)
1480019d6b8fSAnthony Liguori            return -1;
1481019d6b8fSAnthony Liguori         if (s->qcow) {
1482d6a644bbSEric Blake             int64_t n;
14836f712ee0SEric Blake             int ret;
1484cc323997SPaolo Bonzini             ret = bdrv_co_is_allocated(s->qcow->bs, sector_num * BDRV_SECTOR_SIZE,
1485d6a644bbSEric Blake                                        (nb_sectors - i) * BDRV_SECTOR_SIZE, &n);
14866f712ee0SEric Blake             if (ret < 0) {
14876f712ee0SEric Blake                 return ret;
14886f712ee0SEric Blake             }
14896f712ee0SEric Blake             if (ret) {
1490d6a644bbSEric Blake                 DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64
1491d6a644bbSEric Blake                              " allocated\n", sector_num,
1492d6a644bbSEric Blake                              n >> BDRV_SECTOR_BITS));
1493eab76d58SPaolo Bonzini                 if (bdrv_co_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE, n,
149432cc71deSAlberto Faria                                   buf + i * 0x200, 0) < 0) {
1495019d6b8fSAnthony Liguori                     return -1;
14967704df98SKevin Wolf                 }
1497d6a644bbSEric Blake                 i += (n >> BDRV_SECTOR_BITS) - 1;
1498d6a644bbSEric Blake                 sector_num += (n >> BDRV_SECTOR_BITS) - 1;
1499019d6b8fSAnthony Liguori                 continue;
1500019d6b8fSAnthony Liguori             }
1501d6a644bbSEric Blake             DLOG(fprintf(stderr, "sector %" PRId64 " not allocated\n",
1502d6a644bbSEric Blake                          sector_num));
1503019d6b8fSAnthony Liguori         }
15044dc705dcSHervé Poussineau         if (sector_num < s->offset_to_root_dir) {
15054dc705dcSHervé Poussineau             if (sector_num < s->offset_to_fat) {
15064dc705dcSHervé Poussineau                 memcpy(buf + i * 0x200,
15074dc705dcSHervé Poussineau                        &(s->first_sectors[sector_num * 0x200]),
15084dc705dcSHervé Poussineau                        0x200);
15094dc705dcSHervé Poussineau             } else if (sector_num < s->offset_to_fat + s->sectors_per_fat) {
15104dc705dcSHervé Poussineau                 memcpy(buf + i * 0x200,
15114dc705dcSHervé Poussineau                        &(s->fat.pointer[(sector_num
15124dc705dcSHervé Poussineau                                        - s->offset_to_fat) * 0x200]),
15134dc705dcSHervé Poussineau                        0x200);
15144dc705dcSHervé Poussineau             } else if (sector_num < s->offset_to_root_dir) {
15154dc705dcSHervé Poussineau                 memcpy(buf + i * 0x200,
15164dc705dcSHervé Poussineau                        &(s->fat.pointer[(sector_num - s->offset_to_fat
15174dc705dcSHervé Poussineau                                        - s->sectors_per_fat) * 0x200]),
15184dc705dcSHervé Poussineau                        0x200);
15194dc705dcSHervé Poussineau             }
1520019d6b8fSAnthony Liguori         } else {
15214dc705dcSHervé Poussineau             uint32_t sector = sector_num - s->offset_to_root_dir,
1522019d6b8fSAnthony Liguori             sector_offset_in_cluster=(sector%s->sectors_per_cluster),
1523019d6b8fSAnthony Liguori             cluster_num=sector/s->sectors_per_cluster;
1524e654bfe4SPaolo Bonzini             if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) {
1525019d6b8fSAnthony Liguori                 /* LATER TODO: strict: return -1; */
1526019d6b8fSAnthony Liguori                 memset(buf+i*0x200,0,0x200);
1527019d6b8fSAnthony Liguori                 continue;
1528019d6b8fSAnthony Liguori             }
1529019d6b8fSAnthony Liguori             memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
1530019d6b8fSAnthony Liguori         }
1531019d6b8fSAnthony Liguori     }
1532019d6b8fSAnthony Liguori     return 0;
1533019d6b8fSAnthony Liguori }
1534019d6b8fSAnthony Liguori 
1535eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
vvfat_co_preadv(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)1536f7ef38ddSVladimir Sementsov-Ogievskiy vvfat_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
1537f7ef38ddSVladimir Sementsov-Ogievskiy                 QEMUIOVector *qiov, BdrvRequestFlags flags)
15382914caa0SPaolo Bonzini {
15392914caa0SPaolo Bonzini     int ret;
15402914caa0SPaolo Bonzini     BDRVVVFATState *s = bs->opaque;
15414575eb49SKevin Wolf     uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
15424575eb49SKevin Wolf     int nb_sectors = bytes >> BDRV_SECTOR_BITS;
15434575eb49SKevin Wolf     void *buf;
15444575eb49SKevin Wolf 
15451bbbf32dSNir Soffer     assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
15461bbbf32dSNir Soffer     assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
15474575eb49SKevin Wolf 
15484575eb49SKevin Wolf     buf = g_try_malloc(bytes);
15494575eb49SKevin Wolf     if (bytes && buf == NULL) {
15504575eb49SKevin Wolf         return -ENOMEM;
15514575eb49SKevin Wolf     }
15524575eb49SKevin Wolf 
15532914caa0SPaolo Bonzini     qemu_co_mutex_lock(&s->lock);
15542914caa0SPaolo Bonzini     ret = vvfat_read(bs, sector_num, buf, nb_sectors);
15552914caa0SPaolo Bonzini     qemu_co_mutex_unlock(&s->lock);
15564575eb49SKevin Wolf 
15574575eb49SKevin Wolf     qemu_iovec_from_buf(qiov, 0, buf, bytes);
15584575eb49SKevin Wolf     g_free(buf);
15594575eb49SKevin Wolf 
15602914caa0SPaolo Bonzini     return ret;
15612914caa0SPaolo Bonzini }
15622914caa0SPaolo Bonzini 
1563019d6b8fSAnthony Liguori /* LATER TODO: statify all functions */
1564019d6b8fSAnthony Liguori 
1565019d6b8fSAnthony Liguori /*
1566019d6b8fSAnthony Liguori  * Idea of the write support (use snapshot):
1567019d6b8fSAnthony Liguori  *
1568019d6b8fSAnthony Liguori  * 1. check if all data is consistent, recording renames, modifications,
1569019d6b8fSAnthony Liguori  *    new files and directories (in s->commits).
1570019d6b8fSAnthony Liguori  *
1571019d6b8fSAnthony Liguori  * 2. if the data is not consistent, stop committing
1572019d6b8fSAnthony Liguori  *
1573019d6b8fSAnthony Liguori  * 3. handle renames, and create new files and directories (do not yet
1574019d6b8fSAnthony Liguori  *    write their contents)
1575019d6b8fSAnthony Liguori  *
1576019d6b8fSAnthony Liguori  * 4. walk the directories, fixing the mapping and direntries, and marking
1577019d6b8fSAnthony Liguori  *    the handled mappings as not deleted
1578019d6b8fSAnthony Liguori  *
1579019d6b8fSAnthony Liguori  * 5. commit the contents of the files
1580019d6b8fSAnthony Liguori  *
1581019d6b8fSAnthony Liguori  * 6. handle deleted files and directories
1582019d6b8fSAnthony Liguori  *
1583019d6b8fSAnthony Liguori  */
1584019d6b8fSAnthony Liguori 
1585c227f099SAnthony Liguori typedef struct commit_t {
1586019d6b8fSAnthony Liguori     char* path;
1587019d6b8fSAnthony Liguori     union {
1588019d6b8fSAnthony Liguori         struct { uint32_t cluster; } rename;
1589019d6b8fSAnthony Liguori         struct { int dir_index; uint32_t modified_offset; } writeout;
1590019d6b8fSAnthony Liguori         struct { uint32_t first_cluster; } new_file;
1591019d6b8fSAnthony Liguori         struct { uint32_t cluster; } mkdir;
1592019d6b8fSAnthony Liguori     } param;
1593019d6b8fSAnthony Liguori     /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
1594019d6b8fSAnthony Liguori     enum {
1595019d6b8fSAnthony Liguori         ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
1596019d6b8fSAnthony Liguori     } action;
1597c227f099SAnthony Liguori } commit_t;
1598019d6b8fSAnthony Liguori 
clear_commits(BDRVVVFATState * s)1599019d6b8fSAnthony Liguori static void clear_commits(BDRVVVFATState* s)
1600019d6b8fSAnthony Liguori {
1601019d6b8fSAnthony Liguori     int i;
1602c9eb2f3eSAlexChen DLOG(fprintf(stderr, "clear_commits (%u commits)\n", s->commits.next));
1603019d6b8fSAnthony Liguori     for (i = 0; i < s->commits.next; i++) {
1604c227f099SAnthony Liguori         commit_t* commit = array_get(&(s->commits), i);
1605019d6b8fSAnthony Liguori         assert(commit->path || commit->action == ACTION_WRITEOUT);
1606019d6b8fSAnthony Liguori         if (commit->action != ACTION_WRITEOUT) {
1607019d6b8fSAnthony Liguori             assert(commit->path);
1608ce137829SStefan Weil             g_free(commit->path);
1609019d6b8fSAnthony Liguori         } else
1610019d6b8fSAnthony Liguori             assert(commit->path == NULL);
1611019d6b8fSAnthony Liguori     }
1612019d6b8fSAnthony Liguori     s->commits.next = 0;
1613019d6b8fSAnthony Liguori }
1614019d6b8fSAnthony Liguori 
schedule_rename(BDRVVVFATState * s,uint32_t cluster,char * new_path)1615019d6b8fSAnthony Liguori static void schedule_rename(BDRVVVFATState* s,
1616019d6b8fSAnthony Liguori         uint32_t cluster, char* new_path)
1617019d6b8fSAnthony Liguori {
1618c227f099SAnthony Liguori     commit_t* commit = array_get_next(&(s->commits));
1619019d6b8fSAnthony Liguori     commit->path = new_path;
1620019d6b8fSAnthony Liguori     commit->param.rename.cluster = cluster;
1621019d6b8fSAnthony Liguori     commit->action = ACTION_RENAME;
1622019d6b8fSAnthony Liguori }
1623019d6b8fSAnthony Liguori 
schedule_writeout(BDRVVVFATState * s,int dir_index,uint32_t modified_offset)1624019d6b8fSAnthony Liguori static void schedule_writeout(BDRVVVFATState* s,
1625019d6b8fSAnthony Liguori         int dir_index, uint32_t modified_offset)
1626019d6b8fSAnthony Liguori {
1627c227f099SAnthony Liguori     commit_t* commit = array_get_next(&(s->commits));
1628019d6b8fSAnthony Liguori     commit->path = NULL;
1629019d6b8fSAnthony Liguori     commit->param.writeout.dir_index = dir_index;
1630019d6b8fSAnthony Liguori     commit->param.writeout.modified_offset = modified_offset;
1631019d6b8fSAnthony Liguori     commit->action = ACTION_WRITEOUT;
1632019d6b8fSAnthony Liguori }
1633019d6b8fSAnthony Liguori 
schedule_new_file(BDRVVVFATState * s,char * path,uint32_t first_cluster)1634019d6b8fSAnthony Liguori static void schedule_new_file(BDRVVVFATState* s,
1635019d6b8fSAnthony Liguori         char* path, uint32_t first_cluster)
1636019d6b8fSAnthony Liguori {
1637c227f099SAnthony Liguori     commit_t* commit = array_get_next(&(s->commits));
1638019d6b8fSAnthony Liguori     commit->path = path;
1639019d6b8fSAnthony Liguori     commit->param.new_file.first_cluster = first_cluster;
1640019d6b8fSAnthony Liguori     commit->action = ACTION_NEW_FILE;
1641019d6b8fSAnthony Liguori }
1642019d6b8fSAnthony Liguori 
schedule_mkdir(BDRVVVFATState * s,uint32_t cluster,char * path)1643019d6b8fSAnthony Liguori static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
1644019d6b8fSAnthony Liguori {
1645c227f099SAnthony Liguori     commit_t* commit = array_get_next(&(s->commits));
1646019d6b8fSAnthony Liguori     commit->path = path;
1647019d6b8fSAnthony Liguori     commit->param.mkdir.cluster = cluster;
1648019d6b8fSAnthony Liguori     commit->action = ACTION_MKDIR;
1649019d6b8fSAnthony Liguori }
1650019d6b8fSAnthony Liguori 
1651019d6b8fSAnthony Liguori typedef struct {
1652019d6b8fSAnthony Liguori     /*
1653019d6b8fSAnthony Liguori      * Since the sequence number is at most 0x3f, and the filename
1654019d6b8fSAnthony Liguori      * length is at most 13 times the sequence number, the maximal
1655019d6b8fSAnthony Liguori      * filename length is 0x3f * 13 bytes.
1656019d6b8fSAnthony Liguori      */
1657019d6b8fSAnthony Liguori     unsigned char name[0x3f * 13 + 1];
1658e03da26bSHervé Poussineau     gunichar2 name2[0x3f * 13 + 1];
1659019d6b8fSAnthony Liguori     int checksum, len;
1660019d6b8fSAnthony Liguori     int sequence_number;
1661019d6b8fSAnthony Liguori } long_file_name;
1662019d6b8fSAnthony Liguori 
lfn_init(long_file_name * lfn)1663019d6b8fSAnthony Liguori static void lfn_init(long_file_name* lfn)
1664019d6b8fSAnthony Liguori {
1665019d6b8fSAnthony Liguori    lfn->sequence_number = lfn->len = 0;
1666019d6b8fSAnthony Liguori    lfn->checksum = 0x100;
1667019d6b8fSAnthony Liguori }
1668019d6b8fSAnthony Liguori 
1669019d6b8fSAnthony Liguori /* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
parse_long_name(long_file_name * lfn,const direntry_t * direntry)1670019d6b8fSAnthony Liguori static int parse_long_name(long_file_name* lfn,
1671c227f099SAnthony Liguori         const direntry_t* direntry)
1672019d6b8fSAnthony Liguori {
1673019d6b8fSAnthony Liguori     int i, j, offset;
1674019d6b8fSAnthony Liguori     const unsigned char* pointer = (const unsigned char*)direntry;
1675019d6b8fSAnthony Liguori 
1676019d6b8fSAnthony Liguori     if (!is_long_name(direntry))
1677019d6b8fSAnthony Liguori         return 1;
1678019d6b8fSAnthony Liguori 
1679019d6b8fSAnthony Liguori     if (pointer[0] & 0x40) {
1680e03da26bSHervé Poussineau         /* first entry; do some initialization */
1681019d6b8fSAnthony Liguori         lfn->sequence_number = pointer[0] & 0x3f;
1682019d6b8fSAnthony Liguori         lfn->checksum = pointer[13];
1683019d6b8fSAnthony Liguori         lfn->name[0] = 0;
1684019d6b8fSAnthony Liguori         lfn->name[lfn->sequence_number * 13] = 0;
1685e03da26bSHervé Poussineau     } else if ((pointer[0] & 0x3f) != --lfn->sequence_number) {
1686e03da26bSHervé Poussineau         /* not the expected sequence number */
1687019d6b8fSAnthony Liguori         return -1;
1688e03da26bSHervé Poussineau     } else if (pointer[13] != lfn->checksum) {
1689e03da26bSHervé Poussineau         /* not the expected checksum */
1690019d6b8fSAnthony Liguori         return -2;
1691e03da26bSHervé Poussineau     } else if (pointer[12] || pointer[26] || pointer[27]) {
1692e03da26bSHervé Poussineau         /* invalid zero fields */
1693019d6b8fSAnthony Liguori         return -3;
1694e03da26bSHervé Poussineau     }
1695019d6b8fSAnthony Liguori 
1696019d6b8fSAnthony Liguori     offset = 13 * (lfn->sequence_number - 1);
1697019d6b8fSAnthony Liguori     for (i = 0, j = 1; i < 13; i++, j+=2) {
1698019d6b8fSAnthony Liguori         if (j == 11)
1699019d6b8fSAnthony Liguori             j = 14;
1700019d6b8fSAnthony Liguori         else if (j == 26)
1701019d6b8fSAnthony Liguori             j = 28;
1702019d6b8fSAnthony Liguori 
1703e03da26bSHervé Poussineau         if (pointer[j] == 0 && pointer[j + 1] == 0) {
1704e03da26bSHervé Poussineau             /* end of long file name */
1705e03da26bSHervé Poussineau             break;
1706e03da26bSHervé Poussineau         }
1707e03da26bSHervé Poussineau         gunichar2 c = (pointer[j + 1] << 8) + pointer[j];
1708e03da26bSHervé Poussineau         lfn->name2[offset + i] = c;
1709019d6b8fSAnthony Liguori     }
1710019d6b8fSAnthony Liguori 
1711e03da26bSHervé Poussineau     if (pointer[0] & 0x40) {
1712e03da26bSHervé Poussineau         /* first entry; set len */
1713e03da26bSHervé Poussineau         lfn->len = offset + i;
1714e03da26bSHervé Poussineau     }
1715e03da26bSHervé Poussineau     if ((pointer[0] & 0x3f) == 0x01) {
1716e03da26bSHervé Poussineau         /* last entry; finalize entry */
1717e03da26bSHervé Poussineau         glong olen;
1718e03da26bSHervé Poussineau         gchar *utf8 = g_utf16_to_utf8(lfn->name2, lfn->len, NULL, &olen, NULL);
1719e03da26bSHervé Poussineau         if (!utf8) {
1720e03da26bSHervé Poussineau             return -4;
1721e03da26bSHervé Poussineau         }
1722e03da26bSHervé Poussineau         lfn->len = olen;
1723e03da26bSHervé Poussineau         memcpy(lfn->name, utf8, olen + 1);
1724e03da26bSHervé Poussineau         g_free(utf8);
1725e03da26bSHervé Poussineau     }
1726019d6b8fSAnthony Liguori 
1727019d6b8fSAnthony Liguori     return 0;
1728019d6b8fSAnthony Liguori }
1729019d6b8fSAnthony Liguori 
1730019d6b8fSAnthony Liguori /* returns 0 if successful, >0 if no short_name, and <0 on error */
parse_short_name(BDRVVVFATState * s,long_file_name * lfn,direntry_t * direntry)1731019d6b8fSAnthony Liguori static int parse_short_name(BDRVVVFATState* s,
1732c227f099SAnthony Liguori         long_file_name* lfn, direntry_t* direntry)
1733019d6b8fSAnthony Liguori {
1734019d6b8fSAnthony Liguori     int i, j;
1735019d6b8fSAnthony Liguori 
1736019d6b8fSAnthony Liguori     if (!is_short_name(direntry))
1737019d6b8fSAnthony Liguori         return 1;
1738019d6b8fSAnthony Liguori 
1739019d6b8fSAnthony Liguori     for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
1740019d6b8fSAnthony Liguori     for (i = 0; i <= j; i++) {
1741e03da26bSHervé Poussineau         uint8_t c = direntry->name[i];
1742e03da26bSHervé Poussineau         if (c != to_valid_short_char(c)) {
1743019d6b8fSAnthony Liguori             return -1;
1744e03da26bSHervé Poussineau         } else if (s->downcase_short_names) {
1745019d6b8fSAnthony Liguori             lfn->name[i] = qemu_tolower(direntry->name[i]);
1746e03da26bSHervé Poussineau         } else {
1747019d6b8fSAnthony Liguori             lfn->name[i] = direntry->name[i];
1748019d6b8fSAnthony Liguori         }
1749e03da26bSHervé Poussineau     }
1750019d6b8fSAnthony Liguori 
1751f671d173SStefan Weil     for (j = 2; j >= 0 && direntry->name[8 + j] == ' '; j--) {
1752f671d173SStefan Weil     }
1753019d6b8fSAnthony Liguori     if (j >= 0) {
1754019d6b8fSAnthony Liguori         lfn->name[i++] = '.';
1755019d6b8fSAnthony Liguori         lfn->name[i + j + 1] = '\0';
1756019d6b8fSAnthony Liguori         for (;j >= 0; j--) {
1757f671d173SStefan Weil             uint8_t c = direntry->name[8 + j];
1758e03da26bSHervé Poussineau             if (c != to_valid_short_char(c)) {
1759019d6b8fSAnthony Liguori                 return -2;
1760f671d173SStefan Weil             } else if (s->downcase_short_names) {
1761f671d173SStefan Weil                 lfn->name[i + j] = qemu_tolower(c);
1762f671d173SStefan Weil             } else {
1763f671d173SStefan Weil                 lfn->name[i + j] = c;
1764f671d173SStefan Weil             }
1765019d6b8fSAnthony Liguori         }
1766019d6b8fSAnthony Liguori     } else
1767019d6b8fSAnthony Liguori         lfn->name[i + j + 1] = '\0';
1768019d6b8fSAnthony Liguori 
17698c4517fdSHervé Poussineau     if (lfn->name[0] == DIR_KANJI_FAKE) {
17708c4517fdSHervé Poussineau         lfn->name[0] = DIR_KANJI;
177178f002c9SHervé Poussineau     }
1772019d6b8fSAnthony Liguori     lfn->len = strlen((char*)lfn->name);
1773019d6b8fSAnthony Liguori 
1774019d6b8fSAnthony Liguori     return 0;
1775019d6b8fSAnthony Liguori }
1776019d6b8fSAnthony Liguori 
modified_fat_get(BDRVVVFATState * s,unsigned int cluster)1777019d6b8fSAnthony Liguori static inline uint32_t modified_fat_get(BDRVVVFATState* s,
1778019d6b8fSAnthony Liguori         unsigned int cluster)
1779019d6b8fSAnthony Liguori {
1780019d6b8fSAnthony Liguori     if (cluster < s->last_cluster_of_root_directory) {
1781019d6b8fSAnthony Liguori         if (cluster + 1 == s->last_cluster_of_root_directory)
1782019d6b8fSAnthony Liguori             return s->max_fat_value;
1783019d6b8fSAnthony Liguori         else
1784019d6b8fSAnthony Liguori             return cluster + 1;
1785019d6b8fSAnthony Liguori     }
1786019d6b8fSAnthony Liguori 
1787019d6b8fSAnthony Liguori     if (s->fat_type==32) {
1788019d6b8fSAnthony Liguori         uint32_t* entry=((uint32_t*)s->fat2)+cluster;
1789019d6b8fSAnthony Liguori         return le32_to_cpu(*entry);
1790019d6b8fSAnthony Liguori     } else if (s->fat_type==16) {
1791019d6b8fSAnthony Liguori         uint16_t* entry=((uint16_t*)s->fat2)+cluster;
1792019d6b8fSAnthony Liguori         return le16_to_cpu(*entry);
1793019d6b8fSAnthony Liguori     } else {
1794019d6b8fSAnthony Liguori         const uint8_t* x=s->fat2+cluster*3/2;
1795019d6b8fSAnthony Liguori         return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
1796019d6b8fSAnthony Liguori     }
1797019d6b8fSAnthony Liguori }
1798019d6b8fSAnthony Liguori 
1799eab76d58SPaolo Bonzini static inline bool coroutine_fn GRAPH_RDLOCK
cluster_was_modified(BDRVVVFATState * s,uint32_t cluster_num)1800eab76d58SPaolo Bonzini cluster_was_modified(BDRVVVFATState *s, uint32_t cluster_num)
1801019d6b8fSAnthony Liguori {
1802019d6b8fSAnthony Liguori     int was_modified = 0;
1803d6a644bbSEric Blake     int i;
1804019d6b8fSAnthony Liguori 
1805eecc7747SKevin Wolf     if (s->qcow == NULL) {
1806019d6b8fSAnthony Liguori         return 0;
1807eecc7747SKevin Wolf     }
1808019d6b8fSAnthony Liguori 
1809eecc7747SKevin Wolf     for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) {
1810cc323997SPaolo Bonzini         was_modified = bdrv_co_is_allocated(s->qcow->bs,
1811d6a644bbSEric Blake                                             (cluster2sector(s, cluster_num) +
1812d6a644bbSEric Blake                                              i) * BDRV_SECTOR_SIZE,
1813d6a644bbSEric Blake                                             BDRV_SECTOR_SIZE, NULL);
1814eecc7747SKevin Wolf     }
1815019d6b8fSAnthony Liguori 
18166f712ee0SEric Blake     /*
18176f712ee0SEric Blake      * Note that this treats failures to learn allocation status the
18186f712ee0SEric Blake      * same as if an allocation has occurred.  It's as safe as
18196f712ee0SEric Blake      * anything else, given that a failure to learn allocation status
18206f712ee0SEric Blake      * will probably result in more failures.
18216f712ee0SEric Blake      */
18226f712ee0SEric Blake     return !!was_modified;
1823019d6b8fSAnthony Liguori }
1824019d6b8fSAnthony Liguori 
get_basename(const char * path)1825019d6b8fSAnthony Liguori static const char* get_basename(const char* path)
1826019d6b8fSAnthony Liguori {
1827019d6b8fSAnthony Liguori     char* basename = strrchr(path, '/');
1828019d6b8fSAnthony Liguori     if (basename == NULL)
1829019d6b8fSAnthony Liguori         return path;
1830019d6b8fSAnthony Liguori     else
1831019d6b8fSAnthony Liguori         return basename + 1; /* strip '/' */
1832019d6b8fSAnthony Liguori }
1833019d6b8fSAnthony Liguori 
1834019d6b8fSAnthony Liguori /*
1835019d6b8fSAnthony Liguori  * The array s->used_clusters holds the states of the clusters. If it is
1836019d6b8fSAnthony Liguori  * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
1837019d6b8fSAnthony Liguori  * was modified, bit 3 is set.
1838019d6b8fSAnthony Liguori  * If any cluster is allocated, but not part of a file or directory, this
1839019d6b8fSAnthony Liguori  * driver refuses to commit.
1840019d6b8fSAnthony Liguori  */
1841019d6b8fSAnthony Liguori typedef enum {
1842019d6b8fSAnthony Liguori      USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
1843c227f099SAnthony Liguori } used_t;
1844019d6b8fSAnthony Liguori 
1845019d6b8fSAnthony Liguori /*
1846019d6b8fSAnthony Liguori  * get_cluster_count_for_direntry() not only determines how many clusters
1847019d6b8fSAnthony Liguori  * are occupied by direntry, but also if it was renamed or modified.
1848019d6b8fSAnthony Liguori  *
1849019d6b8fSAnthony Liguori  * A file is thought to be renamed *only* if there already was a file with
1850019d6b8fSAnthony Liguori  * exactly the same first cluster, but a different name.
1851019d6b8fSAnthony Liguori  *
1852019d6b8fSAnthony Liguori  * Further, the files/directories handled by this function are
1853019d6b8fSAnthony Liguori  * assumed to be *not* deleted (and *only* those).
1854019d6b8fSAnthony Liguori  */
1855eab76d58SPaolo Bonzini static uint32_t coroutine_fn GRAPH_RDLOCK
get_cluster_count_for_direntry(BDRVVVFATState * s,direntry_t * direntry,const char * path)1856eab76d58SPaolo Bonzini get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const char* path)
1857019d6b8fSAnthony Liguori {
1858019d6b8fSAnthony Liguori     /*
1859019d6b8fSAnthony Liguori      * This is a little bit tricky:
1860019d6b8fSAnthony Liguori      * IF the guest OS just inserts a cluster into the file chain,
1861019d6b8fSAnthony Liguori      * and leaves the rest alone, (i.e. the original file had clusters
1862019d6b8fSAnthony Liguori      * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
1863019d6b8fSAnthony Liguori      *
1864019d6b8fSAnthony Liguori      * - do_commit will write the cluster into the file at the given
1865019d6b8fSAnthony Liguori      *   offset, but
1866019d6b8fSAnthony Liguori      *
1867019d6b8fSAnthony Liguori      * - the cluster which is overwritten should be moved to a later
1868019d6b8fSAnthony Liguori      *   position in the file.
1869019d6b8fSAnthony Liguori      *
1870019d6b8fSAnthony Liguori      * I am not aware that any OS does something as braindead, but this
1871019d6b8fSAnthony Liguori      * situation could happen anyway when not committing for a long time.
1872019d6b8fSAnthony Liguori      * Just to be sure that this does not bite us, detect it, and copy the
1873019d6b8fSAnthony Liguori      * contents of the clusters to-be-overwritten into the qcow.
1874019d6b8fSAnthony Liguori      */
1875019d6b8fSAnthony Liguori     int copy_it = 0;
1876019d6b8fSAnthony Liguori     int was_modified = 0;
1877019d6b8fSAnthony Liguori     int32_t ret = 0;
1878019d6b8fSAnthony Liguori 
1879019d6b8fSAnthony Liguori     uint32_t cluster_num = begin_of_direntry(direntry);
1880019d6b8fSAnthony Liguori     uint32_t offset = 0;
1881019d6b8fSAnthony Liguori     int first_mapping_index = -1;
1882c227f099SAnthony Liguori     mapping_t* mapping = NULL;
1883019d6b8fSAnthony Liguori     const char* basename2 = NULL;
1884019d6b8fSAnthony Liguori 
1885019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
1886019d6b8fSAnthony Liguori 
1887019d6b8fSAnthony Liguori     /* the root directory */
1888019d6b8fSAnthony Liguori     if (cluster_num == 0)
1889019d6b8fSAnthony Liguori         return 0;
1890019d6b8fSAnthony Liguori 
1891019d6b8fSAnthony Liguori     /* write support */
1892019d6b8fSAnthony Liguori     if (s->qcow) {
1893019d6b8fSAnthony Liguori         basename2 = get_basename(path);
1894019d6b8fSAnthony Liguori 
1895019d6b8fSAnthony Liguori         mapping = find_mapping_for_cluster(s, cluster_num);
1896019d6b8fSAnthony Liguori 
1897019d6b8fSAnthony Liguori         if (mapping) {
1898019d6b8fSAnthony Liguori             const char* basename;
1899019d6b8fSAnthony Liguori 
1900019d6b8fSAnthony Liguori             assert(mapping->mode & MODE_DELETED);
1901019d6b8fSAnthony Liguori             mapping->mode &= ~MODE_DELETED;
1902019d6b8fSAnthony Liguori 
1903019d6b8fSAnthony Liguori             basename = get_basename(mapping->path);
1904019d6b8fSAnthony Liguori 
1905019d6b8fSAnthony Liguori             assert(mapping->mode & MODE_NORMAL);
1906019d6b8fSAnthony Liguori 
1907019d6b8fSAnthony Liguori             /* rename */
1908019d6b8fSAnthony Liguori             if (strcmp(basename, basename2))
19097267c094SAnthony Liguori                 schedule_rename(s, cluster_num, g_strdup(path));
1910019d6b8fSAnthony Liguori         } else if (is_file(direntry))
1911019d6b8fSAnthony Liguori             /* new file */
19127267c094SAnthony Liguori             schedule_new_file(s, g_strdup(path), cluster_num);
1913019d6b8fSAnthony Liguori         else {
191443dc2a64SBlue Swirl             abort();
1915019d6b8fSAnthony Liguori             return 0;
1916019d6b8fSAnthony Liguori         }
1917019d6b8fSAnthony Liguori     }
1918019d6b8fSAnthony Liguori 
1919019d6b8fSAnthony Liguori     while(1) {
1920019d6b8fSAnthony Liguori         if (s->qcow) {
1921019d6b8fSAnthony Liguori             if (!copy_it && cluster_was_modified(s, cluster_num)) {
1922019d6b8fSAnthony Liguori                 if (mapping == NULL ||
1923019d6b8fSAnthony Liguori                         mapping->begin > cluster_num ||
1924019d6b8fSAnthony Liguori                         mapping->end <= cluster_num)
1925019d6b8fSAnthony Liguori                 mapping = find_mapping_for_cluster(s, cluster_num);
1926019d6b8fSAnthony Liguori 
1927019d6b8fSAnthony Liguori 
1928019d6b8fSAnthony Liguori                 if (mapping &&
1929019d6b8fSAnthony Liguori                         (mapping->mode & MODE_DIRECTORY) == 0) {
1930019d6b8fSAnthony Liguori 
1931019d6b8fSAnthony Liguori                     /* was modified in qcow */
1932019d6b8fSAnthony Liguori                     if (offset != mapping->info.file.offset + s->cluster_size
1933019d6b8fSAnthony Liguori                             * (cluster_num - mapping->begin)) {
1934019d6b8fSAnthony Liguori                         /* offset of this cluster in file chain has changed */
193543dc2a64SBlue Swirl                         abort();
1936019d6b8fSAnthony Liguori                         copy_it = 1;
1937019d6b8fSAnthony Liguori                     } else if (offset == 0) {
1938019d6b8fSAnthony Liguori                         const char* basename = get_basename(mapping->path);
1939019d6b8fSAnthony Liguori 
1940019d6b8fSAnthony Liguori                         if (strcmp(basename, basename2))
1941019d6b8fSAnthony Liguori                             copy_it = 1;
1942019d6b8fSAnthony Liguori                         first_mapping_index = array_index(&(s->mapping), mapping);
1943019d6b8fSAnthony Liguori                     }
1944019d6b8fSAnthony Liguori 
1945019d6b8fSAnthony Liguori                     if (mapping->first_mapping_index != first_mapping_index
1946019d6b8fSAnthony Liguori                             && mapping->info.file.offset > 0) {
194743dc2a64SBlue Swirl                         abort();
1948019d6b8fSAnthony Liguori                         copy_it = 1;
1949019d6b8fSAnthony Liguori                     }
1950019d6b8fSAnthony Liguori 
1951019d6b8fSAnthony Liguori                     /* need to write out? */
1952019d6b8fSAnthony Liguori                     if (!was_modified && is_file(direntry)) {
1953019d6b8fSAnthony Liguori                         was_modified = 1;
1954019d6b8fSAnthony Liguori                         schedule_writeout(s, mapping->dir_index, offset);
1955019d6b8fSAnthony Liguori                     }
1956019d6b8fSAnthony Liguori                 }
1957019d6b8fSAnthony Liguori             }
1958019d6b8fSAnthony Liguori 
1959019d6b8fSAnthony Liguori             if (copy_it) {
1960d6a644bbSEric Blake                 int i;
1961019d6b8fSAnthony Liguori                 /*
1962019d6b8fSAnthony Liguori                  * This is horribly inefficient, but that is okay, since
1963019d6b8fSAnthony Liguori                  * it is rarely executed, if at all.
1964019d6b8fSAnthony Liguori                  */
1965fb2575f9SMarkus Armbruster                 int64_t offs = cluster2sector(s, cluster_num);
1966019d6b8fSAnthony Liguori 
1967019d6b8fSAnthony Liguori                 vvfat_close_current_file(s);
19687704df98SKevin Wolf                 for (i = 0; i < s->sectors_per_cluster; i++) {
1969eecc7747SKevin Wolf                     int res;
1970eecc7747SKevin Wolf 
1971cc323997SPaolo Bonzini                     res = bdrv_co_is_allocated(s->qcow->bs,
1972fb2575f9SMarkus Armbruster                                                (offs + i) * BDRV_SECTOR_SIZE,
1973d6a644bbSEric Blake                                                BDRV_SECTOR_SIZE, NULL);
19746f712ee0SEric Blake                     if (res < 0) {
19756f712ee0SEric Blake                         return -1;
19766f712ee0SEric Blake                     }
1977eecc7747SKevin Wolf                     if (!res) {
1978fb2575f9SMarkus Armbruster                         res = vvfat_read(s->bs, offs, s->cluster_buffer, 1);
1979eecc7747SKevin Wolf                         if (res) {
1980019d6b8fSAnthony Liguori                             return -1;
19817704df98SKevin Wolf                         }
1982fb2575f9SMarkus Armbruster                         res = bdrv_co_pwrite(s->qcow, offs * BDRV_SECTOR_SIZE,
198332cc71deSAlberto Faria                                              BDRV_SECTOR_SIZE, s->cluster_buffer,
198453fb7844SAlberto Faria                                              0);
1985e5a0a678SAlberto Garcia                         if (res < 0) {
1986019d6b8fSAnthony Liguori                             return -2;
1987019d6b8fSAnthony Liguori                         }
1988019d6b8fSAnthony Liguori                     }
1989019d6b8fSAnthony Liguori                 }
19907704df98SKevin Wolf             }
19917704df98SKevin Wolf         }
1992019d6b8fSAnthony Liguori 
1993019d6b8fSAnthony Liguori         ret++;
1994019d6b8fSAnthony Liguori         if (s->used_clusters[cluster_num] & USED_ANY)
1995019d6b8fSAnthony Liguori             return 0;
1996019d6b8fSAnthony Liguori         s->used_clusters[cluster_num] = USED_FILE;
1997019d6b8fSAnthony Liguori 
1998019d6b8fSAnthony Liguori         cluster_num = modified_fat_get(s, cluster_num);
1999019d6b8fSAnthony Liguori 
2000019d6b8fSAnthony Liguori         if (fat_eof(s, cluster_num))
2001019d6b8fSAnthony Liguori             return ret;
2002019d6b8fSAnthony Liguori         else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
2003019d6b8fSAnthony Liguori             return -1;
2004019d6b8fSAnthony Liguori 
2005019d6b8fSAnthony Liguori         offset += s->cluster_size;
2006019d6b8fSAnthony Liguori     }
2007019d6b8fSAnthony Liguori }
2008019d6b8fSAnthony Liguori 
2009019d6b8fSAnthony Liguori /*
2010019d6b8fSAnthony Liguori  * This function looks at the modified data (qcow).
2011019d6b8fSAnthony Liguori  * It returns 0 upon inconsistency or error, and the number of clusters
2012019d6b8fSAnthony Liguori  * used by the directory, its subdirectories and their files.
2013019d6b8fSAnthony Liguori  */
2014eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
check_directory_consistency(BDRVVVFATState * s,int cluster_num,const char * path)2015eab76d58SPaolo Bonzini check_directory_consistency(BDRVVVFATState *s, int cluster_num, const char* path)
2016019d6b8fSAnthony Liguori {
2017019d6b8fSAnthony Liguori     int ret = 0;
20187267c094SAnthony Liguori     unsigned char* cluster = g_malloc(s->cluster_size);
2019c227f099SAnthony Liguori     direntry_t* direntries = (direntry_t*)cluster;
2020c227f099SAnthony Liguori     mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
2021019d6b8fSAnthony Liguori 
2022019d6b8fSAnthony Liguori     long_file_name lfn;
2023019d6b8fSAnthony Liguori     int path_len = strlen(path);
20240d460d6fSKevin Wolf     char path2[PATH_MAX + 1];
2025019d6b8fSAnthony Liguori 
2026019d6b8fSAnthony Liguori     assert(path_len < PATH_MAX); /* len was tested before! */
2027019d6b8fSAnthony Liguori     pstrcpy(path2, sizeof(path2), path);
2028019d6b8fSAnthony Liguori     path2[path_len] = '/';
2029019d6b8fSAnthony Liguori     path2[path_len + 1] = '\0';
2030019d6b8fSAnthony Liguori 
2031019d6b8fSAnthony Liguori     if (mapping) {
2032019d6b8fSAnthony Liguori         const char* basename = get_basename(mapping->path);
2033019d6b8fSAnthony Liguori         const char* basename2 = get_basename(path);
2034019d6b8fSAnthony Liguori 
2035019d6b8fSAnthony Liguori         assert(mapping->mode & MODE_DIRECTORY);
2036019d6b8fSAnthony Liguori 
2037019d6b8fSAnthony Liguori         assert(mapping->mode & MODE_DELETED);
2038019d6b8fSAnthony Liguori         mapping->mode &= ~MODE_DELETED;
2039019d6b8fSAnthony Liguori 
2040019d6b8fSAnthony Liguori         if (strcmp(basename, basename2))
20417267c094SAnthony Liguori             schedule_rename(s, cluster_num, g_strdup(path));
2042019d6b8fSAnthony Liguori     } else
2043019d6b8fSAnthony Liguori         /* new directory */
20447267c094SAnthony Liguori         schedule_mkdir(s, cluster_num, g_strdup(path));
2045019d6b8fSAnthony Liguori 
2046019d6b8fSAnthony Liguori     lfn_init(&lfn);
2047019d6b8fSAnthony Liguori     do {
2048019d6b8fSAnthony Liguori         int i;
2049019d6b8fSAnthony Liguori         int subret = 0;
2050019d6b8fSAnthony Liguori 
2051019d6b8fSAnthony Liguori         ret++;
2052019d6b8fSAnthony Liguori 
2053019d6b8fSAnthony Liguori         if (s->used_clusters[cluster_num] & USED_ANY) {
2054019d6b8fSAnthony Liguori             fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
20556262bbd3SMarkus Armbruster             goto fail;
2056019d6b8fSAnthony Liguori         }
2057019d6b8fSAnthony Liguori         s->used_clusters[cluster_num] = USED_DIRECTORY;
2058019d6b8fSAnthony Liguori 
2059019d6b8fSAnthony Liguori DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
2060019d6b8fSAnthony Liguori         subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
2061019d6b8fSAnthony Liguori                 s->sectors_per_cluster);
2062019d6b8fSAnthony Liguori         if (subret) {
2063019d6b8fSAnthony Liguori             fprintf(stderr, "Error fetching direntries\n");
2064019d6b8fSAnthony Liguori         fail:
2065ce137829SStefan Weil             g_free(cluster);
2066019d6b8fSAnthony Liguori             return 0;
2067019d6b8fSAnthony Liguori         }
2068019d6b8fSAnthony Liguori 
2069019d6b8fSAnthony Liguori         for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
2070019d6b8fSAnthony Liguori             int cluster_count = 0;
2071019d6b8fSAnthony Liguori 
2072019d6b8fSAnthony Liguori DLOG(fprintf(stderr, "check direntry %d:\n", i); print_direntry(direntries + i));
2073019d6b8fSAnthony Liguori             if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
2074019d6b8fSAnthony Liguori                     is_free(direntries + i))
2075019d6b8fSAnthony Liguori                 continue;
2076019d6b8fSAnthony Liguori 
2077019d6b8fSAnthony Liguori             subret = parse_long_name(&lfn, direntries + i);
2078019d6b8fSAnthony Liguori             if (subret < 0) {
2079019d6b8fSAnthony Liguori                 fprintf(stderr, "Error in long name\n");
2080019d6b8fSAnthony Liguori                 goto fail;
2081019d6b8fSAnthony Liguori             }
2082019d6b8fSAnthony Liguori             if (subret == 0 || is_free(direntries + i))
2083019d6b8fSAnthony Liguori                 continue;
2084019d6b8fSAnthony Liguori 
2085019d6b8fSAnthony Liguori             if (fat_chksum(direntries+i) != lfn.checksum) {
2086019d6b8fSAnthony Liguori                 subret = parse_short_name(s, &lfn, direntries + i);
2087019d6b8fSAnthony Liguori                 if (subret < 0) {
2088019d6b8fSAnthony Liguori                     fprintf(stderr, "Error in short name (%d)\n", subret);
2089019d6b8fSAnthony Liguori                     goto fail;
2090019d6b8fSAnthony Liguori                 }
2091019d6b8fSAnthony Liguori                 if (subret > 0 || !strcmp((char*)lfn.name, ".")
2092019d6b8fSAnthony Liguori                         || !strcmp((char*)lfn.name, ".."))
2093019d6b8fSAnthony Liguori                     continue;
2094019d6b8fSAnthony Liguori             }
2095019d6b8fSAnthony Liguori             lfn.checksum = 0x100; /* cannot use long name twice */
2096019d6b8fSAnthony Liguori 
2097c79e243eSKevin Wolf             if (!valid_filename(lfn.name)) {
2098c79e243eSKevin Wolf                 fprintf(stderr, "Invalid file name\n");
2099c79e243eSKevin Wolf                 goto fail;
2100c79e243eSKevin Wolf             }
2101019d6b8fSAnthony Liguori             if (path_len + 1 + lfn.len >= PATH_MAX) {
2102019d6b8fSAnthony Liguori                 fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
2103019d6b8fSAnthony Liguori                 goto fail;
2104019d6b8fSAnthony Liguori             }
2105019d6b8fSAnthony Liguori             pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
2106019d6b8fSAnthony Liguori                     (char*)lfn.name);
2107019d6b8fSAnthony Liguori 
2108019d6b8fSAnthony Liguori             if (is_directory(direntries + i)) {
2109019d6b8fSAnthony Liguori                 if (begin_of_direntry(direntries + i) == 0) {
2110019d6b8fSAnthony Liguori                     DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
2111019d6b8fSAnthony Liguori                     goto fail;
2112019d6b8fSAnthony Liguori                 }
2113019d6b8fSAnthony Liguori                 cluster_count = check_directory_consistency(s,
2114019d6b8fSAnthony Liguori                         begin_of_direntry(direntries + i), path2);
2115019d6b8fSAnthony Liguori                 if (cluster_count == 0) {
2116019d6b8fSAnthony Liguori                     DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
2117019d6b8fSAnthony Liguori                     goto fail;
2118019d6b8fSAnthony Liguori                 }
2119019d6b8fSAnthony Liguori             } else if (is_file(direntries + i)) {
2120019d6b8fSAnthony Liguori                 /* check file size with FAT */
2121019d6b8fSAnthony Liguori                 cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
2122019d6b8fSAnthony Liguori                 if (cluster_count !=
212313385ae1SLaurent Vivier             DIV_ROUND_UP(le32_to_cpu(direntries[i].size), s->cluster_size)) {
2124019d6b8fSAnthony Liguori                     DLOG(fprintf(stderr, "Cluster count mismatch\n"));
2125019d6b8fSAnthony Liguori                     goto fail;
2126019d6b8fSAnthony Liguori                 }
2127019d6b8fSAnthony Liguori             } else
212843dc2a64SBlue Swirl                 abort(); /* cluster_count = 0; */
2129019d6b8fSAnthony Liguori 
2130019d6b8fSAnthony Liguori             ret += cluster_count;
2131019d6b8fSAnthony Liguori         }
2132019d6b8fSAnthony Liguori 
2133019d6b8fSAnthony Liguori         cluster_num = modified_fat_get(s, cluster_num);
2134019d6b8fSAnthony Liguori     } while(!fat_eof(s, cluster_num));
2135019d6b8fSAnthony Liguori 
2136ce137829SStefan Weil     g_free(cluster);
2137019d6b8fSAnthony Liguori     return ret;
2138019d6b8fSAnthony Liguori }
2139019d6b8fSAnthony Liguori 
2140019d6b8fSAnthony Liguori /* returns 1 on success */
2141eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
is_consistent(BDRVVVFATState * s)2142eab76d58SPaolo Bonzini is_consistent(BDRVVVFATState* s)
2143019d6b8fSAnthony Liguori {
2144019d6b8fSAnthony Liguori     int i, check;
2145019d6b8fSAnthony Liguori     int used_clusters_count = 0;
2146019d6b8fSAnthony Liguori 
2147019d6b8fSAnthony Liguori DLOG(checkpoint());
2148019d6b8fSAnthony Liguori     /*
2149019d6b8fSAnthony Liguori      * - get modified FAT
2150019d6b8fSAnthony Liguori      * - compare the two FATs (TODO)
2151019d6b8fSAnthony Liguori      * - get buffer for marking used clusters
2152f4649069SEric Blake      * - recurse direntries from root (using bs->bdrv_pread to make
2153019d6b8fSAnthony Liguori      *    sure to get the new data)
2154019d6b8fSAnthony Liguori      *   - check that the FAT agrees with the size
2155019d6b8fSAnthony Liguori      *   - count the number of clusters occupied by this directory and
2156019d6b8fSAnthony Liguori      *     its files
2157019d6b8fSAnthony Liguori      * - check that the cumulative used cluster count agrees with the
2158019d6b8fSAnthony Liguori      *   FAT
2159019d6b8fSAnthony Liguori      * - if all is fine, return number of used clusters
2160019d6b8fSAnthony Liguori      */
2161019d6b8fSAnthony Liguori     if (s->fat2 == NULL) {
2162019d6b8fSAnthony Liguori         int size = 0x200 * s->sectors_per_fat;
21637267c094SAnthony Liguori         s->fat2 = g_malloc(size);
2164019d6b8fSAnthony Liguori         memcpy(s->fat2, s->fat.pointer, size);
2165019d6b8fSAnthony Liguori     }
2166019d6b8fSAnthony Liguori     check = vvfat_read(s->bs,
21674dc705dcSHervé Poussineau             s->offset_to_fat, s->fat2, s->sectors_per_fat);
2168019d6b8fSAnthony Liguori     if (check) {
2169019d6b8fSAnthony Liguori         fprintf(stderr, "Could not copy fat\n");
2170019d6b8fSAnthony Liguori         return 0;
2171019d6b8fSAnthony Liguori     }
2172019d6b8fSAnthony Liguori     assert (s->used_clusters);
2173019d6b8fSAnthony Liguori     for (i = 0; i < sector2cluster(s, s->sector_count); i++)
2174019d6b8fSAnthony Liguori         s->used_clusters[i] &= ~USED_ANY;
2175019d6b8fSAnthony Liguori 
2176019d6b8fSAnthony Liguori     clear_commits(s);
2177019d6b8fSAnthony Liguori 
2178019d6b8fSAnthony Liguori     /* mark every mapped file/directory as deleted.
2179019d6b8fSAnthony Liguori      * (check_directory_consistency() will unmark those still present). */
2180019d6b8fSAnthony Liguori     if (s->qcow)
2181019d6b8fSAnthony Liguori         for (i = 0; i < s->mapping.next; i++) {
2182c227f099SAnthony Liguori             mapping_t* mapping = array_get(&(s->mapping), i);
2183019d6b8fSAnthony Liguori             if (mapping->first_mapping_index < 0)
2184019d6b8fSAnthony Liguori                 mapping->mode |= MODE_DELETED;
2185019d6b8fSAnthony Liguori         }
2186019d6b8fSAnthony Liguori 
2187019d6b8fSAnthony Liguori     used_clusters_count = check_directory_consistency(s, 0, s->path);
2188019d6b8fSAnthony Liguori     if (used_clusters_count <= 0) {
2189019d6b8fSAnthony Liguori         DLOG(fprintf(stderr, "problem in directory\n"));
2190019d6b8fSAnthony Liguori         return 0;
2191019d6b8fSAnthony Liguori     }
2192019d6b8fSAnthony Liguori 
2193019d6b8fSAnthony Liguori     check = s->last_cluster_of_root_directory;
2194019d6b8fSAnthony Liguori     for (i = check; i < sector2cluster(s, s->sector_count); i++) {
2195019d6b8fSAnthony Liguori         if (modified_fat_get(s, i)) {
2196019d6b8fSAnthony Liguori             if(!s->used_clusters[i]) {
2197019d6b8fSAnthony Liguori                 DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
2198019d6b8fSAnthony Liguori                 return 0;
2199019d6b8fSAnthony Liguori             }
2200019d6b8fSAnthony Liguori             check++;
2201019d6b8fSAnthony Liguori         }
2202019d6b8fSAnthony Liguori 
2203019d6b8fSAnthony Liguori         if (s->used_clusters[i] == USED_ALLOCATED) {
2204019d6b8fSAnthony Liguori             /* allocated, but not used... */
2205019d6b8fSAnthony Liguori             DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
2206019d6b8fSAnthony Liguori             return 0;
2207019d6b8fSAnthony Liguori         }
2208019d6b8fSAnthony Liguori     }
2209019d6b8fSAnthony Liguori 
2210019d6b8fSAnthony Liguori     if (check != used_clusters_count)
2211019d6b8fSAnthony Liguori         return 0;
2212019d6b8fSAnthony Liguori 
2213019d6b8fSAnthony Liguori     return used_clusters_count;
2214019d6b8fSAnthony Liguori }
2215019d6b8fSAnthony Liguori 
adjust_mapping_indices(BDRVVVFATState * s,int offset,int adjust)2216019d6b8fSAnthony Liguori static inline void adjust_mapping_indices(BDRVVVFATState* s,
2217019d6b8fSAnthony Liguori         int offset, int adjust)
2218019d6b8fSAnthony Liguori {
2219019d6b8fSAnthony Liguori     int i;
2220019d6b8fSAnthony Liguori 
2221019d6b8fSAnthony Liguori     for (i = 0; i < s->mapping.next; i++) {
2222c227f099SAnthony Liguori         mapping_t* mapping = array_get(&(s->mapping), i);
2223019d6b8fSAnthony Liguori 
2224019d6b8fSAnthony Liguori #define ADJUST_MAPPING_INDEX(name) \
2225019d6b8fSAnthony Liguori         if (mapping->name >= offset) \
2226019d6b8fSAnthony Liguori             mapping->name += adjust
2227019d6b8fSAnthony Liguori 
2228019d6b8fSAnthony Liguori         ADJUST_MAPPING_INDEX(first_mapping_index);
2229019d6b8fSAnthony Liguori         if (mapping->mode & MODE_DIRECTORY)
2230019d6b8fSAnthony Liguori             ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
2231019d6b8fSAnthony Liguori     }
2232019d6b8fSAnthony Liguori }
2233019d6b8fSAnthony Liguori 
2234019d6b8fSAnthony Liguori /* insert or update mapping */
insert_mapping(BDRVVVFATState * s,uint32_t begin,uint32_t end)2235c227f099SAnthony Liguori static mapping_t* insert_mapping(BDRVVVFATState* s,
2236019d6b8fSAnthony Liguori         uint32_t begin, uint32_t end)
2237019d6b8fSAnthony Liguori {
2238019d6b8fSAnthony Liguori     /*
2239019d6b8fSAnthony Liguori      * - find mapping where mapping->begin >= begin,
2240019d6b8fSAnthony Liguori      * - if mapping->begin > begin: insert
2241019d6b8fSAnthony Liguori      *   - adjust all references to mappings!
2242019d6b8fSAnthony Liguori      * - else: adjust
2243019d6b8fSAnthony Liguori      * - replace name
2244019d6b8fSAnthony Liguori      */
2245019d6b8fSAnthony Liguori     int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
2246c227f099SAnthony Liguori     mapping_t* mapping = NULL;
2247c227f099SAnthony Liguori     mapping_t* first_mapping = array_get(&(s->mapping), 0);
2248019d6b8fSAnthony Liguori 
2249019d6b8fSAnthony Liguori     if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
2250019d6b8fSAnthony Liguori             && mapping->begin < begin) {
2251019d6b8fSAnthony Liguori         mapping->end = begin;
2252019d6b8fSAnthony Liguori         index++;
2253019d6b8fSAnthony Liguori         mapping = array_get(&(s->mapping), index);
2254019d6b8fSAnthony Liguori     }
2255019d6b8fSAnthony Liguori     if (index >= s->mapping.next || mapping->begin > begin) {
2256019d6b8fSAnthony Liguori         mapping = array_insert(&(s->mapping), index, 1);
2257019d6b8fSAnthony Liguori         mapping->path = NULL;
2258019d6b8fSAnthony Liguori         adjust_mapping_indices(s, index, +1);
2259019d6b8fSAnthony Liguori     }
2260019d6b8fSAnthony Liguori 
2261019d6b8fSAnthony Liguori     mapping->begin = begin;
2262019d6b8fSAnthony Liguori     mapping->end = end;
2263019d6b8fSAnthony Liguori 
2264c227f099SAnthony Liguori DLOG(mapping_t* next_mapping;
2265019d6b8fSAnthony Liguori assert(index + 1 >= s->mapping.next ||
2266019d6b8fSAnthony Liguori ((next_mapping = array_get(&(s->mapping), index + 1)) &&
2267019d6b8fSAnthony Liguori  next_mapping->begin >= end)));
2268019d6b8fSAnthony Liguori 
2269c227f099SAnthony Liguori     if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
2270019d6b8fSAnthony Liguori         s->current_mapping = array_get(&(s->mapping),
2271019d6b8fSAnthony Liguori                 s->current_mapping - first_mapping);
2272019d6b8fSAnthony Liguori 
2273019d6b8fSAnthony Liguori     return mapping;
2274019d6b8fSAnthony Liguori }
2275019d6b8fSAnthony Liguori 
remove_mapping(BDRVVVFATState * s,int mapping_index)2276019d6b8fSAnthony Liguori static int remove_mapping(BDRVVVFATState* s, int mapping_index)
2277019d6b8fSAnthony Liguori {
2278c227f099SAnthony Liguori     mapping_t* mapping = array_get(&(s->mapping), mapping_index);
2279c227f099SAnthony Liguori     mapping_t* first_mapping = array_get(&(s->mapping), 0);
2280019d6b8fSAnthony Liguori 
2281019d6b8fSAnthony Liguori     /* free mapping */
2282ce137829SStefan Weil     if (mapping->first_mapping_index < 0) {
2283ce137829SStefan Weil         g_free(mapping->path);
2284ce137829SStefan Weil     }
2285019d6b8fSAnthony Liguori 
2286019d6b8fSAnthony Liguori     /* remove from s->mapping */
2287019d6b8fSAnthony Liguori     array_remove(&(s->mapping), mapping_index);
2288019d6b8fSAnthony Liguori 
2289019d6b8fSAnthony Liguori     /* adjust all references to mappings */
2290019d6b8fSAnthony Liguori     adjust_mapping_indices(s, mapping_index, -1);
2291019d6b8fSAnthony Liguori 
2292c227f099SAnthony Liguori     if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
2293019d6b8fSAnthony Liguori         s->current_mapping = array_get(&(s->mapping),
2294019d6b8fSAnthony Liguori                 s->current_mapping - first_mapping);
2295019d6b8fSAnthony Liguori 
2296019d6b8fSAnthony Liguori     return 0;
2297019d6b8fSAnthony Liguori }
2298019d6b8fSAnthony Liguori 
adjust_dirindices(BDRVVVFATState * s,int offset,int adjust)2299019d6b8fSAnthony Liguori static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
2300019d6b8fSAnthony Liguori {
2301019d6b8fSAnthony Liguori     int i;
2302019d6b8fSAnthony Liguori     for (i = 0; i < s->mapping.next; i++) {
2303c227f099SAnthony Liguori         mapping_t* mapping = array_get(&(s->mapping), i);
2304019d6b8fSAnthony Liguori         if (mapping->dir_index >= offset)
2305019d6b8fSAnthony Liguori             mapping->dir_index += adjust;
2306019d6b8fSAnthony Liguori         if ((mapping->mode & MODE_DIRECTORY) &&
2307019d6b8fSAnthony Liguori                 mapping->info.dir.first_dir_index >= offset)
2308019d6b8fSAnthony Liguori             mapping->info.dir.first_dir_index += adjust;
2309019d6b8fSAnthony Liguori     }
2310019d6b8fSAnthony Liguori }
2311019d6b8fSAnthony Liguori 
insert_direntries(BDRVVVFATState * s,int dir_index,int count)2312c227f099SAnthony Liguori static direntry_t* insert_direntries(BDRVVVFATState* s,
2313019d6b8fSAnthony Liguori         int dir_index, int count)
2314019d6b8fSAnthony Liguori {
2315019d6b8fSAnthony Liguori     /*
2316019d6b8fSAnthony Liguori      * make room in s->directory,
2317019d6b8fSAnthony Liguori      * adjust_dirindices
2318019d6b8fSAnthony Liguori      */
2319c227f099SAnthony Liguori     direntry_t* result = array_insert(&(s->directory), dir_index, count);
2320019d6b8fSAnthony Liguori     if (result == NULL)
2321019d6b8fSAnthony Liguori         return NULL;
2322019d6b8fSAnthony Liguori     adjust_dirindices(s, dir_index, count);
2323019d6b8fSAnthony Liguori     return result;
2324019d6b8fSAnthony Liguori }
2325019d6b8fSAnthony Liguori 
remove_direntries(BDRVVVFATState * s,int dir_index,int count)2326019d6b8fSAnthony Liguori static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
2327019d6b8fSAnthony Liguori {
2328019d6b8fSAnthony Liguori     int ret = array_remove_slice(&(s->directory), dir_index, count);
2329019d6b8fSAnthony Liguori     if (ret)
2330019d6b8fSAnthony Liguori         return ret;
2331019d6b8fSAnthony Liguori     adjust_dirindices(s, dir_index, -count);
2332019d6b8fSAnthony Liguori     return 0;
2333019d6b8fSAnthony Liguori }
2334019d6b8fSAnthony Liguori 
2335019d6b8fSAnthony Liguori /*
2336019d6b8fSAnthony Liguori  * Adapt the mappings of the cluster chain starting at first cluster
2337019d6b8fSAnthony Liguori  * (i.e. if a file starts at first_cluster, the chain is followed according
2338019d6b8fSAnthony Liguori  * to the modified fat, and the corresponding entries in s->mapping are
2339019d6b8fSAnthony Liguori  * adjusted)
2340019d6b8fSAnthony Liguori  */
commit_mappings(BDRVVVFATState * s,uint32_t first_cluster,int dir_index)2341019d6b8fSAnthony Liguori static int commit_mappings(BDRVVVFATState* s,
2342019d6b8fSAnthony Liguori         uint32_t first_cluster, int dir_index)
2343019d6b8fSAnthony Liguori {
2344c227f099SAnthony Liguori     mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2345c227f099SAnthony Liguori     direntry_t* direntry = array_get(&(s->directory), dir_index);
2346019d6b8fSAnthony Liguori     uint32_t cluster = first_cluster;
2347019d6b8fSAnthony Liguori 
2348019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2349019d6b8fSAnthony Liguori 
2350019d6b8fSAnthony Liguori     assert(mapping);
2351019d6b8fSAnthony Liguori     assert(mapping->begin == first_cluster);
2352019d6b8fSAnthony Liguori     mapping->first_mapping_index = -1;
2353019d6b8fSAnthony Liguori     mapping->dir_index = dir_index;
2354019d6b8fSAnthony Liguori     mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
2355019d6b8fSAnthony Liguori         MODE_DIRECTORY : MODE_NORMAL;
2356019d6b8fSAnthony Liguori 
2357019d6b8fSAnthony Liguori     while (!fat_eof(s, cluster)) {
2358019d6b8fSAnthony Liguori         uint32_t c, c1;
2359019d6b8fSAnthony Liguori 
2360019d6b8fSAnthony Liguori         for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
2361019d6b8fSAnthony Liguori                 c = c1, c1 = modified_fat_get(s, c1));
2362019d6b8fSAnthony Liguori 
2363019d6b8fSAnthony Liguori         c++;
2364019d6b8fSAnthony Liguori         if (c > mapping->end) {
2365019d6b8fSAnthony Liguori             int index = array_index(&(s->mapping), mapping);
2366019d6b8fSAnthony Liguori             int i, max_i = s->mapping.next - index;
2367019d6b8fSAnthony Liguori             for (i = 1; i < max_i && mapping[i].begin < c; i++);
2368019d6b8fSAnthony Liguori             while (--i > 0)
2369019d6b8fSAnthony Liguori                 remove_mapping(s, index + 1);
2370019d6b8fSAnthony Liguori         }
2371019d6b8fSAnthony Liguori         assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
2372019d6b8fSAnthony Liguori                 || mapping[1].begin >= c);
2373019d6b8fSAnthony Liguori         mapping->end = c;
2374019d6b8fSAnthony Liguori 
2375019d6b8fSAnthony Liguori         if (!fat_eof(s, c1)) {
2376019d6b8fSAnthony Liguori             int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
2377c227f099SAnthony Liguori             mapping_t* next_mapping = i >= s->mapping.next ? NULL :
2378019d6b8fSAnthony Liguori                 array_get(&(s->mapping), i);
2379019d6b8fSAnthony Liguori 
2380019d6b8fSAnthony Liguori             if (next_mapping == NULL || next_mapping->begin > c1) {
2381019d6b8fSAnthony Liguori                 int i1 = array_index(&(s->mapping), mapping);
2382019d6b8fSAnthony Liguori 
2383019d6b8fSAnthony Liguori                 next_mapping = insert_mapping(s, c1, c1+1);
2384019d6b8fSAnthony Liguori 
2385019d6b8fSAnthony Liguori                 if (c1 < c)
2386019d6b8fSAnthony Liguori                     i1++;
2387019d6b8fSAnthony Liguori                 mapping = array_get(&(s->mapping), i1);
2388019d6b8fSAnthony Liguori             }
2389019d6b8fSAnthony Liguori 
2390019d6b8fSAnthony Liguori             next_mapping->dir_index = mapping->dir_index;
2391019d6b8fSAnthony Liguori             next_mapping->first_mapping_index =
2392019d6b8fSAnthony Liguori                 mapping->first_mapping_index < 0 ?
2393019d6b8fSAnthony Liguori                 array_index(&(s->mapping), mapping) :
2394019d6b8fSAnthony Liguori                 mapping->first_mapping_index;
2395019d6b8fSAnthony Liguori             next_mapping->path = mapping->path;
2396019d6b8fSAnthony Liguori             next_mapping->mode = mapping->mode;
2397019d6b8fSAnthony Liguori             next_mapping->read_only = mapping->read_only;
2398019d6b8fSAnthony Liguori             if (mapping->mode & MODE_DIRECTORY) {
2399019d6b8fSAnthony Liguori                 next_mapping->info.dir.parent_mapping_index =
2400019d6b8fSAnthony Liguori                         mapping->info.dir.parent_mapping_index;
2401019d6b8fSAnthony Liguori                 next_mapping->info.dir.first_dir_index =
2402019d6b8fSAnthony Liguori                         mapping->info.dir.first_dir_index +
2403019d6b8fSAnthony Liguori                         0x10 * s->sectors_per_cluster *
2404019d6b8fSAnthony Liguori                         (mapping->end - mapping->begin);
2405019d6b8fSAnthony Liguori             } else
2406019d6b8fSAnthony Liguori                 next_mapping->info.file.offset = mapping->info.file.offset +
2407019d6b8fSAnthony Liguori                         mapping->end - mapping->begin;
2408019d6b8fSAnthony Liguori 
2409019d6b8fSAnthony Liguori             mapping = next_mapping;
2410019d6b8fSAnthony Liguori         }
2411019d6b8fSAnthony Liguori 
2412019d6b8fSAnthony Liguori         cluster = c1;
2413019d6b8fSAnthony Liguori     }
2414019d6b8fSAnthony Liguori 
2415019d6b8fSAnthony Liguori     return 0;
2416019d6b8fSAnthony Liguori }
2417019d6b8fSAnthony Liguori 
2418eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
commit_direntries(BDRVVVFATState * s,int dir_index,int parent_mapping_index)2419eab76d58SPaolo Bonzini commit_direntries(BDRVVVFATState* s, int dir_index, int parent_mapping_index)
2420019d6b8fSAnthony Liguori {
2421c227f099SAnthony Liguori     direntry_t* direntry = array_get(&(s->directory), dir_index);
2422019d6b8fSAnthony Liguori     uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
2423c227f099SAnthony Liguori     mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2424019d6b8fSAnthony Liguori     int factor = 0x10 * s->sectors_per_cluster;
2425019d6b8fSAnthony Liguori     int old_cluster_count, new_cluster_count;
24268d9401c2SLiam Merwick     int current_dir_index;
24278d9401c2SLiam Merwick     int first_dir_index;
2428019d6b8fSAnthony Liguori     int ret, i;
2429019d6b8fSAnthony Liguori     uint32_t c;
2430019d6b8fSAnthony Liguori 
2431019d6b8fSAnthony Liguori     assert(direntry);
2432019d6b8fSAnthony Liguori     assert(mapping);
2433019d6b8fSAnthony Liguori     assert(mapping->begin == first_cluster);
2434019d6b8fSAnthony Liguori     assert(mapping->info.dir.first_dir_index < s->directory.next);
2435019d6b8fSAnthony Liguori     assert(mapping->mode & MODE_DIRECTORY);
2436019d6b8fSAnthony Liguori     assert(dir_index == 0 || is_directory(direntry));
2437019d6b8fSAnthony Liguori 
24388d9401c2SLiam Merwick     DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n",
24398d9401c2SLiam Merwick                  mapping->path, parent_mapping_index));
24408d9401c2SLiam Merwick 
24418d9401c2SLiam Merwick     current_dir_index = mapping->info.dir.first_dir_index;
24428d9401c2SLiam Merwick     first_dir_index = current_dir_index;
2443019d6b8fSAnthony Liguori     mapping->info.dir.parent_mapping_index = parent_mapping_index;
2444019d6b8fSAnthony Liguori 
2445019d6b8fSAnthony Liguori     if (first_cluster == 0) {
2446019d6b8fSAnthony Liguori         old_cluster_count = new_cluster_count =
2447019d6b8fSAnthony Liguori             s->last_cluster_of_root_directory;
2448019d6b8fSAnthony Liguori     } else {
2449019d6b8fSAnthony Liguori         for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2450019d6b8fSAnthony Liguori                 c = fat_get(s, c))
2451019d6b8fSAnthony Liguori             old_cluster_count++;
2452019d6b8fSAnthony Liguori 
2453019d6b8fSAnthony Liguori         for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2454019d6b8fSAnthony Liguori                 c = modified_fat_get(s, c))
2455019d6b8fSAnthony Liguori             new_cluster_count++;
2456019d6b8fSAnthony Liguori     }
2457019d6b8fSAnthony Liguori 
2458019d6b8fSAnthony Liguori     if (new_cluster_count > old_cluster_count) {
2459019d6b8fSAnthony Liguori         if (insert_direntries(s,
2460019d6b8fSAnthony Liguori                 current_dir_index + factor * old_cluster_count,
2461019d6b8fSAnthony Liguori                 factor * (new_cluster_count - old_cluster_count)) == NULL)
2462019d6b8fSAnthony Liguori             return -1;
2463019d6b8fSAnthony Liguori     } else if (new_cluster_count < old_cluster_count)
2464019d6b8fSAnthony Liguori         remove_direntries(s,
2465019d6b8fSAnthony Liguori                 current_dir_index + factor * new_cluster_count,
2466019d6b8fSAnthony Liguori                 factor * (old_cluster_count - new_cluster_count));
2467019d6b8fSAnthony Liguori 
2468019d6b8fSAnthony Liguori     for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
2469ebb72c9fSKevin Wolf         direntry_t *first_direntry;
2470fb2575f9SMarkus Armbruster 
2471fb2575f9SMarkus Armbruster         direntry = array_get(&(s->directory), current_dir_index);
2472fb2575f9SMarkus Armbruster         ret = vvfat_read(s->bs, cluster2sector(s, c), (uint8_t *)direntry,
2473019d6b8fSAnthony Liguori                 s->sectors_per_cluster);
2474019d6b8fSAnthony Liguori         if (ret)
2475019d6b8fSAnthony Liguori             return ret;
2476ebb72c9fSKevin Wolf 
2477ebb72c9fSKevin Wolf         /* The first directory entry on the filesystem is the volume name */
2478ebb72c9fSKevin Wolf         first_direntry = (direntry_t*) s->directory.pointer;
2479ebb72c9fSKevin Wolf         assert(!memcmp(first_direntry->name, s->volume_label, 11));
2480ebb72c9fSKevin Wolf 
2481019d6b8fSAnthony Liguori         current_dir_index += factor;
2482019d6b8fSAnthony Liguori     }
2483019d6b8fSAnthony Liguori 
2484019d6b8fSAnthony Liguori     ret = commit_mappings(s, first_cluster, dir_index);
2485019d6b8fSAnthony Liguori     if (ret)
2486019d6b8fSAnthony Liguori         return ret;
2487019d6b8fSAnthony Liguori 
2488019d6b8fSAnthony Liguori     /* recurse */
2489019d6b8fSAnthony Liguori     for (i = 0; i < factor * new_cluster_count; i++) {
2490019d6b8fSAnthony Liguori         direntry = array_get(&(s->directory), first_dir_index + i);
2491019d6b8fSAnthony Liguori         if (is_directory(direntry) && !is_dot(direntry)) {
2492019d6b8fSAnthony Liguori             mapping = find_mapping_for_cluster(s, first_cluster);
24938d9401c2SLiam Merwick             if (mapping == NULL) {
24948d9401c2SLiam Merwick                 return -1;
24958d9401c2SLiam Merwick             }
2496019d6b8fSAnthony Liguori             assert(mapping->mode & MODE_DIRECTORY);
2497019d6b8fSAnthony Liguori             ret = commit_direntries(s, first_dir_index + i,
2498019d6b8fSAnthony Liguori                 array_index(&(s->mapping), mapping));
2499019d6b8fSAnthony Liguori             if (ret)
2500019d6b8fSAnthony Liguori                 return ret;
2501019d6b8fSAnthony Liguori         }
2502019d6b8fSAnthony Liguori     }
2503019d6b8fSAnthony Liguori 
2504019d6b8fSAnthony Liguori     return 0;
2505019d6b8fSAnthony Liguori }
2506019d6b8fSAnthony Liguori 
2507019d6b8fSAnthony Liguori /* commit one file (adjust contents, adjust mapping),
2508019d6b8fSAnthony Liguori    return first_mapping_index */
2509eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
commit_one_file(BDRVVVFATState * s,int dir_index,uint32_t offset)2510eab76d58SPaolo Bonzini commit_one_file(BDRVVVFATState* s, int dir_index, uint32_t offset)
2511019d6b8fSAnthony Liguori {
2512c227f099SAnthony Liguori     direntry_t* direntry = array_get(&(s->directory), dir_index);
2513019d6b8fSAnthony Liguori     uint32_t c = begin_of_direntry(direntry);
2514019d6b8fSAnthony Liguori     uint32_t first_cluster = c;
2515c227f099SAnthony Liguori     mapping_t* mapping = find_mapping_for_cluster(s, c);
2516019d6b8fSAnthony Liguori     uint32_t size = filesize_of_direntry(direntry);
2517443ba6beSKevin Wolf     char *cluster;
2518019d6b8fSAnthony Liguori     uint32_t i;
2519019d6b8fSAnthony Liguori     int fd = 0;
2520019d6b8fSAnthony Liguori 
2521019d6b8fSAnthony Liguori     assert(offset < size);
2522019d6b8fSAnthony Liguori     assert((offset % s->cluster_size) == 0);
2523019d6b8fSAnthony Liguori 
25248d9401c2SLiam Merwick     if (mapping == NULL) {
25258d9401c2SLiam Merwick         return -1;
25268d9401c2SLiam Merwick     }
25278d9401c2SLiam Merwick 
2528019d6b8fSAnthony Liguori     for (i = s->cluster_size; i < offset; i += s->cluster_size)
2529019d6b8fSAnthony Liguori         c = modified_fat_get(s, c);
2530019d6b8fSAnthony Liguori 
2531448058aaSDaniel P. Berrangé     fd = qemu_open_old(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
2532019d6b8fSAnthony Liguori     if (fd < 0) {
2533019d6b8fSAnthony Liguori         fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
2534019d6b8fSAnthony Liguori                 strerror(errno), errno);
2535019d6b8fSAnthony Liguori         return fd;
2536019d6b8fSAnthony Liguori     }
2537ce137829SStefan Weil     if (offset > 0) {
2538ce137829SStefan Weil         if (lseek(fd, offset, SEEK_SET) != offset) {
25392e1e79daSCorey Bryant             qemu_close(fd);
2540019d6b8fSAnthony Liguori             return -3;
2541ce137829SStefan Weil         }
2542ce137829SStefan Weil     }
2543019d6b8fSAnthony Liguori 
2544443ba6beSKevin Wolf     cluster = g_malloc(s->cluster_size);
2545443ba6beSKevin Wolf 
2546019d6b8fSAnthony Liguori     while (offset < size) {
2547019d6b8fSAnthony Liguori         uint32_t c1;
2548019d6b8fSAnthony Liguori         int rest_size = (size - offset > s->cluster_size ?
2549019d6b8fSAnthony Liguori                 s->cluster_size : size - offset);
2550019d6b8fSAnthony Liguori         int ret;
2551019d6b8fSAnthony Liguori 
2552019d6b8fSAnthony Liguori         c1 = modified_fat_get(s, c);
2553019d6b8fSAnthony Liguori 
2554019d6b8fSAnthony Liguori         assert((size - offset == 0 && fat_eof(s, c)) ||
2555019d6b8fSAnthony Liguori                 (size > offset && c >=2 && !fat_eof(s, c)));
2556019d6b8fSAnthony Liguori 
2557019d6b8fSAnthony Liguori         ret = vvfat_read(s->bs, cluster2sector(s, c),
255878ee96deSMarc-André Lureau             (uint8_t*)cluster, DIV_ROUND_UP(rest_size, 0x200));
2559019d6b8fSAnthony Liguori 
2560ce137829SStefan Weil         if (ret < 0) {
25612e1e79daSCorey Bryant             qemu_close(fd);
2562ce137829SStefan Weil             g_free(cluster);
2563019d6b8fSAnthony Liguori             return ret;
2564ce137829SStefan Weil         }
2565019d6b8fSAnthony Liguori 
2566ce137829SStefan Weil         if (write(fd, cluster, rest_size) < 0) {
25672e1e79daSCorey Bryant             qemu_close(fd);
2568ce137829SStefan Weil             g_free(cluster);
2569019d6b8fSAnthony Liguori             return -2;
2570ce137829SStefan Weil         }
2571019d6b8fSAnthony Liguori 
2572019d6b8fSAnthony Liguori         offset += rest_size;
2573019d6b8fSAnthony Liguori         c = c1;
2574019d6b8fSAnthony Liguori     }
2575019d6b8fSAnthony Liguori 
25762dedf83eSKirill A. Shutemov     if (ftruncate(fd, size)) {
25772dedf83eSKirill A. Shutemov         perror("ftruncate()");
25782e1e79daSCorey Bryant         qemu_close(fd);
2579ce137829SStefan Weil         g_free(cluster);
25802dedf83eSKirill A. Shutemov         return -4;
25812dedf83eSKirill A. Shutemov     }
25822e1e79daSCorey Bryant     qemu_close(fd);
2583ce137829SStefan Weil     g_free(cluster);
2584019d6b8fSAnthony Liguori 
2585019d6b8fSAnthony Liguori     return commit_mappings(s, first_cluster, dir_index);
2586019d6b8fSAnthony Liguori }
2587019d6b8fSAnthony Liguori 
2588019d6b8fSAnthony Liguori #ifdef DEBUG
2589019d6b8fSAnthony Liguori /* test, if all mappings point to valid direntries */
check1(BDRVVVFATState * s)2590019d6b8fSAnthony Liguori static void check1(BDRVVVFATState* s)
2591019d6b8fSAnthony Liguori {
2592019d6b8fSAnthony Liguori     int i;
2593019d6b8fSAnthony Liguori     for (i = 0; i < s->mapping.next; i++) {
2594c227f099SAnthony Liguori         mapping_t* mapping = array_get(&(s->mapping), i);
2595019d6b8fSAnthony Liguori         if (mapping->mode & MODE_DELETED) {
2596019d6b8fSAnthony Liguori             fprintf(stderr, "deleted\n");
2597019d6b8fSAnthony Liguori             continue;
2598019d6b8fSAnthony Liguori         }
2599019d6b8fSAnthony Liguori         assert(mapping->dir_index < s->directory.next);
2600c227f099SAnthony Liguori         direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
2601019d6b8fSAnthony Liguori         assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
2602019d6b8fSAnthony Liguori         if (mapping->mode & MODE_DIRECTORY) {
2603019d6b8fSAnthony Liguori             assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
2604019d6b8fSAnthony Liguori             assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
2605019d6b8fSAnthony Liguori         }
2606019d6b8fSAnthony Liguori     }
2607019d6b8fSAnthony Liguori }
2608019d6b8fSAnthony Liguori 
2609019d6b8fSAnthony Liguori /* test, if all direntries have mappings */
check2(BDRVVVFATState * s)2610019d6b8fSAnthony Liguori static void check2(BDRVVVFATState* s)
2611019d6b8fSAnthony Liguori {
2612019d6b8fSAnthony Liguori     int i;
2613019d6b8fSAnthony Liguori     int first_mapping = -1;
2614019d6b8fSAnthony Liguori 
2615019d6b8fSAnthony Liguori     for (i = 0; i < s->directory.next; i++) {
2616c227f099SAnthony Liguori         direntry_t* direntry = array_get(&(s->directory), i);
2617019d6b8fSAnthony Liguori 
2618019d6b8fSAnthony Liguori         if (is_short_name(direntry) && begin_of_direntry(direntry)) {
2619c227f099SAnthony Liguori             mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
2620019d6b8fSAnthony Liguori             assert(mapping);
2621019d6b8fSAnthony Liguori             assert(mapping->dir_index == i || is_dot(direntry));
2622019d6b8fSAnthony Liguori             assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
2623019d6b8fSAnthony Liguori         }
2624019d6b8fSAnthony Liguori 
2625019d6b8fSAnthony Liguori         if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
2626019d6b8fSAnthony Liguori             /* cluster start */
2627019d6b8fSAnthony Liguori             int j, count = 0;
2628019d6b8fSAnthony Liguori 
2629019d6b8fSAnthony Liguori             for (j = 0; j < s->mapping.next; j++) {
2630c227f099SAnthony Liguori                 mapping_t* mapping = array_get(&(s->mapping), j);
2631019d6b8fSAnthony Liguori                 if (mapping->mode & MODE_DELETED)
2632019d6b8fSAnthony Liguori                     continue;
2633019d6b8fSAnthony Liguori                 if (mapping->mode & MODE_DIRECTORY) {
2634019d6b8fSAnthony Liguori                     if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
2635019d6b8fSAnthony Liguori                         assert(++count == 1);
2636019d6b8fSAnthony Liguori                         if (mapping->first_mapping_index == -1)
2637019d6b8fSAnthony Liguori                             first_mapping = array_index(&(s->mapping), mapping);
2638019d6b8fSAnthony Liguori                         else
2639019d6b8fSAnthony Liguori                             assert(first_mapping == mapping->first_mapping_index);
2640019d6b8fSAnthony Liguori                         if (mapping->info.dir.parent_mapping_index < 0)
2641019d6b8fSAnthony Liguori                             assert(j == 0);
2642019d6b8fSAnthony Liguori                         else {
2643c227f099SAnthony Liguori                             mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
2644019d6b8fSAnthony Liguori                             assert(parent->mode & MODE_DIRECTORY);
2645019d6b8fSAnthony Liguori                             assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
2646019d6b8fSAnthony Liguori                         }
2647019d6b8fSAnthony Liguori                     }
2648019d6b8fSAnthony Liguori                 }
2649019d6b8fSAnthony Liguori             }
2650019d6b8fSAnthony Liguori             if (count == 0)
2651019d6b8fSAnthony Liguori                 first_mapping = -1;
2652019d6b8fSAnthony Liguori         }
2653019d6b8fSAnthony Liguori     }
2654019d6b8fSAnthony Liguori }
2655019d6b8fSAnthony Liguori #endif
2656019d6b8fSAnthony Liguori 
handle_renames_and_mkdirs(BDRVVVFATState * s)2657019d6b8fSAnthony Liguori static int handle_renames_and_mkdirs(BDRVVVFATState* s)
2658019d6b8fSAnthony Liguori {
2659019d6b8fSAnthony Liguori     int i;
2660019d6b8fSAnthony Liguori 
2661019d6b8fSAnthony Liguori #ifdef DEBUG
2662019d6b8fSAnthony Liguori     fprintf(stderr, "handle_renames\n");
2663019d6b8fSAnthony Liguori     for (i = 0; i < s->commits.next; i++) {
2664c227f099SAnthony Liguori         commit_t* commit = array_get(&(s->commits), i);
2665c9eb2f3eSAlexChen         fprintf(stderr, "%d, %s (%u, %d)\n", i,
2666c9eb2f3eSAlexChen                 commit->path ? commit->path : "(null)",
2667c9eb2f3eSAlexChen                 commit->param.rename.cluster, commit->action);
2668019d6b8fSAnthony Liguori     }
2669019d6b8fSAnthony Liguori #endif
2670019d6b8fSAnthony Liguori 
2671019d6b8fSAnthony Liguori     for (i = 0; i < s->commits.next;) {
2672c227f099SAnthony Liguori         commit_t* commit = array_get(&(s->commits), i);
2673019d6b8fSAnthony Liguori         if (commit->action == ACTION_RENAME) {
2674c227f099SAnthony Liguori             mapping_t* mapping = find_mapping_for_cluster(s,
2675019d6b8fSAnthony Liguori                     commit->param.rename.cluster);
26768d9401c2SLiam Merwick             char *old_path;
2677019d6b8fSAnthony Liguori 
26788d9401c2SLiam Merwick             if (mapping == NULL) {
26798d9401c2SLiam Merwick                 return -1;
26808d9401c2SLiam Merwick             }
26818d9401c2SLiam Merwick             old_path = mapping->path;
2682019d6b8fSAnthony Liguori             assert(commit->path);
2683019d6b8fSAnthony Liguori             mapping->path = commit->path;
2684019d6b8fSAnthony Liguori             if (rename(old_path, mapping->path))
2685019d6b8fSAnthony Liguori                 return -2;
2686019d6b8fSAnthony Liguori 
2687019d6b8fSAnthony Liguori             if (mapping->mode & MODE_DIRECTORY) {
2688019d6b8fSAnthony Liguori                 int l1 = strlen(mapping->path);
2689019d6b8fSAnthony Liguori                 int l2 = strlen(old_path);
2690019d6b8fSAnthony Liguori                 int diff = l1 - l2;
2691c227f099SAnthony Liguori                 direntry_t* direntry = array_get(&(s->directory),
2692019d6b8fSAnthony Liguori                         mapping->info.dir.first_dir_index);
2693019d6b8fSAnthony Liguori                 uint32_t c = mapping->begin;
2694fb2575f9SMarkus Armbruster                 int j = 0;
2695019d6b8fSAnthony Liguori 
2696019d6b8fSAnthony Liguori                 /* recurse */
2697019d6b8fSAnthony Liguori                 while (!fat_eof(s, c)) {
2698019d6b8fSAnthony Liguori                     do {
2699fb2575f9SMarkus Armbruster                         direntry_t *d = direntry + j;
2700019d6b8fSAnthony Liguori 
2701019d6b8fSAnthony Liguori                         if (is_file(d) || (is_directory(d) && !is_dot(d))) {
27028d9401c2SLiam Merwick                             int l;
27038d9401c2SLiam Merwick                             char *new_path;
2704c227f099SAnthony Liguori                             mapping_t* m = find_mapping_for_cluster(s,
2705019d6b8fSAnthony Liguori                                     begin_of_direntry(d));
27068d9401c2SLiam Merwick                             if (m == NULL) {
27078d9401c2SLiam Merwick                                 return -1;
27088d9401c2SLiam Merwick                             }
27098d9401c2SLiam Merwick                             l = strlen(m->path);
27108d9401c2SLiam Merwick                             new_path = g_malloc(l + diff + 1);
2711019d6b8fSAnthony Liguori 
2712019d6b8fSAnthony Liguori                             assert(!strncmp(m->path, mapping->path, l2));
2713019d6b8fSAnthony Liguori 
2714019d6b8fSAnthony Liguori                             pstrcpy(new_path, l + diff + 1, mapping->path);
2715019d6b8fSAnthony Liguori                             pstrcpy(new_path + l1, l + diff + 1 - l1,
2716019d6b8fSAnthony Liguori                                     m->path + l2);
2717019d6b8fSAnthony Liguori 
2718019d6b8fSAnthony Liguori                             schedule_rename(s, m->begin, new_path);
2719019d6b8fSAnthony Liguori                         }
2720fb2575f9SMarkus Armbruster                         j++;
2721fb2575f9SMarkus Armbruster                     } while (j % (0x10 * s->sectors_per_cluster) != 0);
2722019d6b8fSAnthony Liguori                     c = fat_get(s, c);
2723019d6b8fSAnthony Liguori                 }
2724019d6b8fSAnthony Liguori             }
2725019d6b8fSAnthony Liguori 
2726ce137829SStefan Weil             g_free(old_path);
2727019d6b8fSAnthony Liguori             array_remove(&(s->commits), i);
2728019d6b8fSAnthony Liguori             continue;
2729019d6b8fSAnthony Liguori         } else if (commit->action == ACTION_MKDIR) {
2730c227f099SAnthony Liguori             mapping_t* mapping;
2731019d6b8fSAnthony Liguori             int j, parent_path_len;
2732019d6b8fSAnthony Liguori 
2733c2632994SBin Meng             if (g_mkdir(commit->path, 0755)) {
2734019d6b8fSAnthony Liguori                 return -5;
2735c2632994SBin Meng             }
2736019d6b8fSAnthony Liguori 
2737019d6b8fSAnthony Liguori             mapping = insert_mapping(s, commit->param.mkdir.cluster,
2738019d6b8fSAnthony Liguori                     commit->param.mkdir.cluster + 1);
2739019d6b8fSAnthony Liguori             if (mapping == NULL)
2740019d6b8fSAnthony Liguori                 return -6;
2741019d6b8fSAnthony Liguori 
2742019d6b8fSAnthony Liguori             mapping->mode = MODE_DIRECTORY;
2743019d6b8fSAnthony Liguori             mapping->read_only = 0;
2744019d6b8fSAnthony Liguori             mapping->path = commit->path;
2745019d6b8fSAnthony Liguori             j = s->directory.next;
2746019d6b8fSAnthony Liguori             assert(j);
2747019d6b8fSAnthony Liguori             insert_direntries(s, s->directory.next,
2748019d6b8fSAnthony Liguori                     0x10 * s->sectors_per_cluster);
2749019d6b8fSAnthony Liguori             mapping->info.dir.first_dir_index = j;
2750019d6b8fSAnthony Liguori 
2751019d6b8fSAnthony Liguori             parent_path_len = strlen(commit->path)
2752019d6b8fSAnthony Liguori                 - strlen(get_basename(commit->path)) - 1;
2753019d6b8fSAnthony Liguori             for (j = 0; j < s->mapping.next; j++) {
2754c227f099SAnthony Liguori                 mapping_t* m = array_get(&(s->mapping), j);
2755019d6b8fSAnthony Liguori                 if (m->first_mapping_index < 0 && m != mapping &&
2756019d6b8fSAnthony Liguori                         !strncmp(m->path, mapping->path, parent_path_len) &&
2757019d6b8fSAnthony Liguori                         strlen(m->path) == parent_path_len)
2758019d6b8fSAnthony Liguori                     break;
2759019d6b8fSAnthony Liguori             }
2760019d6b8fSAnthony Liguori             assert(j < s->mapping.next);
2761019d6b8fSAnthony Liguori             mapping->info.dir.parent_mapping_index = j;
2762019d6b8fSAnthony Liguori 
2763019d6b8fSAnthony Liguori             array_remove(&(s->commits), i);
2764019d6b8fSAnthony Liguori             continue;
2765019d6b8fSAnthony Liguori         }
2766019d6b8fSAnthony Liguori 
2767019d6b8fSAnthony Liguori         i++;
2768019d6b8fSAnthony Liguori     }
2769019d6b8fSAnthony Liguori     return 0;
2770019d6b8fSAnthony Liguori }
2771019d6b8fSAnthony Liguori 
2772019d6b8fSAnthony Liguori /*
2773019d6b8fSAnthony Liguori  * TODO: make sure that the short name is not matching *another* file
2774019d6b8fSAnthony Liguori  */
handle_commits(BDRVVVFATState * s)2775eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK handle_commits(BDRVVVFATState* s)
2776019d6b8fSAnthony Liguori {
2777019d6b8fSAnthony Liguori     int i, fail = 0;
2778019d6b8fSAnthony Liguori 
2779019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2780019d6b8fSAnthony Liguori 
2781019d6b8fSAnthony Liguori     for (i = 0; !fail && i < s->commits.next; i++) {
2782c227f099SAnthony Liguori         commit_t* commit = array_get(&(s->commits), i);
2783019d6b8fSAnthony Liguori         switch(commit->action) {
2784019d6b8fSAnthony Liguori         case ACTION_RENAME: case ACTION_MKDIR:
278543dc2a64SBlue Swirl             abort();
2786019d6b8fSAnthony Liguori             fail = -2;
2787019d6b8fSAnthony Liguori             break;
2788019d6b8fSAnthony Liguori         case ACTION_WRITEOUT: {
2789c227f099SAnthony Liguori             direntry_t* entry = array_get(&(s->directory),
2790019d6b8fSAnthony Liguori                     commit->param.writeout.dir_index);
2791019d6b8fSAnthony Liguori             uint32_t begin = begin_of_direntry(entry);
2792c227f099SAnthony Liguori             mapping_t* mapping = find_mapping_for_cluster(s, begin);
2793019d6b8fSAnthony Liguori 
2794019d6b8fSAnthony Liguori             assert(mapping);
2795019d6b8fSAnthony Liguori             assert(mapping->begin == begin);
2796019d6b8fSAnthony Liguori             assert(commit->path == NULL);
2797019d6b8fSAnthony Liguori 
2798019d6b8fSAnthony Liguori             if (commit_one_file(s, commit->param.writeout.dir_index,
2799019d6b8fSAnthony Liguori                         commit->param.writeout.modified_offset))
2800019d6b8fSAnthony Liguori                 fail = -3;
2801019d6b8fSAnthony Liguori 
2802019d6b8fSAnthony Liguori             break;
2803019d6b8fSAnthony Liguori         }
2804019d6b8fSAnthony Liguori         case ACTION_NEW_FILE: {
2805019d6b8fSAnthony Liguori             int begin = commit->param.new_file.first_cluster;
2806c227f099SAnthony Liguori             mapping_t* mapping = find_mapping_for_cluster(s, begin);
2807c227f099SAnthony Liguori             direntry_t* entry;
2808fb2575f9SMarkus Armbruster             int j;
2809019d6b8fSAnthony Liguori 
2810019d6b8fSAnthony Liguori             /* find direntry */
2811fb2575f9SMarkus Armbruster             for (j = 0; j < s->directory.next; j++) {
2812fb2575f9SMarkus Armbruster                 entry = array_get(&(s->directory), j);
2813019d6b8fSAnthony Liguori                 if (is_file(entry) && begin_of_direntry(entry) == begin)
2814019d6b8fSAnthony Liguori                     break;
2815019d6b8fSAnthony Liguori             }
2816019d6b8fSAnthony Liguori 
2817fb2575f9SMarkus Armbruster             if (j >= s->directory.next) {
2818019d6b8fSAnthony Liguori                 fail = -6;
2819019d6b8fSAnthony Liguori                 continue;
2820019d6b8fSAnthony Liguori             }
2821019d6b8fSAnthony Liguori 
2822019d6b8fSAnthony Liguori             /* make sure there exists an initial mapping */
2823019d6b8fSAnthony Liguori             if (mapping && mapping->begin != begin) {
2824019d6b8fSAnthony Liguori                 mapping->end = begin;
2825019d6b8fSAnthony Liguori                 mapping = NULL;
2826019d6b8fSAnthony Liguori             }
2827019d6b8fSAnthony Liguori             if (mapping == NULL) {
2828019d6b8fSAnthony Liguori                 mapping = insert_mapping(s, begin, begin+1);
2829019d6b8fSAnthony Liguori             }
2830019d6b8fSAnthony Liguori             /* most members will be fixed in commit_mappings() */
2831019d6b8fSAnthony Liguori             assert(commit->path);
2832019d6b8fSAnthony Liguori             mapping->path = commit->path;
2833019d6b8fSAnthony Liguori             mapping->read_only = 0;
2834019d6b8fSAnthony Liguori             mapping->mode = MODE_NORMAL;
2835019d6b8fSAnthony Liguori             mapping->info.file.offset = 0;
2836019d6b8fSAnthony Liguori 
2837fb2575f9SMarkus Armbruster             if (commit_one_file(s, j, 0)) {
2838019d6b8fSAnthony Liguori                 fail = -7;
2839fb2575f9SMarkus Armbruster             }
2840019d6b8fSAnthony Liguori 
2841019d6b8fSAnthony Liguori             break;
2842019d6b8fSAnthony Liguori         }
2843019d6b8fSAnthony Liguori         default:
284443dc2a64SBlue Swirl             abort();
2845019d6b8fSAnthony Liguori         }
2846019d6b8fSAnthony Liguori     }
2847019d6b8fSAnthony Liguori     if (i > 0 && array_remove_slice(&(s->commits), 0, i))
2848019d6b8fSAnthony Liguori         return -1;
2849019d6b8fSAnthony Liguori     return fail;
2850019d6b8fSAnthony Liguori }
2851019d6b8fSAnthony Liguori 
handle_deletes(BDRVVVFATState * s)2852019d6b8fSAnthony Liguori static int handle_deletes(BDRVVVFATState* s)
2853019d6b8fSAnthony Liguori {
2854019d6b8fSAnthony Liguori     int i, deferred = 1, deleted = 1;
2855019d6b8fSAnthony Liguori 
2856019d6b8fSAnthony Liguori     /* delete files corresponding to mappings marked as deleted */
2857019d6b8fSAnthony Liguori     /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
2858019d6b8fSAnthony Liguori     while (deferred && deleted) {
2859019d6b8fSAnthony Liguori         deferred = 0;
2860019d6b8fSAnthony Liguori         deleted = 0;
2861019d6b8fSAnthony Liguori 
2862019d6b8fSAnthony Liguori         for (i = 1; i < s->mapping.next; i++) {
2863c227f099SAnthony Liguori             mapping_t* mapping = array_get(&(s->mapping), i);
2864019d6b8fSAnthony Liguori             if (mapping->mode & MODE_DELETED) {
2865c227f099SAnthony Liguori                 direntry_t* entry = array_get(&(s->directory),
2866019d6b8fSAnthony Liguori                         mapping->dir_index);
2867019d6b8fSAnthony Liguori 
2868019d6b8fSAnthony Liguori                 if (is_free(entry)) {
2869019d6b8fSAnthony Liguori                     /* remove file/directory */
2870019d6b8fSAnthony Liguori                     if (mapping->mode & MODE_DIRECTORY) {
2871019d6b8fSAnthony Liguori                         int j, next_dir_index = s->directory.next,
2872019d6b8fSAnthony Liguori                         first_dir_index = mapping->info.dir.first_dir_index;
2873019d6b8fSAnthony Liguori 
2874019d6b8fSAnthony Liguori                         if (rmdir(mapping->path) < 0) {
2875019d6b8fSAnthony Liguori                             if (errno == ENOTEMPTY) {
2876019d6b8fSAnthony Liguori                                 deferred++;
2877019d6b8fSAnthony Liguori                                 continue;
2878019d6b8fSAnthony Liguori                             } else
2879019d6b8fSAnthony Liguori                                 return -5;
2880019d6b8fSAnthony Liguori                         }
2881019d6b8fSAnthony Liguori 
2882019d6b8fSAnthony Liguori                         for (j = 1; j < s->mapping.next; j++) {
2883c227f099SAnthony Liguori                             mapping_t* m = array_get(&(s->mapping), j);
2884019d6b8fSAnthony Liguori                             if (m->mode & MODE_DIRECTORY &&
2885019d6b8fSAnthony Liguori                                     m->info.dir.first_dir_index >
2886019d6b8fSAnthony Liguori                                     first_dir_index &&
2887019d6b8fSAnthony Liguori                                     m->info.dir.first_dir_index <
2888019d6b8fSAnthony Liguori                                     next_dir_index)
2889019d6b8fSAnthony Liguori                                 next_dir_index =
2890019d6b8fSAnthony Liguori                                     m->info.dir.first_dir_index;
2891019d6b8fSAnthony Liguori                         }
2892019d6b8fSAnthony Liguori                         remove_direntries(s, first_dir_index,
2893019d6b8fSAnthony Liguori                                 next_dir_index - first_dir_index);
2894019d6b8fSAnthony Liguori 
2895019d6b8fSAnthony Liguori                         deleted++;
2896019d6b8fSAnthony Liguori                     }
2897019d6b8fSAnthony Liguori                 } else {
2898019d6b8fSAnthony Liguori                     if (unlink(mapping->path))
2899019d6b8fSAnthony Liguori                         return -4;
2900019d6b8fSAnthony Liguori                     deleted++;
2901019d6b8fSAnthony Liguori                 }
2902019d6b8fSAnthony Liguori                 DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
2903019d6b8fSAnthony Liguori                 remove_mapping(s, i);
2904019d6b8fSAnthony Liguori             }
2905019d6b8fSAnthony Liguori         }
2906019d6b8fSAnthony Liguori     }
2907019d6b8fSAnthony Liguori 
2908019d6b8fSAnthony Liguori     return 0;
2909019d6b8fSAnthony Liguori }
2910019d6b8fSAnthony Liguori 
2911019d6b8fSAnthony Liguori /*
2912019d6b8fSAnthony Liguori  * synchronize mapping with new state:
2913019d6b8fSAnthony Liguori  *
2914f4649069SEric Blake  * - copy FAT (with bdrv_pread)
2915019d6b8fSAnthony Liguori  * - mark all filenames corresponding to mappings as deleted
2916f4649069SEric Blake  * - recurse direntries from root (using bs->bdrv_pread)
2917019d6b8fSAnthony Liguori  * - delete files corresponding to mappings marked as deleted
2918019d6b8fSAnthony Liguori  */
do_commit(BDRVVVFATState * s)2919eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK do_commit(BDRVVVFATState* s)
2920019d6b8fSAnthony Liguori {
2921019d6b8fSAnthony Liguori     int ret = 0;
2922019d6b8fSAnthony Liguori 
2923019d6b8fSAnthony Liguori     /* the real meat are the commits. Nothing to do? Move along! */
2924019d6b8fSAnthony Liguori     if (s->commits.next == 0)
2925019d6b8fSAnthony Liguori         return 0;
2926019d6b8fSAnthony Liguori 
2927019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2928019d6b8fSAnthony Liguori 
2929019d6b8fSAnthony Liguori     ret = handle_renames_and_mkdirs(s);
2930019d6b8fSAnthony Liguori     if (ret) {
2931019d6b8fSAnthony Liguori         fprintf(stderr, "Error handling renames (%d)\n", ret);
293243dc2a64SBlue Swirl         abort();
2933019d6b8fSAnthony Liguori         return ret;
2934019d6b8fSAnthony Liguori     }
2935019d6b8fSAnthony Liguori 
2936f4649069SEric Blake     /* copy FAT (with bdrv_pread) */
2937019d6b8fSAnthony Liguori     memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
2938019d6b8fSAnthony Liguori 
2939f4649069SEric Blake     /* recurse direntries from root (using bs->bdrv_pread) */
2940019d6b8fSAnthony Liguori     ret = commit_direntries(s, 0, -1);
2941019d6b8fSAnthony Liguori     if (ret) {
2942019d6b8fSAnthony Liguori         fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
294343dc2a64SBlue Swirl         abort();
2944019d6b8fSAnthony Liguori         return ret;
2945019d6b8fSAnthony Liguori     }
2946019d6b8fSAnthony Liguori 
2947019d6b8fSAnthony Liguori     ret = handle_commits(s);
2948019d6b8fSAnthony Liguori     if (ret) {
2949019d6b8fSAnthony Liguori         fprintf(stderr, "Error handling commits (%d)\n", ret);
295043dc2a64SBlue Swirl         abort();
2951019d6b8fSAnthony Liguori         return ret;
2952019d6b8fSAnthony Liguori     }
2953019d6b8fSAnthony Liguori 
2954019d6b8fSAnthony Liguori     ret = handle_deletes(s);
2955019d6b8fSAnthony Liguori     if (ret) {
2956019d6b8fSAnthony Liguori         fprintf(stderr, "Error deleting\n");
295743dc2a64SBlue Swirl         abort();
2958019d6b8fSAnthony Liguori         return ret;
2959019d6b8fSAnthony Liguori     }
2960019d6b8fSAnthony Liguori 
2961f844ec01SMax Reitz     bdrv_make_empty(s->qcow, NULL);
2962019d6b8fSAnthony Liguori 
2963019d6b8fSAnthony Liguori     memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
2964019d6b8fSAnthony Liguori 
2965019d6b8fSAnthony Liguori DLOG(checkpoint());
2966019d6b8fSAnthony Liguori     return 0;
2967019d6b8fSAnthony Liguori }
2968019d6b8fSAnthony Liguori 
try_commit(BDRVVVFATState * s)2969eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK try_commit(BDRVVVFATState* s)
2970019d6b8fSAnthony Liguori {
2971019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2972019d6b8fSAnthony Liguori DLOG(checkpoint());
2973019d6b8fSAnthony Liguori     if(!is_consistent(s))
2974019d6b8fSAnthony Liguori         return -1;
2975019d6b8fSAnthony Liguori     return do_commit(s);
2976019d6b8fSAnthony Liguori }
2977019d6b8fSAnthony Liguori 
2978eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
vvfat_write(BlockDriverState * bs,int64_t sector_num,const uint8_t * buf,int nb_sectors)2979eab76d58SPaolo Bonzini vvfat_write(BlockDriverState *bs, int64_t sector_num,
2980019d6b8fSAnthony Liguori             const uint8_t *buf, int nb_sectors)
2981019d6b8fSAnthony Liguori {
2982019d6b8fSAnthony Liguori     BDRVVVFATState *s = bs->opaque;
2983019d6b8fSAnthony Liguori     int i, ret;
2984b9b8860dSKevin Wolf     int first_cluster, last_cluster;
2985019d6b8fSAnthony Liguori 
2986019d6b8fSAnthony Liguori DLOG(checkpoint());
2987019d6b8fSAnthony Liguori 
2988ac48e389SKevin Wolf     /* Check if we're operating in read-only mode */
2989ac48e389SKevin Wolf     if (s->qcow == NULL) {
2990ac48e389SKevin Wolf         return -EACCES;
2991ac48e389SKevin Wolf     }
2992ac48e389SKevin Wolf 
2993019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2994019d6b8fSAnthony Liguori 
2995d0f95b6cSHervé Poussineau     if (sector_num == s->offset_to_bootsector && nb_sectors == 1) {
2996d0f95b6cSHervé Poussineau         /*
2997d0f95b6cSHervé Poussineau          * Write on bootsector. Allow only changing the reserved1 field,
2998d0f95b6cSHervé Poussineau          * used to mark volume dirtiness
2999d0f95b6cSHervé Poussineau          */
3000d0f95b6cSHervé Poussineau         unsigned char *bootsector = s->first_sectors
3001d0f95b6cSHervé Poussineau                                     + s->offset_to_bootsector * 0x200;
3002d0f95b6cSHervé Poussineau         /*
3003d0f95b6cSHervé Poussineau          * LATER TODO: if FAT32, this is wrong (see init_directories(),
3004d0f95b6cSHervé Poussineau          * which always creates a FAT16 bootsector)
3005d0f95b6cSHervé Poussineau          */
3006d0f95b6cSHervé Poussineau         const int reserved1_offset = offsetof(bootsector_t, u.fat16.reserved1);
3007d0f95b6cSHervé Poussineau 
3008d0f95b6cSHervé Poussineau         for (i = 0; i < 0x200; i++) {
3009d0f95b6cSHervé Poussineau             if (i != reserved1_offset && bootsector[i] != buf[i]) {
3010d0f95b6cSHervé Poussineau                 fprintf(stderr, "Tried to write to protected bootsector\n");
3011d0f95b6cSHervé Poussineau                 return -1;
3012d0f95b6cSHervé Poussineau             }
3013d0f95b6cSHervé Poussineau         }
3014d0f95b6cSHervé Poussineau 
3015d0f95b6cSHervé Poussineau         /* Update bootsector with the only updatable byte, and return success */
3016d0f95b6cSHervé Poussineau         bootsector[reserved1_offset] = buf[reserved1_offset];
3017d0f95b6cSHervé Poussineau         return 0;
3018d0f95b6cSHervé Poussineau     }
3019d0f95b6cSHervé Poussineau 
3020019d6b8fSAnthony Liguori     /*
3021019d6b8fSAnthony Liguori      * Some sanity checks:
3022019d6b8fSAnthony Liguori      * - do not allow writing to the boot sector
3023019d6b8fSAnthony Liguori      */
30244dc705dcSHervé Poussineau     if (sector_num < s->offset_to_fat)
3025019d6b8fSAnthony Liguori         return -1;
3026019d6b8fSAnthony Liguori 
3027b9b8860dSKevin Wolf     /*
3028b9b8860dSKevin Wolf      * Values will be negative for writes to the FAT, which is located before
3029b9b8860dSKevin Wolf      * the root directory.
3030b9b8860dSKevin Wolf      */
3031b9b8860dSKevin Wolf     first_cluster = sector2cluster(s, sector_num);
3032b9b8860dSKevin Wolf     last_cluster = sector2cluster(s, sector_num + nb_sectors - 1);
3033b9b8860dSKevin Wolf 
3034b9b8860dSKevin Wolf     for (i = first_cluster; i <= last_cluster;) {
3035b9b8860dSKevin Wolf         mapping_t *mapping = NULL;
3036b9b8860dSKevin Wolf 
3037b9b8860dSKevin Wolf         if (i >= 0) {
3038b9b8860dSKevin Wolf             mapping = find_mapping_for_cluster(s, i);
3039b9b8860dSKevin Wolf         }
3040b9b8860dSKevin Wolf 
3041019d6b8fSAnthony Liguori         if (mapping) {
3042019d6b8fSAnthony Liguori             if (mapping->read_only) {
3043019d6b8fSAnthony Liguori                 fprintf(stderr, "Tried to write to write-protected file %s\n",
3044019d6b8fSAnthony Liguori                         mapping->path);
3045019d6b8fSAnthony Liguori                 return -1;
3046019d6b8fSAnthony Liguori             }
3047019d6b8fSAnthony Liguori 
3048019d6b8fSAnthony Liguori             if (mapping->mode & MODE_DIRECTORY) {
3049019d6b8fSAnthony Liguori                 int begin = cluster2sector(s, i);
3050019d6b8fSAnthony Liguori                 int end = begin + s->sectors_per_cluster, k;
3051019d6b8fSAnthony Liguori                 int dir_index;
3052c227f099SAnthony Liguori                 const direntry_t* direntries;
3053019d6b8fSAnthony Liguori                 long_file_name lfn;
3054019d6b8fSAnthony Liguori 
3055019d6b8fSAnthony Liguori                 lfn_init(&lfn);
3056019d6b8fSAnthony Liguori 
3057019d6b8fSAnthony Liguori                 if (begin < sector_num)
3058019d6b8fSAnthony Liguori                     begin = sector_num;
3059019d6b8fSAnthony Liguori                 if (end > sector_num + nb_sectors)
3060019d6b8fSAnthony Liguori                     end = sector_num + nb_sectors;
3061019d6b8fSAnthony Liguori                 dir_index  = mapping->dir_index +
3062019d6b8fSAnthony Liguori                     0x10 * (begin - mapping->begin * s->sectors_per_cluster);
3063c227f099SAnthony Liguori                 direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
3064019d6b8fSAnthony Liguori 
3065019d6b8fSAnthony Liguori                 for (k = 0; k < (end - begin) * 0x10; k++) {
3066019d6b8fSAnthony Liguori                     /* no access to the direntry of a read-only file */
3067e03da26bSHervé Poussineau                     if (is_short_name(direntries + k) &&
3068019d6b8fSAnthony Liguori                             (direntries[k].attributes & 1)) {
3069019d6b8fSAnthony Liguori                         if (memcmp(direntries + k,
3070019d6b8fSAnthony Liguori                                     array_get(&(s->directory), dir_index + k),
3071c227f099SAnthony Liguori                                     sizeof(direntry_t))) {
30722ab4b135SAlistair Francis                             warn_report("tried to write to write-protected "
30732ab4b135SAlistair Francis                                         "file");
3074019d6b8fSAnthony Liguori                             return -1;
3075019d6b8fSAnthony Liguori                         }
3076019d6b8fSAnthony Liguori                     }
3077019d6b8fSAnthony Liguori                 }
3078019d6b8fSAnthony Liguori             }
3079019d6b8fSAnthony Liguori             i = mapping->end;
3080b9b8860dSKevin Wolf         } else {
3081019d6b8fSAnthony Liguori             i++;
3082019d6b8fSAnthony Liguori         }
3083b9b8860dSKevin Wolf     }
3084019d6b8fSAnthony Liguori 
3085019d6b8fSAnthony Liguori     /*
3086019d6b8fSAnthony Liguori      * Use qcow backend. Commit later.
3087019d6b8fSAnthony Liguori      */
3088019d6b8fSAnthony Liguori DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
3089eab76d58SPaolo Bonzini     ret = bdrv_co_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE,
309032cc71deSAlberto Faria                          nb_sectors * BDRV_SECTOR_SIZE, buf, 0);
3091019d6b8fSAnthony Liguori     if (ret < 0) {
3092019d6b8fSAnthony Liguori         fprintf(stderr, "Error writing to qcow backend\n");
3093019d6b8fSAnthony Liguori         return ret;
3094019d6b8fSAnthony Liguori     }
3095019d6b8fSAnthony Liguori 
3096b9b8860dSKevin Wolf     for (i = first_cluster; i <= last_cluster; i++) {
3097b9b8860dSKevin Wolf         if (i >= 0) {
3098019d6b8fSAnthony Liguori             s->used_clusters[i] |= USED_ALLOCATED;
3099b9b8860dSKevin Wolf         }
3100b9b8860dSKevin Wolf     }
3101019d6b8fSAnthony Liguori 
3102019d6b8fSAnthony Liguori DLOG(checkpoint());
3103019d6b8fSAnthony Liguori     /* TODO: add timeout */
3104019d6b8fSAnthony Liguori     try_commit(s);
3105019d6b8fSAnthony Liguori 
3106019d6b8fSAnthony Liguori DLOG(checkpoint());
3107019d6b8fSAnthony Liguori     return 0;
3108019d6b8fSAnthony Liguori }
3109019d6b8fSAnthony Liguori 
3110eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
vvfat_co_pwritev(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)3111e75abedaSVladimir Sementsov-Ogievskiy vvfat_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
3112e75abedaSVladimir Sementsov-Ogievskiy                  QEMUIOVector *qiov, BdrvRequestFlags flags)
3113e183ef75SPaolo Bonzini {
3114e183ef75SPaolo Bonzini     int ret;
3115e183ef75SPaolo Bonzini     BDRVVVFATState *s = bs->opaque;
31164575eb49SKevin Wolf     uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
31174575eb49SKevin Wolf     int nb_sectors = bytes >> BDRV_SECTOR_BITS;
31184575eb49SKevin Wolf     void *buf;
31194575eb49SKevin Wolf 
31201bbbf32dSNir Soffer     assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
31211bbbf32dSNir Soffer     assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
31224575eb49SKevin Wolf 
31234575eb49SKevin Wolf     buf = g_try_malloc(bytes);
31244575eb49SKevin Wolf     if (bytes && buf == NULL) {
31254575eb49SKevin Wolf         return -ENOMEM;
31264575eb49SKevin Wolf     }
31274575eb49SKevin Wolf     qemu_iovec_to_buf(qiov, 0, buf, bytes);
31284575eb49SKevin Wolf 
3129e183ef75SPaolo Bonzini     qemu_co_mutex_lock(&s->lock);
3130e183ef75SPaolo Bonzini     ret = vvfat_write(bs, sector_num, buf, nb_sectors);
3131e183ef75SPaolo Bonzini     qemu_co_mutex_unlock(&s->lock);
31324575eb49SKevin Wolf 
31334575eb49SKevin Wolf     g_free(buf);
31344575eb49SKevin Wolf 
3135e183ef75SPaolo Bonzini     return ret;
3136e183ef75SPaolo Bonzini }
3137e183ef75SPaolo Bonzini 
vvfat_co_block_status(BlockDriverState * bs,bool want_zero,int64_t offset,int64_t bytes,int64_t * n,int64_t * map,BlockDriverState ** file)3138fba3998dSEric Blake static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs,
3139fba3998dSEric Blake                                               bool want_zero, int64_t offset,
3140fba3998dSEric Blake                                               int64_t bytes, int64_t *n,
3141fba3998dSEric Blake                                               int64_t *map,
3142fba3998dSEric Blake                                               BlockDriverState **file)
3143019d6b8fSAnthony Liguori {
3144fba3998dSEric Blake     *n = bytes;
31454bc74be9SPaolo Bonzini     return BDRV_BLOCK_DATA;
3146019d6b8fSAnthony Liguori }
3147019d6b8fSAnthony Liguori 
vvfat_qcow_options(BdrvChildRole role,bool parent_is_format,int * child_flags,QDict * child_options,int parent_flags,QDict * parent_options)31483cdc69d3SMax Reitz static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format,
3149272c02eaSMax Reitz                                int *child_flags, QDict *child_options,
3150eecc7747SKevin Wolf                                int parent_flags, QDict *parent_options)
3151019d6b8fSAnthony Liguori {
3152f87a0e29SAlberto Garcia     qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off");
3153e35bdc12SKevin Wolf     qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off");
31544f8e3a1fSFam Zheng     qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
3155eecc7747SKevin Wolf }
3156eecc7747SKevin Wolf 
31578081f064SVladimir Sementsov-Ogievskiy static BdrvChildClass child_vvfat_qcow;
3158eecc7747SKevin Wolf 
enable_write_target(BlockDriverState * bs,Error ** errp)3159eecc7747SKevin Wolf static int enable_write_target(BlockDriverState *bs, Error **errp)
3160eecc7747SKevin Wolf {
3161eecc7747SKevin Wolf     BDRVVVFATState *s = bs->opaque;
3162facdbb02SChunyan Liu     BlockDriver *bdrv_qcow = NULL;
3163facdbb02SChunyan Liu     QemuOpts *opts = NULL;
3164a655211aSKevin Wolf     int ret;
3165019d6b8fSAnthony Liguori     int size = sector2cluster(s, s->sector_count);
3166e6641719SMax Reitz     QDict *options;
3167e6641719SMax Reitz 
316822c36b75SDaniella Lee     s->used_clusters = g_malloc0(size);
3169019d6b8fSAnthony Liguori 
3170c227f099SAnthony Liguori     array_init(&(s->commits), sizeof(commit_t));
3171019d6b8fSAnthony Liguori 
317269fbfff9SBin Meng     s->qcow_filename = create_tmp_file(errp);
317369fbfff9SBin Meng     if (!s->qcow_filename) {
317469fbfff9SBin Meng         ret = -ENOENT;
317578f27bd0SFam Zheng         goto err;
3176eba25057SJim Meyering     }
317791a073a9SKevin Wolf 
317891a073a9SKevin Wolf     bdrv_qcow = bdrv_find_format("qcow");
31791bcb15cfSMax Reitz     if (!bdrv_qcow) {
31801bcb15cfSMax Reitz         error_setg(errp, "Failed to locate qcow driver");
31811bcb15cfSMax Reitz         ret = -ENOENT;
31821bcb15cfSMax Reitz         goto err;
31831bcb15cfSMax Reitz     }
31841bcb15cfSMax Reitz 
3185c282e1fdSChunyan Liu     opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort);
31862db9b9e9SKevin Wolf     qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
31872db9b9e9SKevin Wolf                         bs->total_sectors * BDRV_SECTOR_SIZE, &error_abort);
3188f43e47dbSMarkus Armbruster     qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort);
318991a073a9SKevin Wolf 
3190c282e1fdSChunyan Liu     ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
3191facdbb02SChunyan Liu     qemu_opts_del(opts);
319278f27bd0SFam Zheng     if (ret < 0) {
319378f27bd0SFam Zheng         goto err;
319478f27bd0SFam Zheng     }
3195a655211aSKevin Wolf 
3196e6641719SMax Reitz     options = qdict_new();
319746f5ac20SEric Blake     qdict_put_str(options, "write-target.driver", "qcow");
3198eecc7747SKevin Wolf     s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs,
31991f38f04eSMax Reitz                               &child_vvfat_qcow,
32001f38f04eSMax Reitz                               BDRV_CHILD_DATA | BDRV_CHILD_METADATA,
32011f38f04eSMax Reitz                               false, errp);
3202cb3e7f08SMarc-André Lureau     qobject_unref(options);
32035b363937SMax Reitz     if (!s->qcow) {
32045b363937SMax Reitz         ret = -EINVAL;
320578f27bd0SFam Zheng         goto err;
3206a655211aSKevin Wolf     }
3207a655211aSKevin Wolf 
3208019d6b8fSAnthony Liguori #ifndef _WIN32
3209019d6b8fSAnthony Liguori     unlink(s->qcow_filename);
3210019d6b8fSAnthony Liguori #endif
3211019d6b8fSAnthony Liguori 
3212019d6b8fSAnthony Liguori     return 0;
321378f27bd0SFam Zheng 
321478f27bd0SFam Zheng err:
321578f27bd0SFam Zheng     return ret;
3216019d6b8fSAnthony Liguori }
3217019d6b8fSAnthony Liguori 
vvfat_child_perm(BlockDriverState * bs,BdrvChild * c,BdrvChildRole role,BlockReopenQueue * reopen_queue,uint64_t perm,uint64_t shared,uint64_t * nperm,uint64_t * nshared)321891ef3825SKevin Wolf static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
3219bf8e925eSMax Reitz                              BdrvChildRole role,
3220e0995dc3SKevin Wolf                              BlockReopenQueue *reopen_queue,
322191ef3825SKevin Wolf                              uint64_t perm, uint64_t shared,
322291ef3825SKevin Wolf                              uint64_t *nperm, uint64_t *nshared)
322391ef3825SKevin Wolf {
32246af72274SVladimir Sementsov-Ogievskiy     assert(role & BDRV_CHILD_DATA);
322591ef3825SKevin Wolf     /* This is a private node, nobody should try to attach to it */
322691ef3825SKevin Wolf     *nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
322791ef3825SKevin Wolf     *nshared = BLK_PERM_WRITE_UNCHANGED;
322891ef3825SKevin Wolf }
322991ef3825SKevin Wolf 
vvfat_close(BlockDriverState * bs)3230019d6b8fSAnthony Liguori static void vvfat_close(BlockDriverState *bs)
3231019d6b8fSAnthony Liguori {
3232019d6b8fSAnthony Liguori     BDRVVVFATState *s = bs->opaque;
3233019d6b8fSAnthony Liguori 
3234019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
3235019d6b8fSAnthony Liguori     array_free(&(s->fat));
3236019d6b8fSAnthony Liguori     array_free(&(s->directory));
3237019d6b8fSAnthony Liguori     array_free(&(s->mapping));
3238ce137829SStefan Weil     g_free(s->cluster_buffer);
32393397f0cbSKevin Wolf 
32403397f0cbSKevin Wolf     if (s->qcow) {
3241c8a7fc51SSteve Sistare         migrate_del_blocker(&s->migration_blocker);
32423397f0cbSKevin Wolf     }
3243019d6b8fSAnthony Liguori }
3244019d6b8fSAnthony Liguori 
32452654267cSMax Reitz static const char *const vvfat_strong_runtime_opts[] = {
32462654267cSMax Reitz     "dir",
32472654267cSMax Reitz     "fat-type",
32482654267cSMax Reitz     "floppy",
32492654267cSMax Reitz     "label",
32502654267cSMax Reitz     "rw",
32512654267cSMax Reitz 
32522654267cSMax Reitz     NULL
32532654267cSMax Reitz };
32542654267cSMax Reitz 
3255019d6b8fSAnthony Liguori static BlockDriver bdrv_vvfat = {
3256019d6b8fSAnthony Liguori     .format_name            = "vvfat",
32577ad9be64SKevin Wolf     .protocol_name          = "fat",
3258019d6b8fSAnthony Liguori     .instance_size          = sizeof(BDRVVVFATState),
32597ad9be64SKevin Wolf 
32607ad9be64SKevin Wolf     .bdrv_parse_filename    = vvfat_parse_filename,
326166f82ceeSKevin Wolf     .bdrv_file_open         = vvfat_open,
3262a6506481SEric Blake     .bdrv_refresh_limits    = vvfat_refresh_limits,
32637ad9be64SKevin Wolf     .bdrv_close             = vvfat_close,
326491ef3825SKevin Wolf     .bdrv_child_perm        = vvfat_child_perm,
32657ad9be64SKevin Wolf 
32664575eb49SKevin Wolf     .bdrv_co_preadv         = vvfat_co_preadv,
32674575eb49SKevin Wolf     .bdrv_co_pwritev        = vvfat_co_pwritev,
3268fba3998dSEric Blake     .bdrv_co_block_status   = vvfat_co_block_status,
32692654267cSMax Reitz 
32702654267cSMax Reitz     .strong_runtime_opts    = vvfat_strong_runtime_opts,
3271019d6b8fSAnthony Liguori };
3272019d6b8fSAnthony Liguori 
bdrv_vvfat_init(void)3273019d6b8fSAnthony Liguori static void bdrv_vvfat_init(void)
3274019d6b8fSAnthony Liguori {
32758081f064SVladimir Sementsov-Ogievskiy     child_vvfat_qcow = child_of_bds;
32768081f064SVladimir Sementsov-Ogievskiy     child_vvfat_qcow.inherit_options = vvfat_qcow_options;
3277019d6b8fSAnthony Liguori     bdrv_register(&bdrv_vvfat);
3278019d6b8fSAnthony Liguori }
3279019d6b8fSAnthony Liguori 
3280019d6b8fSAnthony Liguori block_init(bdrv_vvfat_init);
3281019d6b8fSAnthony Liguori 
3282019d6b8fSAnthony Liguori #ifdef DEBUG
checkpoint(void)32837a6ab45eSThomas Huth static void checkpoint(void)
32847a6ab45eSThomas Huth {
3285c227f099SAnthony Liguori     assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
3286019d6b8fSAnthony Liguori     check1(vvv);
3287019d6b8fSAnthony Liguori     check2(vvv);
3288019d6b8fSAnthony Liguori     assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
3289019d6b8fSAnthony Liguori }
3290019d6b8fSAnthony Liguori #endif
3291