xref: /openbmc/qemu/block/vvfat.c (revision 6d00c6f982562222adbd0613966285792125abe5)
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     }
1373*5eed3db3SAmjad Alsharafi 
1374*5eed3db3SAmjad Alsharafi     s->current_mapping = mapping;
1375019d6b8fSAnthony Liguori     return 0;
1376019d6b8fSAnthony Liguori }
1377019d6b8fSAnthony Liguori 
read_cluster(BDRVVVFATState * s,int cluster_num)1378019d6b8fSAnthony Liguori static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
1379019d6b8fSAnthony Liguori {
1380019d6b8fSAnthony Liguori     if(s->current_cluster != cluster_num) {
1381019d6b8fSAnthony Liguori         int result=0;
1382019d6b8fSAnthony Liguori         off_t offset;
1383019d6b8fSAnthony Liguori         assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
1384019d6b8fSAnthony Liguori         if(!s->current_mapping
1385019d6b8fSAnthony Liguori                 || s->current_mapping->begin>cluster_num
1386019d6b8fSAnthony Liguori                 || s->current_mapping->end<=cluster_num) {
1387019d6b8fSAnthony Liguori             /* binary search of mappings for file */
1388c227f099SAnthony Liguori             mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
1389019d6b8fSAnthony Liguori 
1390019d6b8fSAnthony Liguori             assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
1391019d6b8fSAnthony Liguori 
1392019d6b8fSAnthony Liguori             if (mapping && mapping->mode & MODE_DIRECTORY) {
1393019d6b8fSAnthony Liguori                 vvfat_close_current_file(s);
1394019d6b8fSAnthony Liguori                 s->current_mapping = mapping;
1395019d6b8fSAnthony Liguori read_cluster_directory:
1396019d6b8fSAnthony Liguori                 offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
1397019d6b8fSAnthony Liguori                 s->cluster = (unsigned char*)s->directory.pointer+offset
1398019d6b8fSAnthony Liguori                         + 0x20*s->current_mapping->info.dir.first_dir_index;
1399019d6b8fSAnthony Liguori                 assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
1400019d6b8fSAnthony Liguori                 assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
1401019d6b8fSAnthony Liguori                 s->current_cluster = cluster_num;
1402019d6b8fSAnthony Liguori                 return 0;
1403019d6b8fSAnthony Liguori             }
1404019d6b8fSAnthony Liguori 
1405019d6b8fSAnthony Liguori             if(open_file(s,mapping))
1406019d6b8fSAnthony Liguori                 return -2;
1407019d6b8fSAnthony Liguori         } else if (s->current_mapping->mode & MODE_DIRECTORY)
1408019d6b8fSAnthony Liguori             goto read_cluster_directory;
1409019d6b8fSAnthony Liguori 
1410019d6b8fSAnthony Liguori         assert(s->current_fd);
1411019d6b8fSAnthony Liguori 
141221b25a0eSAmjad Alsharafi         offset = s->cluster_size *
141321b25a0eSAmjad Alsharafi             ((cluster_num - s->current_mapping->begin)
141421b25a0eSAmjad Alsharafi             + s->current_mapping->info.file.offset);
1415019d6b8fSAnthony Liguori         if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
1416019d6b8fSAnthony Liguori             return -3;
1417019d6b8fSAnthony Liguori         s->cluster=s->cluster_buffer;
1418019d6b8fSAnthony Liguori         result=read(s->current_fd,s->cluster,s->cluster_size);
1419019d6b8fSAnthony Liguori         if(result<0) {
1420019d6b8fSAnthony Liguori             s->current_cluster = -1;
1421019d6b8fSAnthony Liguori             return -1;
1422019d6b8fSAnthony Liguori         }
1423019d6b8fSAnthony Liguori         s->current_cluster = cluster_num;
1424019d6b8fSAnthony Liguori     }
1425019d6b8fSAnthony Liguori     return 0;
1426019d6b8fSAnthony Liguori }
1427019d6b8fSAnthony Liguori 
1428019d6b8fSAnthony Liguori #ifdef DEBUG
print_direntry(const direntry_t * direntry)1429c227f099SAnthony Liguori static void print_direntry(const direntry_t* direntry)
1430019d6b8fSAnthony Liguori {
1431019d6b8fSAnthony Liguori     int j = 0;
1432019d6b8fSAnthony Liguori     char buffer[1024];
1433019d6b8fSAnthony Liguori 
14343e89cb04SKevin Wolf     fprintf(stderr, "direntry %p: ", direntry);
1435019d6b8fSAnthony Liguori     if(!direntry)
1436019d6b8fSAnthony Liguori         return;
1437019d6b8fSAnthony Liguori     if(is_long_name(direntry)) {
1438019d6b8fSAnthony Liguori         unsigned char* c=(unsigned char*)direntry;
1439019d6b8fSAnthony Liguori         int i;
1440019d6b8fSAnthony Liguori         for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
1441019d6b8fSAnthony Liguori #define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;}
1442019d6b8fSAnthony Liguori             ADD_CHAR(c[i]);
1443019d6b8fSAnthony Liguori         for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
1444019d6b8fSAnthony Liguori             ADD_CHAR(c[i]);
1445019d6b8fSAnthony Liguori         for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
1446019d6b8fSAnthony Liguori             ADD_CHAR(c[i]);
1447019d6b8fSAnthony Liguori         buffer[j] = 0;
1448019d6b8fSAnthony Liguori         fprintf(stderr, "%s\n", buffer);
1449019d6b8fSAnthony Liguori     } else {
1450019d6b8fSAnthony Liguori         int i;
1451019d6b8fSAnthony Liguori         for(i=0;i<11;i++)
1452019d6b8fSAnthony Liguori             ADD_CHAR(direntry->name[i]);
1453019d6b8fSAnthony Liguori         buffer[j] = 0;
1454c9eb2f3eSAlexChen         fprintf(stderr, "%s attributes=0x%02x begin=%u size=%u\n",
1455019d6b8fSAnthony Liguori                 buffer,
1456019d6b8fSAnthony Liguori                 direntry->attributes,
1457019d6b8fSAnthony Liguori                 begin_of_direntry(direntry),le32_to_cpu(direntry->size));
1458019d6b8fSAnthony Liguori     }
1459019d6b8fSAnthony Liguori }
1460019d6b8fSAnthony Liguori 
print_mapping(const mapping_t * mapping)1461c227f099SAnthony Liguori static void print_mapping(const mapping_t* mapping)
1462019d6b8fSAnthony Liguori {
1463c9eb2f3eSAlexChen     fprintf(stderr, "mapping (%p): begin, end = %u, %u, dir_index = %u, "
14643e89cb04SKevin Wolf         "first_mapping_index = %d, name = %s, mode = 0x%x, " ,
14653e89cb04SKevin Wolf         mapping, mapping->begin, mapping->end, mapping->dir_index,
14663e89cb04SKevin Wolf         mapping->first_mapping_index, mapping->path, mapping->mode);
14673e89cb04SKevin Wolf 
1468019d6b8fSAnthony Liguori     if (mapping->mode & MODE_DIRECTORY)
1469019d6b8fSAnthony Liguori         fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
1470019d6b8fSAnthony Liguori     else
1471c9eb2f3eSAlexChen         fprintf(stderr, "offset = %u\n", mapping->info.file.offset);
1472019d6b8fSAnthony Liguori }
1473019d6b8fSAnthony Liguori #endif
1474019d6b8fSAnthony Liguori 
1475eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
vvfat_read(BlockDriverState * bs,int64_t sector_num,uint8_t * buf,int nb_sectors)1476eab76d58SPaolo Bonzini vvfat_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors)
1477019d6b8fSAnthony Liguori {
1478019d6b8fSAnthony Liguori     BDRVVVFATState *s = bs->opaque;
1479019d6b8fSAnthony Liguori     int i;
1480019d6b8fSAnthony Liguori 
1481019d6b8fSAnthony Liguori     for(i=0;i<nb_sectors;i++,sector_num++) {
1482e654bfe4SPaolo Bonzini         if (sector_num >= bs->total_sectors)
1483019d6b8fSAnthony Liguori            return -1;
1484019d6b8fSAnthony Liguori         if (s->qcow) {
1485d6a644bbSEric Blake             int64_t n;
14866f712ee0SEric Blake             int ret;
1487cc323997SPaolo Bonzini             ret = bdrv_co_is_allocated(s->qcow->bs, sector_num * BDRV_SECTOR_SIZE,
1488d6a644bbSEric Blake                                        (nb_sectors - i) * BDRV_SECTOR_SIZE, &n);
14896f712ee0SEric Blake             if (ret < 0) {
14906f712ee0SEric Blake                 return ret;
14916f712ee0SEric Blake             }
14926f712ee0SEric Blake             if (ret) {
1493d6a644bbSEric Blake                 DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64
1494d6a644bbSEric Blake                              " allocated\n", sector_num,
1495d6a644bbSEric Blake                              n >> BDRV_SECTOR_BITS));
1496eab76d58SPaolo Bonzini                 if (bdrv_co_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE, n,
149732cc71deSAlberto Faria                                   buf + i * 0x200, 0) < 0) {
1498019d6b8fSAnthony Liguori                     return -1;
14997704df98SKevin Wolf                 }
1500d6a644bbSEric Blake                 i += (n >> BDRV_SECTOR_BITS) - 1;
1501d6a644bbSEric Blake                 sector_num += (n >> BDRV_SECTOR_BITS) - 1;
1502019d6b8fSAnthony Liguori                 continue;
1503019d6b8fSAnthony Liguori             }
1504d6a644bbSEric Blake             DLOG(fprintf(stderr, "sector %" PRId64 " not allocated\n",
1505d6a644bbSEric Blake                          sector_num));
1506019d6b8fSAnthony Liguori         }
15074dc705dcSHervé Poussineau         if (sector_num < s->offset_to_root_dir) {
15084dc705dcSHervé Poussineau             if (sector_num < s->offset_to_fat) {
15094dc705dcSHervé Poussineau                 memcpy(buf + i * 0x200,
15104dc705dcSHervé Poussineau                        &(s->first_sectors[sector_num * 0x200]),
15114dc705dcSHervé Poussineau                        0x200);
15124dc705dcSHervé Poussineau             } else if (sector_num < s->offset_to_fat + s->sectors_per_fat) {
15134dc705dcSHervé Poussineau                 memcpy(buf + i * 0x200,
15144dc705dcSHervé Poussineau                        &(s->fat.pointer[(sector_num
15154dc705dcSHervé Poussineau                                        - s->offset_to_fat) * 0x200]),
15164dc705dcSHervé Poussineau                        0x200);
15174dc705dcSHervé Poussineau             } else if (sector_num < s->offset_to_root_dir) {
15184dc705dcSHervé Poussineau                 memcpy(buf + i * 0x200,
15194dc705dcSHervé Poussineau                        &(s->fat.pointer[(sector_num - s->offset_to_fat
15204dc705dcSHervé Poussineau                                        - s->sectors_per_fat) * 0x200]),
15214dc705dcSHervé Poussineau                        0x200);
15224dc705dcSHervé Poussineau             }
1523019d6b8fSAnthony Liguori         } else {
15244dc705dcSHervé Poussineau             uint32_t sector = sector_num - s->offset_to_root_dir,
1525019d6b8fSAnthony Liguori             sector_offset_in_cluster=(sector%s->sectors_per_cluster),
1526019d6b8fSAnthony Liguori             cluster_num=sector/s->sectors_per_cluster;
1527e654bfe4SPaolo Bonzini             if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) {
1528019d6b8fSAnthony Liguori                 /* LATER TODO: strict: return -1; */
1529019d6b8fSAnthony Liguori                 memset(buf+i*0x200,0,0x200);
1530019d6b8fSAnthony Liguori                 continue;
1531019d6b8fSAnthony Liguori             }
1532019d6b8fSAnthony Liguori             memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
1533019d6b8fSAnthony Liguori         }
1534019d6b8fSAnthony Liguori     }
1535019d6b8fSAnthony Liguori     return 0;
1536019d6b8fSAnthony Liguori }
1537019d6b8fSAnthony Liguori 
1538eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
vvfat_co_preadv(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)1539f7ef38ddSVladimir Sementsov-Ogievskiy vvfat_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
1540f7ef38ddSVladimir Sementsov-Ogievskiy                 QEMUIOVector *qiov, BdrvRequestFlags flags)
15412914caa0SPaolo Bonzini {
15422914caa0SPaolo Bonzini     int ret;
15432914caa0SPaolo Bonzini     BDRVVVFATState *s = bs->opaque;
15444575eb49SKevin Wolf     uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
15454575eb49SKevin Wolf     int nb_sectors = bytes >> BDRV_SECTOR_BITS;
15464575eb49SKevin Wolf     void *buf;
15474575eb49SKevin Wolf 
15481bbbf32dSNir Soffer     assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
15491bbbf32dSNir Soffer     assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
15504575eb49SKevin Wolf 
15514575eb49SKevin Wolf     buf = g_try_malloc(bytes);
15524575eb49SKevin Wolf     if (bytes && buf == NULL) {
15534575eb49SKevin Wolf         return -ENOMEM;
15544575eb49SKevin Wolf     }
15554575eb49SKevin Wolf 
15562914caa0SPaolo Bonzini     qemu_co_mutex_lock(&s->lock);
15572914caa0SPaolo Bonzini     ret = vvfat_read(bs, sector_num, buf, nb_sectors);
15582914caa0SPaolo Bonzini     qemu_co_mutex_unlock(&s->lock);
15594575eb49SKevin Wolf 
15604575eb49SKevin Wolf     qemu_iovec_from_buf(qiov, 0, buf, bytes);
15614575eb49SKevin Wolf     g_free(buf);
15624575eb49SKevin Wolf 
15632914caa0SPaolo Bonzini     return ret;
15642914caa0SPaolo Bonzini }
15652914caa0SPaolo Bonzini 
1566019d6b8fSAnthony Liguori /* LATER TODO: statify all functions */
1567019d6b8fSAnthony Liguori 
1568019d6b8fSAnthony Liguori /*
1569019d6b8fSAnthony Liguori  * Idea of the write support (use snapshot):
1570019d6b8fSAnthony Liguori  *
1571019d6b8fSAnthony Liguori  * 1. check if all data is consistent, recording renames, modifications,
1572019d6b8fSAnthony Liguori  *    new files and directories (in s->commits).
1573019d6b8fSAnthony Liguori  *
1574019d6b8fSAnthony Liguori  * 2. if the data is not consistent, stop committing
1575019d6b8fSAnthony Liguori  *
1576019d6b8fSAnthony Liguori  * 3. handle renames, and create new files and directories (do not yet
1577019d6b8fSAnthony Liguori  *    write their contents)
1578019d6b8fSAnthony Liguori  *
1579019d6b8fSAnthony Liguori  * 4. walk the directories, fixing the mapping and direntries, and marking
1580019d6b8fSAnthony Liguori  *    the handled mappings as not deleted
1581019d6b8fSAnthony Liguori  *
1582019d6b8fSAnthony Liguori  * 5. commit the contents of the files
1583019d6b8fSAnthony Liguori  *
1584019d6b8fSAnthony Liguori  * 6. handle deleted files and directories
1585019d6b8fSAnthony Liguori  *
1586019d6b8fSAnthony Liguori  */
1587019d6b8fSAnthony Liguori 
1588c227f099SAnthony Liguori typedef struct commit_t {
1589019d6b8fSAnthony Liguori     char* path;
1590019d6b8fSAnthony Liguori     union {
1591019d6b8fSAnthony Liguori         struct { uint32_t cluster; } rename;
1592019d6b8fSAnthony Liguori         struct { int dir_index; uint32_t modified_offset; } writeout;
1593019d6b8fSAnthony Liguori         struct { uint32_t first_cluster; } new_file;
1594019d6b8fSAnthony Liguori         struct { uint32_t cluster; } mkdir;
1595019d6b8fSAnthony Liguori     } param;
1596019d6b8fSAnthony Liguori     /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
1597019d6b8fSAnthony Liguori     enum {
1598019d6b8fSAnthony Liguori         ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
1599019d6b8fSAnthony Liguori     } action;
1600c227f099SAnthony Liguori } commit_t;
1601019d6b8fSAnthony Liguori 
clear_commits(BDRVVVFATState * s)1602019d6b8fSAnthony Liguori static void clear_commits(BDRVVVFATState* s)
1603019d6b8fSAnthony Liguori {
1604019d6b8fSAnthony Liguori     int i;
1605c9eb2f3eSAlexChen DLOG(fprintf(stderr, "clear_commits (%u commits)\n", s->commits.next));
1606019d6b8fSAnthony Liguori     for (i = 0; i < s->commits.next; i++) {
1607c227f099SAnthony Liguori         commit_t* commit = array_get(&(s->commits), i);
1608019d6b8fSAnthony Liguori         assert(commit->path || commit->action == ACTION_WRITEOUT);
1609019d6b8fSAnthony Liguori         if (commit->action != ACTION_WRITEOUT) {
1610019d6b8fSAnthony Liguori             assert(commit->path);
1611ce137829SStefan Weil             g_free(commit->path);
1612019d6b8fSAnthony Liguori         } else
1613019d6b8fSAnthony Liguori             assert(commit->path == NULL);
1614019d6b8fSAnthony Liguori     }
1615019d6b8fSAnthony Liguori     s->commits.next = 0;
1616019d6b8fSAnthony Liguori }
1617019d6b8fSAnthony Liguori 
schedule_rename(BDRVVVFATState * s,uint32_t cluster,char * new_path)1618019d6b8fSAnthony Liguori static void schedule_rename(BDRVVVFATState* s,
1619019d6b8fSAnthony Liguori         uint32_t cluster, char* new_path)
1620019d6b8fSAnthony Liguori {
1621c227f099SAnthony Liguori     commit_t* commit = array_get_next(&(s->commits));
1622019d6b8fSAnthony Liguori     commit->path = new_path;
1623019d6b8fSAnthony Liguori     commit->param.rename.cluster = cluster;
1624019d6b8fSAnthony Liguori     commit->action = ACTION_RENAME;
1625019d6b8fSAnthony Liguori }
1626019d6b8fSAnthony Liguori 
schedule_writeout(BDRVVVFATState * s,int dir_index,uint32_t modified_offset)1627019d6b8fSAnthony Liguori static void schedule_writeout(BDRVVVFATState* s,
1628019d6b8fSAnthony Liguori         int dir_index, uint32_t modified_offset)
1629019d6b8fSAnthony Liguori {
1630c227f099SAnthony Liguori     commit_t* commit = array_get_next(&(s->commits));
1631019d6b8fSAnthony Liguori     commit->path = NULL;
1632019d6b8fSAnthony Liguori     commit->param.writeout.dir_index = dir_index;
1633019d6b8fSAnthony Liguori     commit->param.writeout.modified_offset = modified_offset;
1634019d6b8fSAnthony Liguori     commit->action = ACTION_WRITEOUT;
1635019d6b8fSAnthony Liguori }
1636019d6b8fSAnthony Liguori 
schedule_new_file(BDRVVVFATState * s,char * path,uint32_t first_cluster)1637019d6b8fSAnthony Liguori static void schedule_new_file(BDRVVVFATState* s,
1638019d6b8fSAnthony Liguori         char* path, uint32_t first_cluster)
1639019d6b8fSAnthony Liguori {
1640c227f099SAnthony Liguori     commit_t* commit = array_get_next(&(s->commits));
1641019d6b8fSAnthony Liguori     commit->path = path;
1642019d6b8fSAnthony Liguori     commit->param.new_file.first_cluster = first_cluster;
1643019d6b8fSAnthony Liguori     commit->action = ACTION_NEW_FILE;
1644019d6b8fSAnthony Liguori }
1645019d6b8fSAnthony Liguori 
schedule_mkdir(BDRVVVFATState * s,uint32_t cluster,char * path)1646019d6b8fSAnthony Liguori static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
1647019d6b8fSAnthony Liguori {
1648c227f099SAnthony Liguori     commit_t* commit = array_get_next(&(s->commits));
1649019d6b8fSAnthony Liguori     commit->path = path;
1650019d6b8fSAnthony Liguori     commit->param.mkdir.cluster = cluster;
1651019d6b8fSAnthony Liguori     commit->action = ACTION_MKDIR;
1652019d6b8fSAnthony Liguori }
1653019d6b8fSAnthony Liguori 
1654019d6b8fSAnthony Liguori typedef struct {
1655019d6b8fSAnthony Liguori     /*
1656019d6b8fSAnthony Liguori      * Since the sequence number is at most 0x3f, and the filename
1657019d6b8fSAnthony Liguori      * length is at most 13 times the sequence number, the maximal
1658019d6b8fSAnthony Liguori      * filename length is 0x3f * 13 bytes.
1659019d6b8fSAnthony Liguori      */
1660019d6b8fSAnthony Liguori     unsigned char name[0x3f * 13 + 1];
1661e03da26bSHervé Poussineau     gunichar2 name2[0x3f * 13 + 1];
1662019d6b8fSAnthony Liguori     int checksum, len;
1663019d6b8fSAnthony Liguori     int sequence_number;
1664019d6b8fSAnthony Liguori } long_file_name;
1665019d6b8fSAnthony Liguori 
lfn_init(long_file_name * lfn)1666019d6b8fSAnthony Liguori static void lfn_init(long_file_name* lfn)
1667019d6b8fSAnthony Liguori {
1668019d6b8fSAnthony Liguori    lfn->sequence_number = lfn->len = 0;
1669019d6b8fSAnthony Liguori    lfn->checksum = 0x100;
1670019d6b8fSAnthony Liguori }
1671019d6b8fSAnthony Liguori 
1672019d6b8fSAnthony 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)1673019d6b8fSAnthony Liguori static int parse_long_name(long_file_name* lfn,
1674c227f099SAnthony Liguori         const direntry_t* direntry)
1675019d6b8fSAnthony Liguori {
1676019d6b8fSAnthony Liguori     int i, j, offset;
1677019d6b8fSAnthony Liguori     const unsigned char* pointer = (const unsigned char*)direntry;
1678019d6b8fSAnthony Liguori 
1679019d6b8fSAnthony Liguori     if (!is_long_name(direntry))
1680019d6b8fSAnthony Liguori         return 1;
1681019d6b8fSAnthony Liguori 
1682019d6b8fSAnthony Liguori     if (pointer[0] & 0x40) {
1683e03da26bSHervé Poussineau         /* first entry; do some initialization */
1684019d6b8fSAnthony Liguori         lfn->sequence_number = pointer[0] & 0x3f;
1685019d6b8fSAnthony Liguori         lfn->checksum = pointer[13];
1686019d6b8fSAnthony Liguori         lfn->name[0] = 0;
1687019d6b8fSAnthony Liguori         lfn->name[lfn->sequence_number * 13] = 0;
1688e03da26bSHervé Poussineau     } else if ((pointer[0] & 0x3f) != --lfn->sequence_number) {
1689e03da26bSHervé Poussineau         /* not the expected sequence number */
1690019d6b8fSAnthony Liguori         return -1;
1691e03da26bSHervé Poussineau     } else if (pointer[13] != lfn->checksum) {
1692e03da26bSHervé Poussineau         /* not the expected checksum */
1693019d6b8fSAnthony Liguori         return -2;
1694e03da26bSHervé Poussineau     } else if (pointer[12] || pointer[26] || pointer[27]) {
1695e03da26bSHervé Poussineau         /* invalid zero fields */
1696019d6b8fSAnthony Liguori         return -3;
1697e03da26bSHervé Poussineau     }
1698019d6b8fSAnthony Liguori 
1699019d6b8fSAnthony Liguori     offset = 13 * (lfn->sequence_number - 1);
1700019d6b8fSAnthony Liguori     for (i = 0, j = 1; i < 13; i++, j+=2) {
1701019d6b8fSAnthony Liguori         if (j == 11)
1702019d6b8fSAnthony Liguori             j = 14;
1703019d6b8fSAnthony Liguori         else if (j == 26)
1704019d6b8fSAnthony Liguori             j = 28;
1705019d6b8fSAnthony Liguori 
1706e03da26bSHervé Poussineau         if (pointer[j] == 0 && pointer[j + 1] == 0) {
1707e03da26bSHervé Poussineau             /* end of long file name */
1708e03da26bSHervé Poussineau             break;
1709e03da26bSHervé Poussineau         }
1710e03da26bSHervé Poussineau         gunichar2 c = (pointer[j + 1] << 8) + pointer[j];
1711e03da26bSHervé Poussineau         lfn->name2[offset + i] = c;
1712019d6b8fSAnthony Liguori     }
1713019d6b8fSAnthony Liguori 
1714e03da26bSHervé Poussineau     if (pointer[0] & 0x40) {
1715e03da26bSHervé Poussineau         /* first entry; set len */
1716e03da26bSHervé Poussineau         lfn->len = offset + i;
1717e03da26bSHervé Poussineau     }
1718e03da26bSHervé Poussineau     if ((pointer[0] & 0x3f) == 0x01) {
1719e03da26bSHervé Poussineau         /* last entry; finalize entry */
1720e03da26bSHervé Poussineau         glong olen;
1721e03da26bSHervé Poussineau         gchar *utf8 = g_utf16_to_utf8(lfn->name2, lfn->len, NULL, &olen, NULL);
1722e03da26bSHervé Poussineau         if (!utf8) {
1723e03da26bSHervé Poussineau             return -4;
1724e03da26bSHervé Poussineau         }
1725e03da26bSHervé Poussineau         lfn->len = olen;
1726e03da26bSHervé Poussineau         memcpy(lfn->name, utf8, olen + 1);
1727e03da26bSHervé Poussineau         g_free(utf8);
1728e03da26bSHervé Poussineau     }
1729019d6b8fSAnthony Liguori 
1730019d6b8fSAnthony Liguori     return 0;
1731019d6b8fSAnthony Liguori }
1732019d6b8fSAnthony Liguori 
1733019d6b8fSAnthony 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)1734019d6b8fSAnthony Liguori static int parse_short_name(BDRVVVFATState* s,
1735c227f099SAnthony Liguori         long_file_name* lfn, direntry_t* direntry)
1736019d6b8fSAnthony Liguori {
1737019d6b8fSAnthony Liguori     int i, j;
1738019d6b8fSAnthony Liguori 
1739019d6b8fSAnthony Liguori     if (!is_short_name(direntry))
1740019d6b8fSAnthony Liguori         return 1;
1741019d6b8fSAnthony Liguori 
1742019d6b8fSAnthony Liguori     for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
1743019d6b8fSAnthony Liguori     for (i = 0; i <= j; i++) {
1744e03da26bSHervé Poussineau         uint8_t c = direntry->name[i];
1745e03da26bSHervé Poussineau         if (c != to_valid_short_char(c)) {
1746019d6b8fSAnthony Liguori             return -1;
1747e03da26bSHervé Poussineau         } else if (s->downcase_short_names) {
1748019d6b8fSAnthony Liguori             lfn->name[i] = qemu_tolower(direntry->name[i]);
1749e03da26bSHervé Poussineau         } else {
1750019d6b8fSAnthony Liguori             lfn->name[i] = direntry->name[i];
1751019d6b8fSAnthony Liguori         }
1752e03da26bSHervé Poussineau     }
1753019d6b8fSAnthony Liguori 
1754f671d173SStefan Weil     for (j = 2; j >= 0 && direntry->name[8 + j] == ' '; j--) {
1755f671d173SStefan Weil     }
1756019d6b8fSAnthony Liguori     if (j >= 0) {
1757019d6b8fSAnthony Liguori         lfn->name[i++] = '.';
1758019d6b8fSAnthony Liguori         lfn->name[i + j + 1] = '\0';
1759019d6b8fSAnthony Liguori         for (;j >= 0; j--) {
1760f671d173SStefan Weil             uint8_t c = direntry->name[8 + j];
1761e03da26bSHervé Poussineau             if (c != to_valid_short_char(c)) {
1762019d6b8fSAnthony Liguori                 return -2;
1763f671d173SStefan Weil             } else if (s->downcase_short_names) {
1764f671d173SStefan Weil                 lfn->name[i + j] = qemu_tolower(c);
1765f671d173SStefan Weil             } else {
1766f671d173SStefan Weil                 lfn->name[i + j] = c;
1767f671d173SStefan Weil             }
1768019d6b8fSAnthony Liguori         }
1769019d6b8fSAnthony Liguori     } else
1770019d6b8fSAnthony Liguori         lfn->name[i + j + 1] = '\0';
1771019d6b8fSAnthony Liguori 
17728c4517fdSHervé Poussineau     if (lfn->name[0] == DIR_KANJI_FAKE) {
17738c4517fdSHervé Poussineau         lfn->name[0] = DIR_KANJI;
177478f002c9SHervé Poussineau     }
1775019d6b8fSAnthony Liguori     lfn->len = strlen((char*)lfn->name);
1776019d6b8fSAnthony Liguori 
1777019d6b8fSAnthony Liguori     return 0;
1778019d6b8fSAnthony Liguori }
1779019d6b8fSAnthony Liguori 
modified_fat_get(BDRVVVFATState * s,unsigned int cluster)1780019d6b8fSAnthony Liguori static inline uint32_t modified_fat_get(BDRVVVFATState* s,
1781019d6b8fSAnthony Liguori         unsigned int cluster)
1782019d6b8fSAnthony Liguori {
1783019d6b8fSAnthony Liguori     if (cluster < s->last_cluster_of_root_directory) {
1784019d6b8fSAnthony Liguori         if (cluster + 1 == s->last_cluster_of_root_directory)
1785019d6b8fSAnthony Liguori             return s->max_fat_value;
1786019d6b8fSAnthony Liguori         else
1787019d6b8fSAnthony Liguori             return cluster + 1;
1788019d6b8fSAnthony Liguori     }
1789019d6b8fSAnthony Liguori 
1790019d6b8fSAnthony Liguori     if (s->fat_type==32) {
1791019d6b8fSAnthony Liguori         uint32_t* entry=((uint32_t*)s->fat2)+cluster;
1792019d6b8fSAnthony Liguori         return le32_to_cpu(*entry);
1793019d6b8fSAnthony Liguori     } else if (s->fat_type==16) {
1794019d6b8fSAnthony Liguori         uint16_t* entry=((uint16_t*)s->fat2)+cluster;
1795019d6b8fSAnthony Liguori         return le16_to_cpu(*entry);
1796019d6b8fSAnthony Liguori     } else {
1797019d6b8fSAnthony Liguori         const uint8_t* x=s->fat2+cluster*3/2;
1798019d6b8fSAnthony Liguori         return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
1799019d6b8fSAnthony Liguori     }
1800019d6b8fSAnthony Liguori }
1801019d6b8fSAnthony Liguori 
1802eab76d58SPaolo Bonzini static inline bool coroutine_fn GRAPH_RDLOCK
cluster_was_modified(BDRVVVFATState * s,uint32_t cluster_num)1803eab76d58SPaolo Bonzini cluster_was_modified(BDRVVVFATState *s, uint32_t cluster_num)
1804019d6b8fSAnthony Liguori {
1805019d6b8fSAnthony Liguori     int was_modified = 0;
1806d6a644bbSEric Blake     int i;
1807019d6b8fSAnthony Liguori 
1808eecc7747SKevin Wolf     if (s->qcow == NULL) {
1809019d6b8fSAnthony Liguori         return 0;
1810eecc7747SKevin Wolf     }
1811019d6b8fSAnthony Liguori 
1812eecc7747SKevin Wolf     for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) {
1813cc323997SPaolo Bonzini         was_modified = bdrv_co_is_allocated(s->qcow->bs,
1814d6a644bbSEric Blake                                             (cluster2sector(s, cluster_num) +
1815d6a644bbSEric Blake                                              i) * BDRV_SECTOR_SIZE,
1816d6a644bbSEric Blake                                             BDRV_SECTOR_SIZE, NULL);
1817eecc7747SKevin Wolf     }
1818019d6b8fSAnthony Liguori 
18196f712ee0SEric Blake     /*
18206f712ee0SEric Blake      * Note that this treats failures to learn allocation status the
18216f712ee0SEric Blake      * same as if an allocation has occurred.  It's as safe as
18226f712ee0SEric Blake      * anything else, given that a failure to learn allocation status
18236f712ee0SEric Blake      * will probably result in more failures.
18246f712ee0SEric Blake      */
18256f712ee0SEric Blake     return !!was_modified;
1826019d6b8fSAnthony Liguori }
1827019d6b8fSAnthony Liguori 
get_basename(const char * path)1828019d6b8fSAnthony Liguori static const char* get_basename(const char* path)
1829019d6b8fSAnthony Liguori {
1830019d6b8fSAnthony Liguori     char* basename = strrchr(path, '/');
1831019d6b8fSAnthony Liguori     if (basename == NULL)
1832019d6b8fSAnthony Liguori         return path;
1833019d6b8fSAnthony Liguori     else
1834019d6b8fSAnthony Liguori         return basename + 1; /* strip '/' */
1835019d6b8fSAnthony Liguori }
1836019d6b8fSAnthony Liguori 
1837019d6b8fSAnthony Liguori /*
1838019d6b8fSAnthony Liguori  * The array s->used_clusters holds the states of the clusters. If it is
1839019d6b8fSAnthony Liguori  * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
1840019d6b8fSAnthony Liguori  * was modified, bit 3 is set.
1841019d6b8fSAnthony Liguori  * If any cluster is allocated, but not part of a file or directory, this
1842019d6b8fSAnthony Liguori  * driver refuses to commit.
1843019d6b8fSAnthony Liguori  */
1844019d6b8fSAnthony Liguori typedef enum {
1845019d6b8fSAnthony Liguori      USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
1846c227f099SAnthony Liguori } used_t;
1847019d6b8fSAnthony Liguori 
1848019d6b8fSAnthony Liguori /*
1849019d6b8fSAnthony Liguori  * get_cluster_count_for_direntry() not only determines how many clusters
1850019d6b8fSAnthony Liguori  * are occupied by direntry, but also if it was renamed or modified.
1851019d6b8fSAnthony Liguori  *
1852019d6b8fSAnthony Liguori  * A file is thought to be renamed *only* if there already was a file with
1853019d6b8fSAnthony Liguori  * exactly the same first cluster, but a different name.
1854019d6b8fSAnthony Liguori  *
1855019d6b8fSAnthony Liguori  * Further, the files/directories handled by this function are
1856019d6b8fSAnthony Liguori  * assumed to be *not* deleted (and *only* those).
1857019d6b8fSAnthony Liguori  */
1858eab76d58SPaolo Bonzini static uint32_t coroutine_fn GRAPH_RDLOCK
get_cluster_count_for_direntry(BDRVVVFATState * s,direntry_t * direntry,const char * path)1859eab76d58SPaolo Bonzini get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const char* path)
1860019d6b8fSAnthony Liguori {
1861019d6b8fSAnthony Liguori     /*
1862019d6b8fSAnthony Liguori      * This is a little bit tricky:
1863019d6b8fSAnthony Liguori      * IF the guest OS just inserts a cluster into the file chain,
1864019d6b8fSAnthony Liguori      * and leaves the rest alone, (i.e. the original file had clusters
1865019d6b8fSAnthony Liguori      * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
1866019d6b8fSAnthony Liguori      *
1867019d6b8fSAnthony Liguori      * - do_commit will write the cluster into the file at the given
1868019d6b8fSAnthony Liguori      *   offset, but
1869019d6b8fSAnthony Liguori      *
1870019d6b8fSAnthony Liguori      * - the cluster which is overwritten should be moved to a later
1871019d6b8fSAnthony Liguori      *   position in the file.
1872019d6b8fSAnthony Liguori      *
1873019d6b8fSAnthony Liguori      * I am not aware that any OS does something as braindead, but this
1874019d6b8fSAnthony Liguori      * situation could happen anyway when not committing for a long time.
1875019d6b8fSAnthony Liguori      * Just to be sure that this does not bite us, detect it, and copy the
1876019d6b8fSAnthony Liguori      * contents of the clusters to-be-overwritten into the qcow.
1877019d6b8fSAnthony Liguori      */
1878019d6b8fSAnthony Liguori     int copy_it = 0;
1879019d6b8fSAnthony Liguori     int was_modified = 0;
1880019d6b8fSAnthony Liguori     int32_t ret = 0;
1881019d6b8fSAnthony Liguori 
1882019d6b8fSAnthony Liguori     uint32_t cluster_num = begin_of_direntry(direntry);
1883019d6b8fSAnthony Liguori     uint32_t offset = 0;
1884c227f099SAnthony Liguori     mapping_t* mapping = NULL;
1885019d6b8fSAnthony Liguori     const char* basename2 = NULL;
1886019d6b8fSAnthony Liguori 
1887019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
1888019d6b8fSAnthony Liguori 
1889019d6b8fSAnthony Liguori     /* the root directory */
1890019d6b8fSAnthony Liguori     if (cluster_num == 0)
1891019d6b8fSAnthony Liguori         return 0;
1892019d6b8fSAnthony Liguori 
1893019d6b8fSAnthony Liguori     /* write support */
1894019d6b8fSAnthony Liguori     if (s->qcow) {
1895019d6b8fSAnthony Liguori         basename2 = get_basename(path);
1896019d6b8fSAnthony Liguori 
1897019d6b8fSAnthony Liguori         mapping = find_mapping_for_cluster(s, cluster_num);
1898019d6b8fSAnthony Liguori 
1899019d6b8fSAnthony Liguori         if (mapping) {
1900019d6b8fSAnthony Liguori             const char* basename;
1901019d6b8fSAnthony Liguori 
1902019d6b8fSAnthony Liguori             assert(mapping->mode & MODE_DELETED);
1903019d6b8fSAnthony Liguori             mapping->mode &= ~MODE_DELETED;
1904019d6b8fSAnthony Liguori 
1905019d6b8fSAnthony Liguori             basename = get_basename(mapping->path);
1906019d6b8fSAnthony Liguori 
1907019d6b8fSAnthony Liguori             assert(mapping->mode & MODE_NORMAL);
1908019d6b8fSAnthony Liguori 
1909019d6b8fSAnthony Liguori             /* rename */
1910019d6b8fSAnthony Liguori             if (strcmp(basename, basename2))
19117267c094SAnthony Liguori                 schedule_rename(s, cluster_num, g_strdup(path));
1912019d6b8fSAnthony Liguori         } else if (is_file(direntry))
1913019d6b8fSAnthony Liguori             /* new file */
19147267c094SAnthony Liguori             schedule_new_file(s, g_strdup(path), cluster_num);
1915019d6b8fSAnthony Liguori         else {
191643dc2a64SBlue Swirl             abort();
1917019d6b8fSAnthony Liguori             return 0;
1918019d6b8fSAnthony Liguori         }
1919019d6b8fSAnthony Liguori     }
1920019d6b8fSAnthony Liguori 
1921019d6b8fSAnthony Liguori     while(1) {
1922019d6b8fSAnthony Liguori         if (s->qcow) {
1923019d6b8fSAnthony Liguori             if (!copy_it && cluster_was_modified(s, cluster_num)) {
1924019d6b8fSAnthony Liguori                 if (mapping == NULL ||
1925019d6b8fSAnthony Liguori                         mapping->begin > cluster_num ||
1926019d6b8fSAnthony Liguori                         mapping->end <= cluster_num)
1927019d6b8fSAnthony Liguori                 mapping = find_mapping_for_cluster(s, cluster_num);
1928019d6b8fSAnthony Liguori 
1929019d6b8fSAnthony Liguori 
1930019d6b8fSAnthony Liguori                 if (mapping &&
1931019d6b8fSAnthony Liguori                         (mapping->mode & MODE_DIRECTORY) == 0) {
1932019d6b8fSAnthony Liguori 
1933019d6b8fSAnthony Liguori                     /* was modified in qcow */
193421b25a0eSAmjad Alsharafi                     if (offset != s->cluster_size
193521b25a0eSAmjad Alsharafi                             * ((cluster_num - mapping->begin)
193621b25a0eSAmjad Alsharafi                             + mapping->info.file.offset)) {
1937019d6b8fSAnthony Liguori                         /* offset of this cluster in file chain has changed */
193843dc2a64SBlue Swirl                         abort();
1939019d6b8fSAnthony Liguori                         copy_it = 1;
1940019d6b8fSAnthony Liguori                     } else if (offset == 0) {
1941019d6b8fSAnthony Liguori                         const char* basename = get_basename(mapping->path);
1942019d6b8fSAnthony Liguori 
1943019d6b8fSAnthony Liguori                         if (strcmp(basename, basename2))
1944019d6b8fSAnthony Liguori                             copy_it = 1;
1945019d6b8fSAnthony Liguori                     }
1946f60a6f7eSAmjad Alsharafi                     assert(mapping->first_mapping_index == -1
1947f60a6f7eSAmjad Alsharafi                             || mapping->info.file.offset > 0);
1948019d6b8fSAnthony Liguori 
1949019d6b8fSAnthony Liguori                     /* need to write out? */
1950019d6b8fSAnthony Liguori                     if (!was_modified && is_file(direntry)) {
1951019d6b8fSAnthony Liguori                         was_modified = 1;
1952019d6b8fSAnthony Liguori                         schedule_writeout(s, mapping->dir_index, offset);
1953019d6b8fSAnthony Liguori                     }
1954019d6b8fSAnthony Liguori                 }
1955019d6b8fSAnthony Liguori             }
1956019d6b8fSAnthony Liguori 
1957019d6b8fSAnthony Liguori             if (copy_it) {
1958d6a644bbSEric Blake                 int i;
1959019d6b8fSAnthony Liguori                 /*
1960019d6b8fSAnthony Liguori                  * This is horribly inefficient, but that is okay, since
1961019d6b8fSAnthony Liguori                  * it is rarely executed, if at all.
1962019d6b8fSAnthony Liguori                  */
1963fb2575f9SMarkus Armbruster                 int64_t offs = cluster2sector(s, cluster_num);
1964019d6b8fSAnthony Liguori 
1965019d6b8fSAnthony Liguori                 vvfat_close_current_file(s);
19667704df98SKevin Wolf                 for (i = 0; i < s->sectors_per_cluster; i++) {
1967eecc7747SKevin Wolf                     int res;
1968eecc7747SKevin Wolf 
1969cc323997SPaolo Bonzini                     res = bdrv_co_is_allocated(s->qcow->bs,
1970fb2575f9SMarkus Armbruster                                                (offs + i) * BDRV_SECTOR_SIZE,
1971d6a644bbSEric Blake                                                BDRV_SECTOR_SIZE, NULL);
19726f712ee0SEric Blake                     if (res < 0) {
19736f712ee0SEric Blake                         return -1;
19746f712ee0SEric Blake                     }
1975eecc7747SKevin Wolf                     if (!res) {
1976fb2575f9SMarkus Armbruster                         res = vvfat_read(s->bs, offs, s->cluster_buffer, 1);
1977eecc7747SKevin Wolf                         if (res) {
1978019d6b8fSAnthony Liguori                             return -1;
19797704df98SKevin Wolf                         }
1980fb2575f9SMarkus Armbruster                         res = bdrv_co_pwrite(s->qcow, offs * BDRV_SECTOR_SIZE,
198132cc71deSAlberto Faria                                              BDRV_SECTOR_SIZE, s->cluster_buffer,
198253fb7844SAlberto Faria                                              0);
1983e5a0a678SAlberto Garcia                         if (res < 0) {
1984019d6b8fSAnthony Liguori                             return -2;
1985019d6b8fSAnthony Liguori                         }
1986019d6b8fSAnthony Liguori                     }
1987019d6b8fSAnthony Liguori                 }
19887704df98SKevin Wolf             }
19897704df98SKevin Wolf         }
1990019d6b8fSAnthony Liguori 
1991019d6b8fSAnthony Liguori         ret++;
1992019d6b8fSAnthony Liguori         if (s->used_clusters[cluster_num] & USED_ANY)
1993019d6b8fSAnthony Liguori             return 0;
1994019d6b8fSAnthony Liguori         s->used_clusters[cluster_num] = USED_FILE;
1995019d6b8fSAnthony Liguori 
1996019d6b8fSAnthony Liguori         cluster_num = modified_fat_get(s, cluster_num);
1997019d6b8fSAnthony Liguori 
1998019d6b8fSAnthony Liguori         if (fat_eof(s, cluster_num))
1999019d6b8fSAnthony Liguori             return ret;
2000019d6b8fSAnthony Liguori         else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
2001019d6b8fSAnthony Liguori             return -1;
2002019d6b8fSAnthony Liguori 
2003019d6b8fSAnthony Liguori         offset += s->cluster_size;
2004019d6b8fSAnthony Liguori     }
2005019d6b8fSAnthony Liguori }
2006019d6b8fSAnthony Liguori 
2007019d6b8fSAnthony Liguori /*
2008019d6b8fSAnthony Liguori  * This function looks at the modified data (qcow).
2009019d6b8fSAnthony Liguori  * It returns 0 upon inconsistency or error, and the number of clusters
2010019d6b8fSAnthony Liguori  * used by the directory, its subdirectories and their files.
2011019d6b8fSAnthony Liguori  */
2012eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
check_directory_consistency(BDRVVVFATState * s,int cluster_num,const char * path)2013eab76d58SPaolo Bonzini check_directory_consistency(BDRVVVFATState *s, int cluster_num, const char* path)
2014019d6b8fSAnthony Liguori {
2015019d6b8fSAnthony Liguori     int ret = 0;
20167267c094SAnthony Liguori     unsigned char* cluster = g_malloc(s->cluster_size);
2017c227f099SAnthony Liguori     direntry_t* direntries = (direntry_t*)cluster;
2018c227f099SAnthony Liguori     mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
2019019d6b8fSAnthony Liguori 
2020019d6b8fSAnthony Liguori     long_file_name lfn;
2021019d6b8fSAnthony Liguori     int path_len = strlen(path);
20220d460d6fSKevin Wolf     char path2[PATH_MAX + 1];
2023019d6b8fSAnthony Liguori 
2024019d6b8fSAnthony Liguori     assert(path_len < PATH_MAX); /* len was tested before! */
2025019d6b8fSAnthony Liguori     pstrcpy(path2, sizeof(path2), path);
2026019d6b8fSAnthony Liguori     path2[path_len] = '/';
2027019d6b8fSAnthony Liguori     path2[path_len + 1] = '\0';
2028019d6b8fSAnthony Liguori 
2029019d6b8fSAnthony Liguori     if (mapping) {
2030019d6b8fSAnthony Liguori         const char* basename = get_basename(mapping->path);
2031019d6b8fSAnthony Liguori         const char* basename2 = get_basename(path);
2032019d6b8fSAnthony Liguori 
2033019d6b8fSAnthony Liguori         assert(mapping->mode & MODE_DIRECTORY);
2034019d6b8fSAnthony Liguori 
2035019d6b8fSAnthony Liguori         assert(mapping->mode & MODE_DELETED);
2036019d6b8fSAnthony Liguori         mapping->mode &= ~MODE_DELETED;
2037019d6b8fSAnthony Liguori 
2038019d6b8fSAnthony Liguori         if (strcmp(basename, basename2))
20397267c094SAnthony Liguori             schedule_rename(s, cluster_num, g_strdup(path));
2040019d6b8fSAnthony Liguori     } else
2041019d6b8fSAnthony Liguori         /* new directory */
20427267c094SAnthony Liguori         schedule_mkdir(s, cluster_num, g_strdup(path));
2043019d6b8fSAnthony Liguori 
2044019d6b8fSAnthony Liguori     lfn_init(&lfn);
2045019d6b8fSAnthony Liguori     do {
2046019d6b8fSAnthony Liguori         int i;
2047019d6b8fSAnthony Liguori         int subret = 0;
2048019d6b8fSAnthony Liguori 
2049019d6b8fSAnthony Liguori         ret++;
2050019d6b8fSAnthony Liguori 
2051019d6b8fSAnthony Liguori         if (s->used_clusters[cluster_num] & USED_ANY) {
2052019d6b8fSAnthony Liguori             fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
20536262bbd3SMarkus Armbruster             goto fail;
2054019d6b8fSAnthony Liguori         }
2055019d6b8fSAnthony Liguori         s->used_clusters[cluster_num] = USED_DIRECTORY;
2056019d6b8fSAnthony Liguori 
2057019d6b8fSAnthony Liguori DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
2058019d6b8fSAnthony Liguori         subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
2059019d6b8fSAnthony Liguori                 s->sectors_per_cluster);
2060019d6b8fSAnthony Liguori         if (subret) {
2061019d6b8fSAnthony Liguori             fprintf(stderr, "Error fetching direntries\n");
2062019d6b8fSAnthony Liguori         fail:
2063ce137829SStefan Weil             g_free(cluster);
2064019d6b8fSAnthony Liguori             return 0;
2065019d6b8fSAnthony Liguori         }
2066019d6b8fSAnthony Liguori 
2067019d6b8fSAnthony Liguori         for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
2068019d6b8fSAnthony Liguori             int cluster_count = 0;
2069019d6b8fSAnthony Liguori 
2070019d6b8fSAnthony Liguori DLOG(fprintf(stderr, "check direntry %d:\n", i); print_direntry(direntries + i));
2071019d6b8fSAnthony Liguori             if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
2072019d6b8fSAnthony Liguori                     is_free(direntries + i))
2073019d6b8fSAnthony Liguori                 continue;
2074019d6b8fSAnthony Liguori 
2075019d6b8fSAnthony Liguori             subret = parse_long_name(&lfn, direntries + i);
2076019d6b8fSAnthony Liguori             if (subret < 0) {
2077019d6b8fSAnthony Liguori                 fprintf(stderr, "Error in long name\n");
2078019d6b8fSAnthony Liguori                 goto fail;
2079019d6b8fSAnthony Liguori             }
2080019d6b8fSAnthony Liguori             if (subret == 0 || is_free(direntries + i))
2081019d6b8fSAnthony Liguori                 continue;
2082019d6b8fSAnthony Liguori 
2083019d6b8fSAnthony Liguori             if (fat_chksum(direntries+i) != lfn.checksum) {
2084019d6b8fSAnthony Liguori                 subret = parse_short_name(s, &lfn, direntries + i);
2085019d6b8fSAnthony Liguori                 if (subret < 0) {
2086019d6b8fSAnthony Liguori                     fprintf(stderr, "Error in short name (%d)\n", subret);
2087019d6b8fSAnthony Liguori                     goto fail;
2088019d6b8fSAnthony Liguori                 }
2089019d6b8fSAnthony Liguori                 if (subret > 0 || !strcmp((char*)lfn.name, ".")
2090019d6b8fSAnthony Liguori                         || !strcmp((char*)lfn.name, ".."))
2091019d6b8fSAnthony Liguori                     continue;
2092019d6b8fSAnthony Liguori             }
2093019d6b8fSAnthony Liguori             lfn.checksum = 0x100; /* cannot use long name twice */
2094019d6b8fSAnthony Liguori 
2095c79e243eSKevin Wolf             if (!valid_filename(lfn.name)) {
2096c79e243eSKevin Wolf                 fprintf(stderr, "Invalid file name\n");
2097c79e243eSKevin Wolf                 goto fail;
2098c79e243eSKevin Wolf             }
2099019d6b8fSAnthony Liguori             if (path_len + 1 + lfn.len >= PATH_MAX) {
2100019d6b8fSAnthony Liguori                 fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
2101019d6b8fSAnthony Liguori                 goto fail;
2102019d6b8fSAnthony Liguori             }
2103019d6b8fSAnthony Liguori             pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
2104019d6b8fSAnthony Liguori                     (char*)lfn.name);
2105019d6b8fSAnthony Liguori 
2106019d6b8fSAnthony Liguori             if (is_directory(direntries + i)) {
2107019d6b8fSAnthony Liguori                 if (begin_of_direntry(direntries + i) == 0) {
2108019d6b8fSAnthony Liguori                     DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
2109019d6b8fSAnthony Liguori                     goto fail;
2110019d6b8fSAnthony Liguori                 }
2111019d6b8fSAnthony Liguori                 cluster_count = check_directory_consistency(s,
2112019d6b8fSAnthony Liguori                         begin_of_direntry(direntries + i), path2);
2113019d6b8fSAnthony Liguori                 if (cluster_count == 0) {
2114019d6b8fSAnthony Liguori                     DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
2115019d6b8fSAnthony Liguori                     goto fail;
2116019d6b8fSAnthony Liguori                 }
2117019d6b8fSAnthony Liguori             } else if (is_file(direntries + i)) {
2118019d6b8fSAnthony Liguori                 /* check file size with FAT */
2119019d6b8fSAnthony Liguori                 cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
2120019d6b8fSAnthony Liguori                 if (cluster_count !=
212113385ae1SLaurent Vivier             DIV_ROUND_UP(le32_to_cpu(direntries[i].size), s->cluster_size)) {
2122019d6b8fSAnthony Liguori                     DLOG(fprintf(stderr, "Cluster count mismatch\n"));
2123019d6b8fSAnthony Liguori                     goto fail;
2124019d6b8fSAnthony Liguori                 }
2125019d6b8fSAnthony Liguori             } else
212643dc2a64SBlue Swirl                 abort(); /* cluster_count = 0; */
2127019d6b8fSAnthony Liguori 
2128019d6b8fSAnthony Liguori             ret += cluster_count;
2129019d6b8fSAnthony Liguori         }
2130019d6b8fSAnthony Liguori 
2131019d6b8fSAnthony Liguori         cluster_num = modified_fat_get(s, cluster_num);
2132019d6b8fSAnthony Liguori     } while(!fat_eof(s, cluster_num));
2133019d6b8fSAnthony Liguori 
2134ce137829SStefan Weil     g_free(cluster);
2135019d6b8fSAnthony Liguori     return ret;
2136019d6b8fSAnthony Liguori }
2137019d6b8fSAnthony Liguori 
2138019d6b8fSAnthony Liguori /* returns 1 on success */
2139eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
is_consistent(BDRVVVFATState * s)2140eab76d58SPaolo Bonzini is_consistent(BDRVVVFATState* s)
2141019d6b8fSAnthony Liguori {
2142019d6b8fSAnthony Liguori     int i, check;
2143019d6b8fSAnthony Liguori     int used_clusters_count = 0;
2144019d6b8fSAnthony Liguori 
2145019d6b8fSAnthony Liguori DLOG(checkpoint());
2146019d6b8fSAnthony Liguori     /*
2147019d6b8fSAnthony Liguori      * - get modified FAT
2148019d6b8fSAnthony Liguori      * - compare the two FATs (TODO)
2149019d6b8fSAnthony Liguori      * - get buffer for marking used clusters
2150f4649069SEric Blake      * - recurse direntries from root (using bs->bdrv_pread to make
2151019d6b8fSAnthony Liguori      *    sure to get the new data)
2152019d6b8fSAnthony Liguori      *   - check that the FAT agrees with the size
2153019d6b8fSAnthony Liguori      *   - count the number of clusters occupied by this directory and
2154019d6b8fSAnthony Liguori      *     its files
2155019d6b8fSAnthony Liguori      * - check that the cumulative used cluster count agrees with the
2156019d6b8fSAnthony Liguori      *   FAT
2157019d6b8fSAnthony Liguori      * - if all is fine, return number of used clusters
2158019d6b8fSAnthony Liguori      */
2159019d6b8fSAnthony Liguori     if (s->fat2 == NULL) {
2160019d6b8fSAnthony Liguori         int size = 0x200 * s->sectors_per_fat;
21617267c094SAnthony Liguori         s->fat2 = g_malloc(size);
2162019d6b8fSAnthony Liguori         memcpy(s->fat2, s->fat.pointer, size);
2163019d6b8fSAnthony Liguori     }
2164019d6b8fSAnthony Liguori     check = vvfat_read(s->bs,
21654dc705dcSHervé Poussineau             s->offset_to_fat, s->fat2, s->sectors_per_fat);
2166019d6b8fSAnthony Liguori     if (check) {
2167019d6b8fSAnthony Liguori         fprintf(stderr, "Could not copy fat\n");
2168019d6b8fSAnthony Liguori         return 0;
2169019d6b8fSAnthony Liguori     }
2170019d6b8fSAnthony Liguori     assert (s->used_clusters);
2171019d6b8fSAnthony Liguori     for (i = 0; i < sector2cluster(s, s->sector_count); i++)
2172019d6b8fSAnthony Liguori         s->used_clusters[i] &= ~USED_ANY;
2173019d6b8fSAnthony Liguori 
2174019d6b8fSAnthony Liguori     clear_commits(s);
2175019d6b8fSAnthony Liguori 
2176019d6b8fSAnthony Liguori     /* mark every mapped file/directory as deleted.
2177019d6b8fSAnthony Liguori      * (check_directory_consistency() will unmark those still present). */
2178019d6b8fSAnthony Liguori     if (s->qcow)
2179019d6b8fSAnthony Liguori         for (i = 0; i < s->mapping.next; i++) {
2180c227f099SAnthony Liguori             mapping_t* mapping = array_get(&(s->mapping), i);
2181019d6b8fSAnthony Liguori             if (mapping->first_mapping_index < 0)
2182019d6b8fSAnthony Liguori                 mapping->mode |= MODE_DELETED;
2183019d6b8fSAnthony Liguori         }
2184019d6b8fSAnthony Liguori 
2185019d6b8fSAnthony Liguori     used_clusters_count = check_directory_consistency(s, 0, s->path);
2186019d6b8fSAnthony Liguori     if (used_clusters_count <= 0) {
2187019d6b8fSAnthony Liguori         DLOG(fprintf(stderr, "problem in directory\n"));
2188019d6b8fSAnthony Liguori         return 0;
2189019d6b8fSAnthony Liguori     }
2190019d6b8fSAnthony Liguori 
2191019d6b8fSAnthony Liguori     check = s->last_cluster_of_root_directory;
2192019d6b8fSAnthony Liguori     for (i = check; i < sector2cluster(s, s->sector_count); i++) {
2193019d6b8fSAnthony Liguori         if (modified_fat_get(s, i)) {
2194019d6b8fSAnthony Liguori             if(!s->used_clusters[i]) {
2195019d6b8fSAnthony Liguori                 DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
2196019d6b8fSAnthony Liguori                 return 0;
2197019d6b8fSAnthony Liguori             }
2198019d6b8fSAnthony Liguori             check++;
2199019d6b8fSAnthony Liguori         }
2200019d6b8fSAnthony Liguori 
2201019d6b8fSAnthony Liguori         if (s->used_clusters[i] == USED_ALLOCATED) {
2202019d6b8fSAnthony Liguori             /* allocated, but not used... */
2203019d6b8fSAnthony Liguori             DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
2204019d6b8fSAnthony Liguori             return 0;
2205019d6b8fSAnthony Liguori         }
2206019d6b8fSAnthony Liguori     }
2207019d6b8fSAnthony Liguori 
2208019d6b8fSAnthony Liguori     if (check != used_clusters_count)
2209019d6b8fSAnthony Liguori         return 0;
2210019d6b8fSAnthony Liguori 
2211019d6b8fSAnthony Liguori     return used_clusters_count;
2212019d6b8fSAnthony Liguori }
2213019d6b8fSAnthony Liguori 
adjust_mapping_indices(BDRVVVFATState * s,int offset,int adjust)2214019d6b8fSAnthony Liguori static inline void adjust_mapping_indices(BDRVVVFATState* s,
2215019d6b8fSAnthony Liguori         int offset, int adjust)
2216019d6b8fSAnthony Liguori {
2217019d6b8fSAnthony Liguori     int i;
2218019d6b8fSAnthony Liguori 
2219019d6b8fSAnthony Liguori     for (i = 0; i < s->mapping.next; i++) {
2220c227f099SAnthony Liguori         mapping_t* mapping = array_get(&(s->mapping), i);
2221019d6b8fSAnthony Liguori 
2222019d6b8fSAnthony Liguori #define ADJUST_MAPPING_INDEX(name) \
2223019d6b8fSAnthony Liguori         if (mapping->name >= offset) \
2224019d6b8fSAnthony Liguori             mapping->name += adjust
2225019d6b8fSAnthony Liguori 
2226019d6b8fSAnthony Liguori         ADJUST_MAPPING_INDEX(first_mapping_index);
2227019d6b8fSAnthony Liguori         if (mapping->mode & MODE_DIRECTORY)
2228019d6b8fSAnthony Liguori             ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
2229019d6b8fSAnthony Liguori     }
2230019d6b8fSAnthony Liguori }
2231019d6b8fSAnthony Liguori 
2232019d6b8fSAnthony Liguori /* insert or update mapping */
insert_mapping(BDRVVVFATState * s,uint32_t begin,uint32_t end)2233c227f099SAnthony Liguori static mapping_t* insert_mapping(BDRVVVFATState* s,
2234019d6b8fSAnthony Liguori         uint32_t begin, uint32_t end)
2235019d6b8fSAnthony Liguori {
2236019d6b8fSAnthony Liguori     /*
2237019d6b8fSAnthony Liguori      * - find mapping where mapping->begin >= begin,
2238019d6b8fSAnthony Liguori      * - if mapping->begin > begin: insert
2239019d6b8fSAnthony Liguori      *   - adjust all references to mappings!
2240019d6b8fSAnthony Liguori      * - else: adjust
2241019d6b8fSAnthony Liguori      * - replace name
2242019d6b8fSAnthony Liguori      */
2243019d6b8fSAnthony Liguori     int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
2244c227f099SAnthony Liguori     mapping_t* mapping = NULL;
2245c227f099SAnthony Liguori     mapping_t* first_mapping = array_get(&(s->mapping), 0);
2246019d6b8fSAnthony Liguori 
2247019d6b8fSAnthony Liguori     if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
2248019d6b8fSAnthony Liguori             && mapping->begin < begin) {
2249019d6b8fSAnthony Liguori         mapping->end = begin;
2250019d6b8fSAnthony Liguori         index++;
2251019d6b8fSAnthony Liguori         mapping = array_get(&(s->mapping), index);
2252019d6b8fSAnthony Liguori     }
2253019d6b8fSAnthony Liguori     if (index >= s->mapping.next || mapping->begin > begin) {
2254019d6b8fSAnthony Liguori         mapping = array_insert(&(s->mapping), index, 1);
2255019d6b8fSAnthony Liguori         mapping->path = NULL;
2256019d6b8fSAnthony Liguori         adjust_mapping_indices(s, index, +1);
2257019d6b8fSAnthony Liguori     }
2258019d6b8fSAnthony Liguori 
2259019d6b8fSAnthony Liguori     mapping->begin = begin;
2260019d6b8fSAnthony Liguori     mapping->end = end;
2261019d6b8fSAnthony Liguori 
2262c227f099SAnthony Liguori DLOG(mapping_t* next_mapping;
2263019d6b8fSAnthony Liguori assert(index + 1 >= s->mapping.next ||
2264019d6b8fSAnthony Liguori ((next_mapping = array_get(&(s->mapping), index + 1)) &&
2265019d6b8fSAnthony Liguori  next_mapping->begin >= end)));
2266019d6b8fSAnthony Liguori 
2267c227f099SAnthony Liguori     if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
2268019d6b8fSAnthony Liguori         s->current_mapping = array_get(&(s->mapping),
2269019d6b8fSAnthony Liguori                 s->current_mapping - first_mapping);
2270019d6b8fSAnthony Liguori 
2271019d6b8fSAnthony Liguori     return mapping;
2272019d6b8fSAnthony Liguori }
2273019d6b8fSAnthony Liguori 
remove_mapping(BDRVVVFATState * s,int mapping_index)2274019d6b8fSAnthony Liguori static int remove_mapping(BDRVVVFATState* s, int mapping_index)
2275019d6b8fSAnthony Liguori {
2276c227f099SAnthony Liguori     mapping_t* mapping = array_get(&(s->mapping), mapping_index);
2277c227f099SAnthony Liguori     mapping_t* first_mapping = array_get(&(s->mapping), 0);
2278019d6b8fSAnthony Liguori 
2279019d6b8fSAnthony Liguori     /* free mapping */
2280ce137829SStefan Weil     if (mapping->first_mapping_index < 0) {
2281ce137829SStefan Weil         g_free(mapping->path);
2282ce137829SStefan Weil     }
2283019d6b8fSAnthony Liguori 
2284019d6b8fSAnthony Liguori     /* remove from s->mapping */
2285019d6b8fSAnthony Liguori     array_remove(&(s->mapping), mapping_index);
2286019d6b8fSAnthony Liguori 
2287019d6b8fSAnthony Liguori     /* adjust all references to mappings */
2288019d6b8fSAnthony Liguori     adjust_mapping_indices(s, mapping_index, -1);
2289019d6b8fSAnthony Liguori 
2290c227f099SAnthony Liguori     if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
2291019d6b8fSAnthony Liguori         s->current_mapping = array_get(&(s->mapping),
2292019d6b8fSAnthony Liguori                 s->current_mapping - first_mapping);
2293019d6b8fSAnthony Liguori 
2294019d6b8fSAnthony Liguori     return 0;
2295019d6b8fSAnthony Liguori }
2296019d6b8fSAnthony Liguori 
adjust_dirindices(BDRVVVFATState * s,int offset,int adjust)2297019d6b8fSAnthony Liguori static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
2298019d6b8fSAnthony Liguori {
2299019d6b8fSAnthony Liguori     int i;
2300019d6b8fSAnthony Liguori     for (i = 0; i < s->mapping.next; i++) {
2301c227f099SAnthony Liguori         mapping_t* mapping = array_get(&(s->mapping), i);
2302019d6b8fSAnthony Liguori         if (mapping->dir_index >= offset)
2303019d6b8fSAnthony Liguori             mapping->dir_index += adjust;
2304019d6b8fSAnthony Liguori         if ((mapping->mode & MODE_DIRECTORY) &&
2305019d6b8fSAnthony Liguori                 mapping->info.dir.first_dir_index >= offset)
2306019d6b8fSAnthony Liguori             mapping->info.dir.first_dir_index += adjust;
2307019d6b8fSAnthony Liguori     }
2308019d6b8fSAnthony Liguori }
2309019d6b8fSAnthony Liguori 
insert_direntries(BDRVVVFATState * s,int dir_index,int count)2310c227f099SAnthony Liguori static direntry_t* insert_direntries(BDRVVVFATState* s,
2311019d6b8fSAnthony Liguori         int dir_index, int count)
2312019d6b8fSAnthony Liguori {
2313019d6b8fSAnthony Liguori     /*
2314019d6b8fSAnthony Liguori      * make room in s->directory,
2315019d6b8fSAnthony Liguori      * adjust_dirindices
2316019d6b8fSAnthony Liguori      */
2317c227f099SAnthony Liguori     direntry_t* result = array_insert(&(s->directory), dir_index, count);
2318019d6b8fSAnthony Liguori     if (result == NULL)
2319019d6b8fSAnthony Liguori         return NULL;
2320019d6b8fSAnthony Liguori     adjust_dirindices(s, dir_index, count);
2321019d6b8fSAnthony Liguori     return result;
2322019d6b8fSAnthony Liguori }
2323019d6b8fSAnthony Liguori 
remove_direntries(BDRVVVFATState * s,int dir_index,int count)2324019d6b8fSAnthony Liguori static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
2325019d6b8fSAnthony Liguori {
2326019d6b8fSAnthony Liguori     int ret = array_remove_slice(&(s->directory), dir_index, count);
2327019d6b8fSAnthony Liguori     if (ret)
2328019d6b8fSAnthony Liguori         return ret;
2329019d6b8fSAnthony Liguori     adjust_dirindices(s, dir_index, -count);
2330019d6b8fSAnthony Liguori     return 0;
2331019d6b8fSAnthony Liguori }
2332019d6b8fSAnthony Liguori 
2333019d6b8fSAnthony Liguori /*
2334019d6b8fSAnthony Liguori  * Adapt the mappings of the cluster chain starting at first cluster
2335019d6b8fSAnthony Liguori  * (i.e. if a file starts at first_cluster, the chain is followed according
2336019d6b8fSAnthony Liguori  * to the modified fat, and the corresponding entries in s->mapping are
2337019d6b8fSAnthony Liguori  * adjusted)
2338019d6b8fSAnthony Liguori  */
commit_mappings(BDRVVVFATState * s,uint32_t first_cluster,int dir_index)2339019d6b8fSAnthony Liguori static int commit_mappings(BDRVVVFATState* s,
2340019d6b8fSAnthony Liguori         uint32_t first_cluster, int dir_index)
2341019d6b8fSAnthony Liguori {
2342c227f099SAnthony Liguori     mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2343c227f099SAnthony Liguori     direntry_t* direntry = array_get(&(s->directory), dir_index);
2344019d6b8fSAnthony Liguori     uint32_t cluster = first_cluster;
2345019d6b8fSAnthony Liguori 
2346019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2347019d6b8fSAnthony Liguori 
2348019d6b8fSAnthony Liguori     assert(mapping);
2349019d6b8fSAnthony Liguori     assert(mapping->begin == first_cluster);
2350019d6b8fSAnthony Liguori     mapping->first_mapping_index = -1;
2351019d6b8fSAnthony Liguori     mapping->dir_index = dir_index;
2352019d6b8fSAnthony Liguori     mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
2353019d6b8fSAnthony Liguori         MODE_DIRECTORY : MODE_NORMAL;
2354019d6b8fSAnthony Liguori 
2355019d6b8fSAnthony Liguori     while (!fat_eof(s, cluster)) {
2356019d6b8fSAnthony Liguori         uint32_t c, c1;
2357019d6b8fSAnthony Liguori 
2358019d6b8fSAnthony Liguori         for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
2359019d6b8fSAnthony Liguori                 c = c1, c1 = modified_fat_get(s, c1));
2360019d6b8fSAnthony Liguori 
2361019d6b8fSAnthony Liguori         c++;
2362019d6b8fSAnthony Liguori         if (c > mapping->end) {
2363019d6b8fSAnthony Liguori             int index = array_index(&(s->mapping), mapping);
2364019d6b8fSAnthony Liguori             int i, max_i = s->mapping.next - index;
2365019d6b8fSAnthony Liguori             for (i = 1; i < max_i && mapping[i].begin < c; i++);
2366019d6b8fSAnthony Liguori             while (--i > 0)
2367019d6b8fSAnthony Liguori                 remove_mapping(s, index + 1);
2368019d6b8fSAnthony Liguori         }
2369019d6b8fSAnthony Liguori         assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
2370019d6b8fSAnthony Liguori                 || mapping[1].begin >= c);
2371019d6b8fSAnthony Liguori         mapping->end = c;
2372019d6b8fSAnthony Liguori 
2373019d6b8fSAnthony Liguori         if (!fat_eof(s, c1)) {
2374019d6b8fSAnthony Liguori             int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
2375c227f099SAnthony Liguori             mapping_t* next_mapping = i >= s->mapping.next ? NULL :
2376019d6b8fSAnthony Liguori                 array_get(&(s->mapping), i);
2377019d6b8fSAnthony Liguori 
2378019d6b8fSAnthony Liguori             if (next_mapping == NULL || next_mapping->begin > c1) {
2379019d6b8fSAnthony Liguori                 int i1 = array_index(&(s->mapping), mapping);
2380019d6b8fSAnthony Liguori 
2381019d6b8fSAnthony Liguori                 next_mapping = insert_mapping(s, c1, c1+1);
2382019d6b8fSAnthony Liguori 
2383019d6b8fSAnthony Liguori                 if (c1 < c)
2384019d6b8fSAnthony Liguori                     i1++;
2385019d6b8fSAnthony Liguori                 mapping = array_get(&(s->mapping), i1);
2386019d6b8fSAnthony Liguori             }
2387019d6b8fSAnthony Liguori 
2388019d6b8fSAnthony Liguori             next_mapping->dir_index = mapping->dir_index;
2389019d6b8fSAnthony Liguori             next_mapping->first_mapping_index =
2390019d6b8fSAnthony Liguori                 mapping->first_mapping_index < 0 ?
2391019d6b8fSAnthony Liguori                 array_index(&(s->mapping), mapping) :
2392019d6b8fSAnthony Liguori                 mapping->first_mapping_index;
2393019d6b8fSAnthony Liguori             next_mapping->path = mapping->path;
2394019d6b8fSAnthony Liguori             next_mapping->mode = mapping->mode;
2395019d6b8fSAnthony Liguori             next_mapping->read_only = mapping->read_only;
2396019d6b8fSAnthony Liguori             if (mapping->mode & MODE_DIRECTORY) {
2397019d6b8fSAnthony Liguori                 next_mapping->info.dir.parent_mapping_index =
2398019d6b8fSAnthony Liguori                         mapping->info.dir.parent_mapping_index;
2399019d6b8fSAnthony Liguori                 next_mapping->info.dir.first_dir_index =
2400019d6b8fSAnthony Liguori                         mapping->info.dir.first_dir_index +
2401019d6b8fSAnthony Liguori                         0x10 * s->sectors_per_cluster *
2402019d6b8fSAnthony Liguori                         (mapping->end - mapping->begin);
2403019d6b8fSAnthony Liguori             } else
2404019d6b8fSAnthony Liguori                 next_mapping->info.file.offset = mapping->info.file.offset +
240521b25a0eSAmjad Alsharafi                         (mapping->end - mapping->begin);
2406019d6b8fSAnthony Liguori 
2407019d6b8fSAnthony Liguori             mapping = next_mapping;
2408019d6b8fSAnthony Liguori         }
2409019d6b8fSAnthony Liguori 
2410019d6b8fSAnthony Liguori         cluster = c1;
2411019d6b8fSAnthony Liguori     }
2412019d6b8fSAnthony Liguori 
2413019d6b8fSAnthony Liguori     return 0;
2414019d6b8fSAnthony Liguori }
2415019d6b8fSAnthony Liguori 
2416eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
commit_direntries(BDRVVVFATState * s,int dir_index,int parent_mapping_index)2417eab76d58SPaolo Bonzini commit_direntries(BDRVVVFATState* s, int dir_index, int parent_mapping_index)
2418019d6b8fSAnthony Liguori {
2419c227f099SAnthony Liguori     direntry_t* direntry = array_get(&(s->directory), dir_index);
2420019d6b8fSAnthony Liguori     uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
2421c227f099SAnthony Liguori     mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
2422019d6b8fSAnthony Liguori     int factor = 0x10 * s->sectors_per_cluster;
2423019d6b8fSAnthony Liguori     int old_cluster_count, new_cluster_count;
24248d9401c2SLiam Merwick     int current_dir_index;
24258d9401c2SLiam Merwick     int first_dir_index;
2426019d6b8fSAnthony Liguori     int ret, i;
2427019d6b8fSAnthony Liguori     uint32_t c;
2428019d6b8fSAnthony Liguori 
2429019d6b8fSAnthony Liguori     assert(direntry);
2430019d6b8fSAnthony Liguori     assert(mapping);
2431019d6b8fSAnthony Liguori     assert(mapping->begin == first_cluster);
2432019d6b8fSAnthony Liguori     assert(mapping->info.dir.first_dir_index < s->directory.next);
2433019d6b8fSAnthony Liguori     assert(mapping->mode & MODE_DIRECTORY);
2434019d6b8fSAnthony Liguori     assert(dir_index == 0 || is_directory(direntry));
2435019d6b8fSAnthony Liguori 
24368d9401c2SLiam Merwick     DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n",
24378d9401c2SLiam Merwick                  mapping->path, parent_mapping_index));
24388d9401c2SLiam Merwick 
24398d9401c2SLiam Merwick     current_dir_index = mapping->info.dir.first_dir_index;
24408d9401c2SLiam Merwick     first_dir_index = current_dir_index;
2441019d6b8fSAnthony Liguori     mapping->info.dir.parent_mapping_index = parent_mapping_index;
2442019d6b8fSAnthony Liguori 
2443019d6b8fSAnthony Liguori     if (first_cluster == 0) {
2444019d6b8fSAnthony Liguori         old_cluster_count = new_cluster_count =
2445019d6b8fSAnthony Liguori             s->last_cluster_of_root_directory;
2446019d6b8fSAnthony Liguori     } else {
2447019d6b8fSAnthony Liguori         for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2448019d6b8fSAnthony Liguori                 c = fat_get(s, c))
2449019d6b8fSAnthony Liguori             old_cluster_count++;
2450019d6b8fSAnthony Liguori 
2451019d6b8fSAnthony Liguori         for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
2452019d6b8fSAnthony Liguori                 c = modified_fat_get(s, c))
2453019d6b8fSAnthony Liguori             new_cluster_count++;
2454019d6b8fSAnthony Liguori     }
2455019d6b8fSAnthony Liguori 
2456019d6b8fSAnthony Liguori     if (new_cluster_count > old_cluster_count) {
2457019d6b8fSAnthony Liguori         if (insert_direntries(s,
2458019d6b8fSAnthony Liguori                 current_dir_index + factor * old_cluster_count,
2459019d6b8fSAnthony Liguori                 factor * (new_cluster_count - old_cluster_count)) == NULL)
2460019d6b8fSAnthony Liguori             return -1;
2461019d6b8fSAnthony Liguori     } else if (new_cluster_count < old_cluster_count)
2462019d6b8fSAnthony Liguori         remove_direntries(s,
2463019d6b8fSAnthony Liguori                 current_dir_index + factor * new_cluster_count,
2464019d6b8fSAnthony Liguori                 factor * (old_cluster_count - new_cluster_count));
2465019d6b8fSAnthony Liguori 
2466019d6b8fSAnthony Liguori     for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
2467ebb72c9fSKevin Wolf         direntry_t *first_direntry;
2468fb2575f9SMarkus Armbruster 
2469fb2575f9SMarkus Armbruster         direntry = array_get(&(s->directory), current_dir_index);
2470fb2575f9SMarkus Armbruster         ret = vvfat_read(s->bs, cluster2sector(s, c), (uint8_t *)direntry,
2471019d6b8fSAnthony Liguori                 s->sectors_per_cluster);
2472019d6b8fSAnthony Liguori         if (ret)
2473019d6b8fSAnthony Liguori             return ret;
2474ebb72c9fSKevin Wolf 
2475ebb72c9fSKevin Wolf         /* The first directory entry on the filesystem is the volume name */
2476ebb72c9fSKevin Wolf         first_direntry = (direntry_t*) s->directory.pointer;
2477ebb72c9fSKevin Wolf         assert(!memcmp(first_direntry->name, s->volume_label, 11));
2478ebb72c9fSKevin Wolf 
2479019d6b8fSAnthony Liguori         current_dir_index += factor;
2480019d6b8fSAnthony Liguori     }
2481019d6b8fSAnthony Liguori 
2482019d6b8fSAnthony Liguori     ret = commit_mappings(s, first_cluster, dir_index);
2483019d6b8fSAnthony Liguori     if (ret)
2484019d6b8fSAnthony Liguori         return ret;
2485019d6b8fSAnthony Liguori 
2486019d6b8fSAnthony Liguori     /* recurse */
2487019d6b8fSAnthony Liguori     for (i = 0; i < factor * new_cluster_count; i++) {
2488019d6b8fSAnthony Liguori         direntry = array_get(&(s->directory), first_dir_index + i);
2489019d6b8fSAnthony Liguori         if (is_directory(direntry) && !is_dot(direntry)) {
2490019d6b8fSAnthony Liguori             mapping = find_mapping_for_cluster(s, first_cluster);
24918d9401c2SLiam Merwick             if (mapping == NULL) {
24928d9401c2SLiam Merwick                 return -1;
24938d9401c2SLiam Merwick             }
2494019d6b8fSAnthony Liguori             assert(mapping->mode & MODE_DIRECTORY);
2495019d6b8fSAnthony Liguori             ret = commit_direntries(s, first_dir_index + i,
2496019d6b8fSAnthony Liguori                 array_index(&(s->mapping), mapping));
2497019d6b8fSAnthony Liguori             if (ret)
2498019d6b8fSAnthony Liguori                 return ret;
2499019d6b8fSAnthony Liguori         }
2500019d6b8fSAnthony Liguori     }
2501019d6b8fSAnthony Liguori 
2502019d6b8fSAnthony Liguori     return 0;
2503019d6b8fSAnthony Liguori }
2504019d6b8fSAnthony Liguori 
2505019d6b8fSAnthony Liguori /* commit one file (adjust contents, adjust mapping),
2506019d6b8fSAnthony Liguori    return first_mapping_index */
2507eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
commit_one_file(BDRVVVFATState * s,int dir_index,uint32_t offset)2508eab76d58SPaolo Bonzini commit_one_file(BDRVVVFATState* s, int dir_index, uint32_t offset)
2509019d6b8fSAnthony Liguori {
2510c227f099SAnthony Liguori     direntry_t* direntry = array_get(&(s->directory), dir_index);
2511019d6b8fSAnthony Liguori     uint32_t c = begin_of_direntry(direntry);
2512019d6b8fSAnthony Liguori     uint32_t first_cluster = c;
2513c227f099SAnthony Liguori     mapping_t* mapping = find_mapping_for_cluster(s, c);
2514019d6b8fSAnthony Liguori     uint32_t size = filesize_of_direntry(direntry);
2515443ba6beSKevin Wolf     char *cluster;
2516019d6b8fSAnthony Liguori     uint32_t i;
2517019d6b8fSAnthony Liguori     int fd = 0;
2518019d6b8fSAnthony Liguori 
2519019d6b8fSAnthony Liguori     assert(offset < size);
2520019d6b8fSAnthony Liguori     assert((offset % s->cluster_size) == 0);
2521019d6b8fSAnthony Liguori 
25228d9401c2SLiam Merwick     if (mapping == NULL) {
25238d9401c2SLiam Merwick         return -1;
25248d9401c2SLiam Merwick     }
25258d9401c2SLiam Merwick 
2526b881cf00SAmjad Alsharafi     for (i = 0; i < offset; i += s->cluster_size) {
2527019d6b8fSAnthony Liguori         c = modified_fat_get(s, c);
2528b881cf00SAmjad Alsharafi     }
2529019d6b8fSAnthony Liguori 
2530448058aaSDaniel P. Berrangé     fd = qemu_open_old(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
2531019d6b8fSAnthony Liguori     if (fd < 0) {
2532019d6b8fSAnthony Liguori         fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
2533019d6b8fSAnthony Liguori                 strerror(errno), errno);
2534019d6b8fSAnthony Liguori         return fd;
2535019d6b8fSAnthony Liguori     }
2536ce137829SStefan Weil     if (offset > 0) {
2537ce137829SStefan Weil         if (lseek(fd, offset, SEEK_SET) != offset) {
25382e1e79daSCorey Bryant             qemu_close(fd);
2539019d6b8fSAnthony Liguori             return -3;
2540ce137829SStefan Weil         }
2541ce137829SStefan Weil     }
2542019d6b8fSAnthony Liguori 
2543443ba6beSKevin Wolf     cluster = g_malloc(s->cluster_size);
2544443ba6beSKevin Wolf 
2545019d6b8fSAnthony Liguori     while (offset < size) {
2546019d6b8fSAnthony Liguori         uint32_t c1;
2547019d6b8fSAnthony Liguori         int rest_size = (size - offset > s->cluster_size ?
2548019d6b8fSAnthony Liguori                 s->cluster_size : size - offset);
2549019d6b8fSAnthony Liguori         int ret;
2550019d6b8fSAnthony Liguori 
2551019d6b8fSAnthony Liguori         c1 = modified_fat_get(s, c);
2552019d6b8fSAnthony Liguori 
2553019d6b8fSAnthony Liguori         assert((size - offset == 0 && fat_eof(s, c)) ||
2554019d6b8fSAnthony Liguori                 (size > offset && c >=2 && !fat_eof(s, c)));
2555019d6b8fSAnthony Liguori 
2556019d6b8fSAnthony Liguori         ret = vvfat_read(s->bs, cluster2sector(s, c),
255778ee96deSMarc-André Lureau             (uint8_t*)cluster, DIV_ROUND_UP(rest_size, 0x200));
2558019d6b8fSAnthony Liguori 
2559ce137829SStefan Weil         if (ret < 0) {
25602e1e79daSCorey Bryant             qemu_close(fd);
2561ce137829SStefan Weil             g_free(cluster);
2562019d6b8fSAnthony Liguori             return ret;
2563ce137829SStefan Weil         }
2564019d6b8fSAnthony Liguori 
2565ce137829SStefan Weil         if (write(fd, cluster, rest_size) < 0) {
25662e1e79daSCorey Bryant             qemu_close(fd);
2567ce137829SStefan Weil             g_free(cluster);
2568019d6b8fSAnthony Liguori             return -2;
2569ce137829SStefan Weil         }
2570019d6b8fSAnthony Liguori 
2571019d6b8fSAnthony Liguori         offset += rest_size;
2572019d6b8fSAnthony Liguori         c = c1;
2573019d6b8fSAnthony Liguori     }
2574019d6b8fSAnthony Liguori 
25752dedf83eSKirill A. Shutemov     if (ftruncate(fd, size)) {
25762dedf83eSKirill A. Shutemov         perror("ftruncate()");
25772e1e79daSCorey Bryant         qemu_close(fd);
2578ce137829SStefan Weil         g_free(cluster);
25792dedf83eSKirill A. Shutemov         return -4;
25802dedf83eSKirill A. Shutemov     }
25812e1e79daSCorey Bryant     qemu_close(fd);
2582ce137829SStefan Weil     g_free(cluster);
2583019d6b8fSAnthony Liguori 
2584019d6b8fSAnthony Liguori     return commit_mappings(s, first_cluster, dir_index);
2585019d6b8fSAnthony Liguori }
2586019d6b8fSAnthony Liguori 
2587019d6b8fSAnthony Liguori #ifdef DEBUG
2588019d6b8fSAnthony Liguori /* test, if all mappings point to valid direntries */
check1(BDRVVVFATState * s)2589019d6b8fSAnthony Liguori static void check1(BDRVVVFATState* s)
2590019d6b8fSAnthony Liguori {
2591019d6b8fSAnthony Liguori     int i;
2592019d6b8fSAnthony Liguori     for (i = 0; i < s->mapping.next; i++) {
2593c227f099SAnthony Liguori         mapping_t* mapping = array_get(&(s->mapping), i);
2594019d6b8fSAnthony Liguori         if (mapping->mode & MODE_DELETED) {
2595019d6b8fSAnthony Liguori             fprintf(stderr, "deleted\n");
2596019d6b8fSAnthony Liguori             continue;
2597019d6b8fSAnthony Liguori         }
2598019d6b8fSAnthony Liguori         assert(mapping->dir_index < s->directory.next);
2599c227f099SAnthony Liguori         direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
2600019d6b8fSAnthony Liguori         assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
2601019d6b8fSAnthony Liguori         if (mapping->mode & MODE_DIRECTORY) {
2602019d6b8fSAnthony Liguori             assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
2603019d6b8fSAnthony Liguori             assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
2604019d6b8fSAnthony Liguori         }
2605019d6b8fSAnthony Liguori     }
2606019d6b8fSAnthony Liguori }
2607019d6b8fSAnthony Liguori 
2608019d6b8fSAnthony Liguori /* test, if all direntries have mappings */
check2(BDRVVVFATState * s)2609019d6b8fSAnthony Liguori static void check2(BDRVVVFATState* s)
2610019d6b8fSAnthony Liguori {
2611019d6b8fSAnthony Liguori     int i;
2612019d6b8fSAnthony Liguori     int first_mapping = -1;
2613019d6b8fSAnthony Liguori 
2614019d6b8fSAnthony Liguori     for (i = 0; i < s->directory.next; i++) {
2615c227f099SAnthony Liguori         direntry_t* direntry = array_get(&(s->directory), i);
2616019d6b8fSAnthony Liguori 
2617019d6b8fSAnthony Liguori         if (is_short_name(direntry) && begin_of_direntry(direntry)) {
2618c227f099SAnthony Liguori             mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
2619019d6b8fSAnthony Liguori             assert(mapping);
2620019d6b8fSAnthony Liguori             assert(mapping->dir_index == i || is_dot(direntry));
2621019d6b8fSAnthony Liguori             assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
2622019d6b8fSAnthony Liguori         }
2623019d6b8fSAnthony Liguori 
2624019d6b8fSAnthony Liguori         if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
2625019d6b8fSAnthony Liguori             /* cluster start */
2626019d6b8fSAnthony Liguori             int j, count = 0;
2627019d6b8fSAnthony Liguori 
2628019d6b8fSAnthony Liguori             for (j = 0; j < s->mapping.next; j++) {
2629c227f099SAnthony Liguori                 mapping_t* mapping = array_get(&(s->mapping), j);
2630019d6b8fSAnthony Liguori                 if (mapping->mode & MODE_DELETED)
2631019d6b8fSAnthony Liguori                     continue;
2632019d6b8fSAnthony Liguori                 if (mapping->mode & MODE_DIRECTORY) {
2633019d6b8fSAnthony Liguori                     if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
2634019d6b8fSAnthony Liguori                         assert(++count == 1);
2635019d6b8fSAnthony Liguori                         if (mapping->first_mapping_index == -1)
2636019d6b8fSAnthony Liguori                             first_mapping = array_index(&(s->mapping), mapping);
2637019d6b8fSAnthony Liguori                         else
2638019d6b8fSAnthony Liguori                             assert(first_mapping == mapping->first_mapping_index);
2639019d6b8fSAnthony Liguori                         if (mapping->info.dir.parent_mapping_index < 0)
2640019d6b8fSAnthony Liguori                             assert(j == 0);
2641019d6b8fSAnthony Liguori                         else {
2642c227f099SAnthony Liguori                             mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
2643019d6b8fSAnthony Liguori                             assert(parent->mode & MODE_DIRECTORY);
2644019d6b8fSAnthony Liguori                             assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
2645019d6b8fSAnthony Liguori                         }
2646019d6b8fSAnthony Liguori                     }
2647019d6b8fSAnthony Liguori                 }
2648019d6b8fSAnthony Liguori             }
2649019d6b8fSAnthony Liguori             if (count == 0)
2650019d6b8fSAnthony Liguori                 first_mapping = -1;
2651019d6b8fSAnthony Liguori         }
2652019d6b8fSAnthony Liguori     }
2653019d6b8fSAnthony Liguori }
2654019d6b8fSAnthony Liguori #endif
2655019d6b8fSAnthony Liguori 
handle_renames_and_mkdirs(BDRVVVFATState * s)2656019d6b8fSAnthony Liguori static int handle_renames_and_mkdirs(BDRVVVFATState* s)
2657019d6b8fSAnthony Liguori {
2658019d6b8fSAnthony Liguori     int i;
2659019d6b8fSAnthony Liguori 
2660019d6b8fSAnthony Liguori #ifdef DEBUG
2661019d6b8fSAnthony Liguori     fprintf(stderr, "handle_renames\n");
2662019d6b8fSAnthony Liguori     for (i = 0; i < s->commits.next; i++) {
2663c227f099SAnthony Liguori         commit_t* commit = array_get(&(s->commits), i);
2664c9eb2f3eSAlexChen         fprintf(stderr, "%d, %s (%u, %d)\n", i,
2665c9eb2f3eSAlexChen                 commit->path ? commit->path : "(null)",
2666c9eb2f3eSAlexChen                 commit->param.rename.cluster, commit->action);
2667019d6b8fSAnthony Liguori     }
2668019d6b8fSAnthony Liguori #endif
2669019d6b8fSAnthony Liguori 
2670019d6b8fSAnthony Liguori     for (i = 0; i < s->commits.next;) {
2671c227f099SAnthony Liguori         commit_t* commit = array_get(&(s->commits), i);
2672019d6b8fSAnthony Liguori         if (commit->action == ACTION_RENAME) {
2673c227f099SAnthony Liguori             mapping_t* mapping = find_mapping_for_cluster(s,
2674019d6b8fSAnthony Liguori                     commit->param.rename.cluster);
26758d9401c2SLiam Merwick             char *old_path;
2676019d6b8fSAnthony Liguori 
26778d9401c2SLiam Merwick             if (mapping == NULL) {
26788d9401c2SLiam Merwick                 return -1;
26798d9401c2SLiam Merwick             }
26808d9401c2SLiam Merwick             old_path = mapping->path;
2681019d6b8fSAnthony Liguori             assert(commit->path);
2682019d6b8fSAnthony Liguori             mapping->path = commit->path;
2683019d6b8fSAnthony Liguori             if (rename(old_path, mapping->path))
2684019d6b8fSAnthony Liguori                 return -2;
2685019d6b8fSAnthony Liguori 
2686019d6b8fSAnthony Liguori             if (mapping->mode & MODE_DIRECTORY) {
2687019d6b8fSAnthony Liguori                 int l1 = strlen(mapping->path);
2688019d6b8fSAnthony Liguori                 int l2 = strlen(old_path);
2689019d6b8fSAnthony Liguori                 int diff = l1 - l2;
2690c227f099SAnthony Liguori                 direntry_t* direntry = array_get(&(s->directory),
2691019d6b8fSAnthony Liguori                         mapping->info.dir.first_dir_index);
2692019d6b8fSAnthony Liguori                 uint32_t c = mapping->begin;
2693fb2575f9SMarkus Armbruster                 int j = 0;
2694019d6b8fSAnthony Liguori 
2695019d6b8fSAnthony Liguori                 /* recurse */
2696019d6b8fSAnthony Liguori                 while (!fat_eof(s, c)) {
2697019d6b8fSAnthony Liguori                     do {
2698fb2575f9SMarkus Armbruster                         direntry_t *d = direntry + j;
2699019d6b8fSAnthony Liguori 
2700019d6b8fSAnthony Liguori                         if (is_file(d) || (is_directory(d) && !is_dot(d))) {
27018d9401c2SLiam Merwick                             int l;
27028d9401c2SLiam Merwick                             char *new_path;
2703c227f099SAnthony Liguori                             mapping_t* m = find_mapping_for_cluster(s,
2704019d6b8fSAnthony Liguori                                     begin_of_direntry(d));
27058d9401c2SLiam Merwick                             if (m == NULL) {
27068d9401c2SLiam Merwick                                 return -1;
27078d9401c2SLiam Merwick                             }
27088d9401c2SLiam Merwick                             l = strlen(m->path);
27098d9401c2SLiam Merwick                             new_path = g_malloc(l + diff + 1);
2710019d6b8fSAnthony Liguori 
2711019d6b8fSAnthony Liguori                             assert(!strncmp(m->path, mapping->path, l2));
2712019d6b8fSAnthony Liguori 
2713019d6b8fSAnthony Liguori                             pstrcpy(new_path, l + diff + 1, mapping->path);
2714019d6b8fSAnthony Liguori                             pstrcpy(new_path + l1, l + diff + 1 - l1,
2715019d6b8fSAnthony Liguori                                     m->path + l2);
2716019d6b8fSAnthony Liguori 
2717019d6b8fSAnthony Liguori                             schedule_rename(s, m->begin, new_path);
2718019d6b8fSAnthony Liguori                         }
2719fb2575f9SMarkus Armbruster                         j++;
2720fb2575f9SMarkus Armbruster                     } while (j % (0x10 * s->sectors_per_cluster) != 0);
2721019d6b8fSAnthony Liguori                     c = fat_get(s, c);
2722019d6b8fSAnthony Liguori                 }
2723019d6b8fSAnthony Liguori             }
2724019d6b8fSAnthony Liguori 
2725ce137829SStefan Weil             g_free(old_path);
2726019d6b8fSAnthony Liguori             array_remove(&(s->commits), i);
2727019d6b8fSAnthony Liguori             continue;
2728019d6b8fSAnthony Liguori         } else if (commit->action == ACTION_MKDIR) {
2729c227f099SAnthony Liguori             mapping_t* mapping;
2730019d6b8fSAnthony Liguori             int j, parent_path_len;
2731019d6b8fSAnthony Liguori 
2732c2632994SBin Meng             if (g_mkdir(commit->path, 0755)) {
2733019d6b8fSAnthony Liguori                 return -5;
2734c2632994SBin Meng             }
2735019d6b8fSAnthony Liguori 
2736019d6b8fSAnthony Liguori             mapping = insert_mapping(s, commit->param.mkdir.cluster,
2737019d6b8fSAnthony Liguori                     commit->param.mkdir.cluster + 1);
2738019d6b8fSAnthony Liguori             if (mapping == NULL)
2739019d6b8fSAnthony Liguori                 return -6;
2740019d6b8fSAnthony Liguori 
2741019d6b8fSAnthony Liguori             mapping->mode = MODE_DIRECTORY;
2742019d6b8fSAnthony Liguori             mapping->read_only = 0;
2743019d6b8fSAnthony Liguori             mapping->path = commit->path;
2744019d6b8fSAnthony Liguori             j = s->directory.next;
2745019d6b8fSAnthony Liguori             assert(j);
2746019d6b8fSAnthony Liguori             insert_direntries(s, s->directory.next,
2747019d6b8fSAnthony Liguori                     0x10 * s->sectors_per_cluster);
2748019d6b8fSAnthony Liguori             mapping->info.dir.first_dir_index = j;
2749019d6b8fSAnthony Liguori 
2750019d6b8fSAnthony Liguori             parent_path_len = strlen(commit->path)
2751019d6b8fSAnthony Liguori                 - strlen(get_basename(commit->path)) - 1;
2752019d6b8fSAnthony Liguori             for (j = 0; j < s->mapping.next; j++) {
2753c227f099SAnthony Liguori                 mapping_t* m = array_get(&(s->mapping), j);
2754019d6b8fSAnthony Liguori                 if (m->first_mapping_index < 0 && m != mapping &&
2755019d6b8fSAnthony Liguori                         !strncmp(m->path, mapping->path, parent_path_len) &&
2756019d6b8fSAnthony Liguori                         strlen(m->path) == parent_path_len)
2757019d6b8fSAnthony Liguori                     break;
2758019d6b8fSAnthony Liguori             }
2759019d6b8fSAnthony Liguori             assert(j < s->mapping.next);
2760019d6b8fSAnthony Liguori             mapping->info.dir.parent_mapping_index = j;
2761019d6b8fSAnthony Liguori 
2762019d6b8fSAnthony Liguori             array_remove(&(s->commits), i);
2763019d6b8fSAnthony Liguori             continue;
2764019d6b8fSAnthony Liguori         }
2765019d6b8fSAnthony Liguori 
2766019d6b8fSAnthony Liguori         i++;
2767019d6b8fSAnthony Liguori     }
2768019d6b8fSAnthony Liguori     return 0;
2769019d6b8fSAnthony Liguori }
2770019d6b8fSAnthony Liguori 
2771019d6b8fSAnthony Liguori /*
2772019d6b8fSAnthony Liguori  * TODO: make sure that the short name is not matching *another* file
2773019d6b8fSAnthony Liguori  */
handle_commits(BDRVVVFATState * s)2774eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK handle_commits(BDRVVVFATState* s)
2775019d6b8fSAnthony Liguori {
2776019d6b8fSAnthony Liguori     int i, fail = 0;
2777019d6b8fSAnthony Liguori 
2778019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2779019d6b8fSAnthony Liguori 
2780019d6b8fSAnthony Liguori     for (i = 0; !fail && i < s->commits.next; i++) {
2781c227f099SAnthony Liguori         commit_t* commit = array_get(&(s->commits), i);
2782019d6b8fSAnthony Liguori         switch(commit->action) {
2783019d6b8fSAnthony Liguori         case ACTION_RENAME: case ACTION_MKDIR:
278443dc2a64SBlue Swirl             abort();
2785019d6b8fSAnthony Liguori             fail = -2;
2786019d6b8fSAnthony Liguori             break;
2787019d6b8fSAnthony Liguori         case ACTION_WRITEOUT: {
2788c227f099SAnthony Liguori             direntry_t* entry = array_get(&(s->directory),
2789019d6b8fSAnthony Liguori                     commit->param.writeout.dir_index);
2790019d6b8fSAnthony Liguori             uint32_t begin = begin_of_direntry(entry);
2791c227f099SAnthony Liguori             mapping_t* mapping = find_mapping_for_cluster(s, begin);
2792019d6b8fSAnthony Liguori 
2793019d6b8fSAnthony Liguori             assert(mapping);
2794019d6b8fSAnthony Liguori             assert(mapping->begin == begin);
2795019d6b8fSAnthony Liguori             assert(commit->path == NULL);
2796019d6b8fSAnthony Liguori 
2797019d6b8fSAnthony Liguori             if (commit_one_file(s, commit->param.writeout.dir_index,
2798019d6b8fSAnthony Liguori                         commit->param.writeout.modified_offset))
2799019d6b8fSAnthony Liguori                 fail = -3;
2800019d6b8fSAnthony Liguori 
2801019d6b8fSAnthony Liguori             break;
2802019d6b8fSAnthony Liguori         }
2803019d6b8fSAnthony Liguori         case ACTION_NEW_FILE: {
2804019d6b8fSAnthony Liguori             int begin = commit->param.new_file.first_cluster;
2805c227f099SAnthony Liguori             mapping_t* mapping = find_mapping_for_cluster(s, begin);
2806c227f099SAnthony Liguori             direntry_t* entry;
2807fb2575f9SMarkus Armbruster             int j;
2808019d6b8fSAnthony Liguori 
2809019d6b8fSAnthony Liguori             /* find direntry */
2810fb2575f9SMarkus Armbruster             for (j = 0; j < s->directory.next; j++) {
2811fb2575f9SMarkus Armbruster                 entry = array_get(&(s->directory), j);
2812019d6b8fSAnthony Liguori                 if (is_file(entry) && begin_of_direntry(entry) == begin)
2813019d6b8fSAnthony Liguori                     break;
2814019d6b8fSAnthony Liguori             }
2815019d6b8fSAnthony Liguori 
2816fb2575f9SMarkus Armbruster             if (j >= s->directory.next) {
2817019d6b8fSAnthony Liguori                 fail = -6;
2818019d6b8fSAnthony Liguori                 continue;
2819019d6b8fSAnthony Liguori             }
2820019d6b8fSAnthony Liguori 
2821019d6b8fSAnthony Liguori             /* make sure there exists an initial mapping */
2822019d6b8fSAnthony Liguori             if (mapping && mapping->begin != begin) {
2823019d6b8fSAnthony Liguori                 mapping->end = begin;
2824019d6b8fSAnthony Liguori                 mapping = NULL;
2825019d6b8fSAnthony Liguori             }
2826019d6b8fSAnthony Liguori             if (mapping == NULL) {
2827019d6b8fSAnthony Liguori                 mapping = insert_mapping(s, begin, begin+1);
2828019d6b8fSAnthony Liguori             }
2829019d6b8fSAnthony Liguori             /* most members will be fixed in commit_mappings() */
2830019d6b8fSAnthony Liguori             assert(commit->path);
2831019d6b8fSAnthony Liguori             mapping->path = commit->path;
2832019d6b8fSAnthony Liguori             mapping->read_only = 0;
2833019d6b8fSAnthony Liguori             mapping->mode = MODE_NORMAL;
2834019d6b8fSAnthony Liguori             mapping->info.file.offset = 0;
2835019d6b8fSAnthony Liguori 
2836fb2575f9SMarkus Armbruster             if (commit_one_file(s, j, 0)) {
2837019d6b8fSAnthony Liguori                 fail = -7;
2838fb2575f9SMarkus Armbruster             }
2839019d6b8fSAnthony Liguori 
2840019d6b8fSAnthony Liguori             break;
2841019d6b8fSAnthony Liguori         }
2842019d6b8fSAnthony Liguori         default:
284343dc2a64SBlue Swirl             abort();
2844019d6b8fSAnthony Liguori         }
2845019d6b8fSAnthony Liguori     }
2846019d6b8fSAnthony Liguori     if (i > 0 && array_remove_slice(&(s->commits), 0, i))
2847019d6b8fSAnthony Liguori         return -1;
2848019d6b8fSAnthony Liguori     return fail;
2849019d6b8fSAnthony Liguori }
2850019d6b8fSAnthony Liguori 
handle_deletes(BDRVVVFATState * s)2851019d6b8fSAnthony Liguori static int handle_deletes(BDRVVVFATState* s)
2852019d6b8fSAnthony Liguori {
2853019d6b8fSAnthony Liguori     int i, deferred = 1, deleted = 1;
2854019d6b8fSAnthony Liguori 
2855019d6b8fSAnthony Liguori     /* delete files corresponding to mappings marked as deleted */
2856019d6b8fSAnthony Liguori     /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
2857019d6b8fSAnthony Liguori     while (deferred && deleted) {
2858019d6b8fSAnthony Liguori         deferred = 0;
2859019d6b8fSAnthony Liguori         deleted = 0;
2860019d6b8fSAnthony Liguori 
2861019d6b8fSAnthony Liguori         for (i = 1; i < s->mapping.next; i++) {
2862c227f099SAnthony Liguori             mapping_t* mapping = array_get(&(s->mapping), i);
2863019d6b8fSAnthony Liguori             if (mapping->mode & MODE_DELETED) {
2864c227f099SAnthony Liguori                 direntry_t* entry = array_get(&(s->directory),
2865019d6b8fSAnthony Liguori                         mapping->dir_index);
2866019d6b8fSAnthony Liguori 
2867019d6b8fSAnthony Liguori                 if (is_free(entry)) {
2868019d6b8fSAnthony Liguori                     /* remove file/directory */
2869019d6b8fSAnthony Liguori                     if (mapping->mode & MODE_DIRECTORY) {
2870019d6b8fSAnthony Liguori                         int j, next_dir_index = s->directory.next,
2871019d6b8fSAnthony Liguori                         first_dir_index = mapping->info.dir.first_dir_index;
2872019d6b8fSAnthony Liguori 
2873019d6b8fSAnthony Liguori                         if (rmdir(mapping->path) < 0) {
2874019d6b8fSAnthony Liguori                             if (errno == ENOTEMPTY) {
2875019d6b8fSAnthony Liguori                                 deferred++;
2876019d6b8fSAnthony Liguori                                 continue;
2877019d6b8fSAnthony Liguori                             } else
2878019d6b8fSAnthony Liguori                                 return -5;
2879019d6b8fSAnthony Liguori                         }
2880019d6b8fSAnthony Liguori 
2881019d6b8fSAnthony Liguori                         for (j = 1; j < s->mapping.next; j++) {
2882c227f099SAnthony Liguori                             mapping_t* m = array_get(&(s->mapping), j);
2883019d6b8fSAnthony Liguori                             if (m->mode & MODE_DIRECTORY &&
2884019d6b8fSAnthony Liguori                                     m->info.dir.first_dir_index >
2885019d6b8fSAnthony Liguori                                     first_dir_index &&
2886019d6b8fSAnthony Liguori                                     m->info.dir.first_dir_index <
2887019d6b8fSAnthony Liguori                                     next_dir_index)
2888019d6b8fSAnthony Liguori                                 next_dir_index =
2889019d6b8fSAnthony Liguori                                     m->info.dir.first_dir_index;
2890019d6b8fSAnthony Liguori                         }
2891019d6b8fSAnthony Liguori                         remove_direntries(s, first_dir_index,
2892019d6b8fSAnthony Liguori                                 next_dir_index - first_dir_index);
2893019d6b8fSAnthony Liguori 
2894019d6b8fSAnthony Liguori                         deleted++;
2895019d6b8fSAnthony Liguori                     }
2896019d6b8fSAnthony Liguori                 } else {
2897019d6b8fSAnthony Liguori                     if (unlink(mapping->path))
2898019d6b8fSAnthony Liguori                         return -4;
2899019d6b8fSAnthony Liguori                     deleted++;
2900019d6b8fSAnthony Liguori                 }
2901019d6b8fSAnthony Liguori                 DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
2902019d6b8fSAnthony Liguori                 remove_mapping(s, i);
2903019d6b8fSAnthony Liguori             }
2904019d6b8fSAnthony Liguori         }
2905019d6b8fSAnthony Liguori     }
2906019d6b8fSAnthony Liguori 
2907019d6b8fSAnthony Liguori     return 0;
2908019d6b8fSAnthony Liguori }
2909019d6b8fSAnthony Liguori 
2910019d6b8fSAnthony Liguori /*
2911019d6b8fSAnthony Liguori  * synchronize mapping with new state:
2912019d6b8fSAnthony Liguori  *
2913f4649069SEric Blake  * - copy FAT (with bdrv_pread)
2914019d6b8fSAnthony Liguori  * - mark all filenames corresponding to mappings as deleted
2915f4649069SEric Blake  * - recurse direntries from root (using bs->bdrv_pread)
2916019d6b8fSAnthony Liguori  * - delete files corresponding to mappings marked as deleted
2917019d6b8fSAnthony Liguori  */
do_commit(BDRVVVFATState * s)2918eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK do_commit(BDRVVVFATState* s)
2919019d6b8fSAnthony Liguori {
2920019d6b8fSAnthony Liguori     int ret = 0;
2921019d6b8fSAnthony Liguori 
2922019d6b8fSAnthony Liguori     /* the real meat are the commits. Nothing to do? Move along! */
2923019d6b8fSAnthony Liguori     if (s->commits.next == 0)
2924019d6b8fSAnthony Liguori         return 0;
2925019d6b8fSAnthony Liguori 
2926019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2927019d6b8fSAnthony Liguori 
2928019d6b8fSAnthony Liguori     ret = handle_renames_and_mkdirs(s);
2929019d6b8fSAnthony Liguori     if (ret) {
2930019d6b8fSAnthony Liguori         fprintf(stderr, "Error handling renames (%d)\n", ret);
293143dc2a64SBlue Swirl         abort();
2932019d6b8fSAnthony Liguori         return ret;
2933019d6b8fSAnthony Liguori     }
2934019d6b8fSAnthony Liguori 
2935f4649069SEric Blake     /* copy FAT (with bdrv_pread) */
2936019d6b8fSAnthony Liguori     memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
2937019d6b8fSAnthony Liguori 
2938f4649069SEric Blake     /* recurse direntries from root (using bs->bdrv_pread) */
2939019d6b8fSAnthony Liguori     ret = commit_direntries(s, 0, -1);
2940019d6b8fSAnthony Liguori     if (ret) {
2941019d6b8fSAnthony Liguori         fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
294243dc2a64SBlue Swirl         abort();
2943019d6b8fSAnthony Liguori         return ret;
2944019d6b8fSAnthony Liguori     }
2945019d6b8fSAnthony Liguori 
2946019d6b8fSAnthony Liguori     ret = handle_commits(s);
2947019d6b8fSAnthony Liguori     if (ret) {
2948019d6b8fSAnthony Liguori         fprintf(stderr, "Error handling commits (%d)\n", ret);
294943dc2a64SBlue Swirl         abort();
2950019d6b8fSAnthony Liguori         return ret;
2951019d6b8fSAnthony Liguori     }
2952019d6b8fSAnthony Liguori 
2953019d6b8fSAnthony Liguori     ret = handle_deletes(s);
2954019d6b8fSAnthony Liguori     if (ret) {
2955019d6b8fSAnthony Liguori         fprintf(stderr, "Error deleting\n");
295643dc2a64SBlue Swirl         abort();
2957019d6b8fSAnthony Liguori         return ret;
2958019d6b8fSAnthony Liguori     }
2959019d6b8fSAnthony Liguori 
2960f844ec01SMax Reitz     bdrv_make_empty(s->qcow, NULL);
2961019d6b8fSAnthony Liguori 
2962019d6b8fSAnthony Liguori     memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
2963019d6b8fSAnthony Liguori 
2964019d6b8fSAnthony Liguori DLOG(checkpoint());
2965019d6b8fSAnthony Liguori     return 0;
2966019d6b8fSAnthony Liguori }
2967019d6b8fSAnthony Liguori 
try_commit(BDRVVVFATState * s)2968eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK try_commit(BDRVVVFATState* s)
2969019d6b8fSAnthony Liguori {
2970019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2971019d6b8fSAnthony Liguori DLOG(checkpoint());
2972019d6b8fSAnthony Liguori     if(!is_consistent(s))
2973019d6b8fSAnthony Liguori         return -1;
2974019d6b8fSAnthony Liguori     return do_commit(s);
2975019d6b8fSAnthony Liguori }
2976019d6b8fSAnthony Liguori 
2977eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
vvfat_write(BlockDriverState * bs,int64_t sector_num,const uint8_t * buf,int nb_sectors)2978eab76d58SPaolo Bonzini vvfat_write(BlockDriverState *bs, int64_t sector_num,
2979019d6b8fSAnthony Liguori             const uint8_t *buf, int nb_sectors)
2980019d6b8fSAnthony Liguori {
2981019d6b8fSAnthony Liguori     BDRVVVFATState *s = bs->opaque;
2982019d6b8fSAnthony Liguori     int i, ret;
2983b9b8860dSKevin Wolf     int first_cluster, last_cluster;
2984019d6b8fSAnthony Liguori 
2985019d6b8fSAnthony Liguori DLOG(checkpoint());
2986019d6b8fSAnthony Liguori 
2987ac48e389SKevin Wolf     /* Check if we're operating in read-only mode */
2988ac48e389SKevin Wolf     if (s->qcow == NULL) {
2989ac48e389SKevin Wolf         return -EACCES;
2990ac48e389SKevin Wolf     }
2991ac48e389SKevin Wolf 
2992019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
2993019d6b8fSAnthony Liguori 
2994d0f95b6cSHervé Poussineau     if (sector_num == s->offset_to_bootsector && nb_sectors == 1) {
2995d0f95b6cSHervé Poussineau         /*
2996d0f95b6cSHervé Poussineau          * Write on bootsector. Allow only changing the reserved1 field,
2997d0f95b6cSHervé Poussineau          * used to mark volume dirtiness
2998d0f95b6cSHervé Poussineau          */
2999d0f95b6cSHervé Poussineau         unsigned char *bootsector = s->first_sectors
3000d0f95b6cSHervé Poussineau                                     + s->offset_to_bootsector * 0x200;
3001d0f95b6cSHervé Poussineau         /*
3002d0f95b6cSHervé Poussineau          * LATER TODO: if FAT32, this is wrong (see init_directories(),
3003d0f95b6cSHervé Poussineau          * which always creates a FAT16 bootsector)
3004d0f95b6cSHervé Poussineau          */
3005d0f95b6cSHervé Poussineau         const int reserved1_offset = offsetof(bootsector_t, u.fat16.reserved1);
3006d0f95b6cSHervé Poussineau 
3007d0f95b6cSHervé Poussineau         for (i = 0; i < 0x200; i++) {
3008d0f95b6cSHervé Poussineau             if (i != reserved1_offset && bootsector[i] != buf[i]) {
3009d0f95b6cSHervé Poussineau                 fprintf(stderr, "Tried to write to protected bootsector\n");
3010d0f95b6cSHervé Poussineau                 return -1;
3011d0f95b6cSHervé Poussineau             }
3012d0f95b6cSHervé Poussineau         }
3013d0f95b6cSHervé Poussineau 
3014d0f95b6cSHervé Poussineau         /* Update bootsector with the only updatable byte, and return success */
3015d0f95b6cSHervé Poussineau         bootsector[reserved1_offset] = buf[reserved1_offset];
3016d0f95b6cSHervé Poussineau         return 0;
3017d0f95b6cSHervé Poussineau     }
3018d0f95b6cSHervé Poussineau 
3019019d6b8fSAnthony Liguori     /*
3020019d6b8fSAnthony Liguori      * Some sanity checks:
3021019d6b8fSAnthony Liguori      * - do not allow writing to the boot sector
3022019d6b8fSAnthony Liguori      */
30234dc705dcSHervé Poussineau     if (sector_num < s->offset_to_fat)
3024019d6b8fSAnthony Liguori         return -1;
3025019d6b8fSAnthony Liguori 
3026b9b8860dSKevin Wolf     /*
3027b9b8860dSKevin Wolf      * Values will be negative for writes to the FAT, which is located before
3028b9b8860dSKevin Wolf      * the root directory.
3029b9b8860dSKevin Wolf      */
3030b9b8860dSKevin Wolf     first_cluster = sector2cluster(s, sector_num);
3031b9b8860dSKevin Wolf     last_cluster = sector2cluster(s, sector_num + nb_sectors - 1);
3032b9b8860dSKevin Wolf 
3033b9b8860dSKevin Wolf     for (i = first_cluster; i <= last_cluster;) {
3034b9b8860dSKevin Wolf         mapping_t *mapping = NULL;
3035b9b8860dSKevin Wolf 
3036b9b8860dSKevin Wolf         if (i >= 0) {
3037b9b8860dSKevin Wolf             mapping = find_mapping_for_cluster(s, i);
3038b9b8860dSKevin Wolf         }
3039b9b8860dSKevin Wolf 
3040019d6b8fSAnthony Liguori         if (mapping) {
3041019d6b8fSAnthony Liguori             if (mapping->read_only) {
3042019d6b8fSAnthony Liguori                 fprintf(stderr, "Tried to write to write-protected file %s\n",
3043019d6b8fSAnthony Liguori                         mapping->path);
3044019d6b8fSAnthony Liguori                 return -1;
3045019d6b8fSAnthony Liguori             }
3046019d6b8fSAnthony Liguori 
3047019d6b8fSAnthony Liguori             if (mapping->mode & MODE_DIRECTORY) {
3048019d6b8fSAnthony Liguori                 int begin = cluster2sector(s, i);
3049019d6b8fSAnthony Liguori                 int end = begin + s->sectors_per_cluster, k;
3050019d6b8fSAnthony Liguori                 int dir_index;
3051c227f099SAnthony Liguori                 const direntry_t* direntries;
3052019d6b8fSAnthony Liguori                 long_file_name lfn;
3053019d6b8fSAnthony Liguori 
3054019d6b8fSAnthony Liguori                 lfn_init(&lfn);
3055019d6b8fSAnthony Liguori 
3056019d6b8fSAnthony Liguori                 if (begin < sector_num)
3057019d6b8fSAnthony Liguori                     begin = sector_num;
3058019d6b8fSAnthony Liguori                 if (end > sector_num + nb_sectors)
3059019d6b8fSAnthony Liguori                     end = sector_num + nb_sectors;
3060019d6b8fSAnthony Liguori                 dir_index  = mapping->dir_index +
3061019d6b8fSAnthony Liguori                     0x10 * (begin - mapping->begin * s->sectors_per_cluster);
3062c227f099SAnthony Liguori                 direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
3063019d6b8fSAnthony Liguori 
3064019d6b8fSAnthony Liguori                 for (k = 0; k < (end - begin) * 0x10; k++) {
3065019d6b8fSAnthony Liguori                     /* no access to the direntry of a read-only file */
3066e03da26bSHervé Poussineau                     if (is_short_name(direntries + k) &&
3067019d6b8fSAnthony Liguori                             (direntries[k].attributes & 1)) {
3068019d6b8fSAnthony Liguori                         if (memcmp(direntries + k,
3069019d6b8fSAnthony Liguori                                     array_get(&(s->directory), dir_index + k),
3070c227f099SAnthony Liguori                                     sizeof(direntry_t))) {
30712ab4b135SAlistair Francis                             warn_report("tried to write to write-protected "
30722ab4b135SAlistair Francis                                         "file");
3073019d6b8fSAnthony Liguori                             return -1;
3074019d6b8fSAnthony Liguori                         }
3075019d6b8fSAnthony Liguori                     }
3076019d6b8fSAnthony Liguori                 }
3077019d6b8fSAnthony Liguori             }
3078019d6b8fSAnthony Liguori             i = mapping->end;
3079b9b8860dSKevin Wolf         } else {
3080019d6b8fSAnthony Liguori             i++;
3081019d6b8fSAnthony Liguori         }
3082b9b8860dSKevin Wolf     }
3083019d6b8fSAnthony Liguori 
3084019d6b8fSAnthony Liguori     /*
3085019d6b8fSAnthony Liguori      * Use qcow backend. Commit later.
3086019d6b8fSAnthony Liguori      */
3087019d6b8fSAnthony Liguori DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
3088eab76d58SPaolo Bonzini     ret = bdrv_co_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE,
308932cc71deSAlberto Faria                          nb_sectors * BDRV_SECTOR_SIZE, buf, 0);
3090019d6b8fSAnthony Liguori     if (ret < 0) {
3091019d6b8fSAnthony Liguori         fprintf(stderr, "Error writing to qcow backend\n");
3092019d6b8fSAnthony Liguori         return ret;
3093019d6b8fSAnthony Liguori     }
3094019d6b8fSAnthony Liguori 
3095b9b8860dSKevin Wolf     for (i = first_cluster; i <= last_cluster; i++) {
3096b9b8860dSKevin Wolf         if (i >= 0) {
3097019d6b8fSAnthony Liguori             s->used_clusters[i] |= USED_ALLOCATED;
3098b9b8860dSKevin Wolf         }
3099b9b8860dSKevin Wolf     }
3100019d6b8fSAnthony Liguori 
3101019d6b8fSAnthony Liguori DLOG(checkpoint());
3102019d6b8fSAnthony Liguori     /* TODO: add timeout */
3103019d6b8fSAnthony Liguori     try_commit(s);
3104019d6b8fSAnthony Liguori 
3105019d6b8fSAnthony Liguori DLOG(checkpoint());
3106019d6b8fSAnthony Liguori     return 0;
3107019d6b8fSAnthony Liguori }
3108019d6b8fSAnthony Liguori 
3109eab76d58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
vvfat_co_pwritev(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)3110e75abedaSVladimir Sementsov-Ogievskiy vvfat_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
3111e75abedaSVladimir Sementsov-Ogievskiy                  QEMUIOVector *qiov, BdrvRequestFlags flags)
3112e183ef75SPaolo Bonzini {
3113e183ef75SPaolo Bonzini     int ret;
3114e183ef75SPaolo Bonzini     BDRVVVFATState *s = bs->opaque;
31154575eb49SKevin Wolf     uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
31164575eb49SKevin Wolf     int nb_sectors = bytes >> BDRV_SECTOR_BITS;
31174575eb49SKevin Wolf     void *buf;
31184575eb49SKevin Wolf 
31191bbbf32dSNir Soffer     assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
31201bbbf32dSNir Soffer     assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
31214575eb49SKevin Wolf 
31224575eb49SKevin Wolf     buf = g_try_malloc(bytes);
31234575eb49SKevin Wolf     if (bytes && buf == NULL) {
31244575eb49SKevin Wolf         return -ENOMEM;
31254575eb49SKevin Wolf     }
31264575eb49SKevin Wolf     qemu_iovec_to_buf(qiov, 0, buf, bytes);
31274575eb49SKevin Wolf 
3128e183ef75SPaolo Bonzini     qemu_co_mutex_lock(&s->lock);
3129e183ef75SPaolo Bonzini     ret = vvfat_write(bs, sector_num, buf, nb_sectors);
3130e183ef75SPaolo Bonzini     qemu_co_mutex_unlock(&s->lock);
31314575eb49SKevin Wolf 
31324575eb49SKevin Wolf     g_free(buf);
31334575eb49SKevin Wolf 
3134e183ef75SPaolo Bonzini     return ret;
3135e183ef75SPaolo Bonzini }
3136e183ef75SPaolo Bonzini 
vvfat_co_block_status(BlockDriverState * bs,bool want_zero,int64_t offset,int64_t bytes,int64_t * n,int64_t * map,BlockDriverState ** file)3137fba3998dSEric Blake static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs,
3138fba3998dSEric Blake                                               bool want_zero, int64_t offset,
3139fba3998dSEric Blake                                               int64_t bytes, int64_t *n,
3140fba3998dSEric Blake                                               int64_t *map,
3141fba3998dSEric Blake                                               BlockDriverState **file)
3142019d6b8fSAnthony Liguori {
3143fba3998dSEric Blake     *n = bytes;
31444bc74be9SPaolo Bonzini     return BDRV_BLOCK_DATA;
3145019d6b8fSAnthony Liguori }
3146019d6b8fSAnthony Liguori 
vvfat_qcow_options(BdrvChildRole role,bool parent_is_format,int * child_flags,QDict * child_options,int parent_flags,QDict * parent_options)31473cdc69d3SMax Reitz static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format,
3148272c02eaSMax Reitz                                int *child_flags, QDict *child_options,
3149eecc7747SKevin Wolf                                int parent_flags, QDict *parent_options)
3150019d6b8fSAnthony Liguori {
3151f87a0e29SAlberto Garcia     qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off");
3152e35bdc12SKevin Wolf     qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off");
31534f8e3a1fSFam Zheng     qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
3154eecc7747SKevin Wolf }
3155eecc7747SKevin Wolf 
31568081f064SVladimir Sementsov-Ogievskiy static BdrvChildClass child_vvfat_qcow;
3157eecc7747SKevin Wolf 
enable_write_target(BlockDriverState * bs,Error ** errp)3158eecc7747SKevin Wolf static int enable_write_target(BlockDriverState *bs, Error **errp)
3159eecc7747SKevin Wolf {
3160eecc7747SKevin Wolf     BDRVVVFATState *s = bs->opaque;
3161facdbb02SChunyan Liu     BlockDriver *bdrv_qcow = NULL;
3162facdbb02SChunyan Liu     QemuOpts *opts = NULL;
3163a655211aSKevin Wolf     int ret;
3164019d6b8fSAnthony Liguori     int size = sector2cluster(s, s->sector_count);
3165e6641719SMax Reitz     QDict *options;
3166e6641719SMax Reitz 
316722c36b75SDaniella Lee     s->used_clusters = g_malloc0(size);
3168019d6b8fSAnthony Liguori 
3169c227f099SAnthony Liguori     array_init(&(s->commits), sizeof(commit_t));
3170019d6b8fSAnthony Liguori 
317169fbfff9SBin Meng     s->qcow_filename = create_tmp_file(errp);
317269fbfff9SBin Meng     if (!s->qcow_filename) {
317369fbfff9SBin Meng         ret = -ENOENT;
317478f27bd0SFam Zheng         goto err;
3175eba25057SJim Meyering     }
317691a073a9SKevin Wolf 
317791a073a9SKevin Wolf     bdrv_qcow = bdrv_find_format("qcow");
31781bcb15cfSMax Reitz     if (!bdrv_qcow) {
31791bcb15cfSMax Reitz         error_setg(errp, "Failed to locate qcow driver");
31801bcb15cfSMax Reitz         ret = -ENOENT;
31811bcb15cfSMax Reitz         goto err;
31821bcb15cfSMax Reitz     }
31831bcb15cfSMax Reitz 
3184c282e1fdSChunyan Liu     opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort);
31852db9b9e9SKevin Wolf     qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
31862db9b9e9SKevin Wolf                         bs->total_sectors * BDRV_SECTOR_SIZE, &error_abort);
3187f43e47dbSMarkus Armbruster     qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort);
318891a073a9SKevin Wolf 
3189c282e1fdSChunyan Liu     ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
3190facdbb02SChunyan Liu     qemu_opts_del(opts);
319178f27bd0SFam Zheng     if (ret < 0) {
319278f27bd0SFam Zheng         goto err;
319378f27bd0SFam Zheng     }
3194a655211aSKevin Wolf 
3195e6641719SMax Reitz     options = qdict_new();
319646f5ac20SEric Blake     qdict_put_str(options, "write-target.driver", "qcow");
3197eecc7747SKevin Wolf     s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs,
31981f38f04eSMax Reitz                               &child_vvfat_qcow,
31991f38f04eSMax Reitz                               BDRV_CHILD_DATA | BDRV_CHILD_METADATA,
32001f38f04eSMax Reitz                               false, errp);
3201cb3e7f08SMarc-André Lureau     qobject_unref(options);
32025b363937SMax Reitz     if (!s->qcow) {
32035b363937SMax Reitz         ret = -EINVAL;
320478f27bd0SFam Zheng         goto err;
3205a655211aSKevin Wolf     }
3206a655211aSKevin Wolf 
3207019d6b8fSAnthony Liguori #ifndef _WIN32
3208019d6b8fSAnthony Liguori     unlink(s->qcow_filename);
3209019d6b8fSAnthony Liguori #endif
3210019d6b8fSAnthony Liguori 
3211019d6b8fSAnthony Liguori     return 0;
321278f27bd0SFam Zheng 
321378f27bd0SFam Zheng err:
321478f27bd0SFam Zheng     return ret;
3215019d6b8fSAnthony Liguori }
3216019d6b8fSAnthony 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)321791ef3825SKevin Wolf static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
3218bf8e925eSMax Reitz                              BdrvChildRole role,
3219e0995dc3SKevin Wolf                              BlockReopenQueue *reopen_queue,
322091ef3825SKevin Wolf                              uint64_t perm, uint64_t shared,
322191ef3825SKevin Wolf                              uint64_t *nperm, uint64_t *nshared)
322291ef3825SKevin Wolf {
32236af72274SVladimir Sementsov-Ogievskiy     assert(role & BDRV_CHILD_DATA);
322491ef3825SKevin Wolf     /* This is a private node, nobody should try to attach to it */
322591ef3825SKevin Wolf     *nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
322691ef3825SKevin Wolf     *nshared = BLK_PERM_WRITE_UNCHANGED;
322791ef3825SKevin Wolf }
322891ef3825SKevin Wolf 
vvfat_close(BlockDriverState * bs)3229019d6b8fSAnthony Liguori static void vvfat_close(BlockDriverState *bs)
3230019d6b8fSAnthony Liguori {
3231019d6b8fSAnthony Liguori     BDRVVVFATState *s = bs->opaque;
3232019d6b8fSAnthony Liguori 
3233019d6b8fSAnthony Liguori     vvfat_close_current_file(s);
3234019d6b8fSAnthony Liguori     array_free(&(s->fat));
3235019d6b8fSAnthony Liguori     array_free(&(s->directory));
3236019d6b8fSAnthony Liguori     array_free(&(s->mapping));
3237ce137829SStefan Weil     g_free(s->cluster_buffer);
32383397f0cbSKevin Wolf 
32393397f0cbSKevin Wolf     if (s->qcow) {
3240c8a7fc51SSteve Sistare         migrate_del_blocker(&s->migration_blocker);
32413397f0cbSKevin Wolf     }
3242019d6b8fSAnthony Liguori }
3243019d6b8fSAnthony Liguori 
32442654267cSMax Reitz static const char *const vvfat_strong_runtime_opts[] = {
32452654267cSMax Reitz     "dir",
32462654267cSMax Reitz     "fat-type",
32472654267cSMax Reitz     "floppy",
32482654267cSMax Reitz     "label",
32492654267cSMax Reitz     "rw",
32502654267cSMax Reitz 
32512654267cSMax Reitz     NULL
32522654267cSMax Reitz };
32532654267cSMax Reitz 
3254019d6b8fSAnthony Liguori static BlockDriver bdrv_vvfat = {
3255019d6b8fSAnthony Liguori     .format_name            = "vvfat",
32567ad9be64SKevin Wolf     .protocol_name          = "fat",
3257019d6b8fSAnthony Liguori     .instance_size          = sizeof(BDRVVVFATState),
32587ad9be64SKevin Wolf 
32597ad9be64SKevin Wolf     .bdrv_parse_filename    = vvfat_parse_filename,
326044b424dcSPaolo Bonzini     .bdrv_open              = vvfat_open,
3261a6506481SEric Blake     .bdrv_refresh_limits    = vvfat_refresh_limits,
32627ad9be64SKevin Wolf     .bdrv_close             = vvfat_close,
326391ef3825SKevin Wolf     .bdrv_child_perm        = vvfat_child_perm,
32647ad9be64SKevin Wolf 
32654575eb49SKevin Wolf     .bdrv_co_preadv         = vvfat_co_preadv,
32664575eb49SKevin Wolf     .bdrv_co_pwritev        = vvfat_co_pwritev,
3267fba3998dSEric Blake     .bdrv_co_block_status   = vvfat_co_block_status,
32682654267cSMax Reitz 
32692654267cSMax Reitz     .strong_runtime_opts    = vvfat_strong_runtime_opts,
3270019d6b8fSAnthony Liguori };
3271019d6b8fSAnthony Liguori 
bdrv_vvfat_init(void)3272019d6b8fSAnthony Liguori static void bdrv_vvfat_init(void)
3273019d6b8fSAnthony Liguori {
32748081f064SVladimir Sementsov-Ogievskiy     child_vvfat_qcow = child_of_bds;
32758081f064SVladimir Sementsov-Ogievskiy     child_vvfat_qcow.inherit_options = vvfat_qcow_options;
3276019d6b8fSAnthony Liguori     bdrv_register(&bdrv_vvfat);
3277019d6b8fSAnthony Liguori }
3278019d6b8fSAnthony Liguori 
3279019d6b8fSAnthony Liguori block_init(bdrv_vvfat_init);
3280019d6b8fSAnthony Liguori 
3281019d6b8fSAnthony Liguori #ifdef DEBUG
checkpoint(void)32827a6ab45eSThomas Huth static void checkpoint(void)
32837a6ab45eSThomas Huth {
3284c227f099SAnthony Liguori     assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
3285019d6b8fSAnthony Liguori     check1(vvv);
3286019d6b8fSAnthony Liguori     check2(vvv);
3287019d6b8fSAnthony Liguori     assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
3288019d6b8fSAnthony Liguori }
3289019d6b8fSAnthony Liguori #endif
3290