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