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