diff options
Diffstat (limited to 'firmware/drivers')
-rw-r--r-- | firmware/drivers/fat.c | 4206 |
1 files changed, 2230 insertions, 1976 deletions
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c index fb75355898..44e5ab2f4c 100644 --- a/firmware/drivers/fat.c +++ b/firmware/drivers/fat.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2002 by Linus Nielsen Feltzing | 10 | * Copyright (C) 2002 by Linus Nielsen Feltzing |
11 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | 12 | * |
12 | * This program is free software; you can redistribute it and/or | 13 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 14 | * modify it under the terms of the GNU General Public License |
@@ -18,27 +19,44 @@ | |||
18 | * KIND, either express or implied. | 19 | * KIND, either express or implied. |
19 | * | 20 | * |
20 | ****************************************************************************/ | 21 | ****************************************************************************/ |
21 | #include <stdio.h> | 22 | #include "config.h" |
23 | #include "system.h" | ||
24 | #include "sys/types.h" | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | 26 | #include <ctype.h> |
25 | #include <stdbool.h> | 27 | #include <stdlib.h> |
26 | #include "fat.h" | 28 | #include <stdio.h> |
29 | #include "fs_attr.h" | ||
30 | #include "pathfuncs.h" | ||
31 | #include "disk_cache.h" | ||
32 | #include "file_internal.h" /* for struct filestr_cache */ | ||
27 | #include "storage.h" | 33 | #include "storage.h" |
28 | #include "debug.h" | ||
29 | #include "panic.h" | ||
30 | #include "system.h" | ||
31 | #include "timefuncs.h" | 34 | #include "timefuncs.h" |
32 | #include "kernel.h" | ||
33 | #include "rbunicode.h" | 35 | #include "rbunicode.h" |
36 | #include "debug.h" | ||
37 | #include "panic.h" | ||
34 | /*#define LOGF_ENABLE*/ | 38 | /*#define LOGF_ENABLE*/ |
35 | #include "logf.h" | 39 | #include "logf.h" |
36 | 40 | ||
37 | #define BYTES2INT16(array,pos) \ | 41 | #define BYTES2INT32(array, pos) \ |
38 | (array[pos] | (array[pos+1] << 8 )) | 42 | (((uint32_t)array[pos+0] << 0) | \ |
39 | #define BYTES2INT32(array,pos) \ | 43 | ((uint32_t)array[pos+1] << 8) | \ |
40 | ((long)array[pos] | ((long)array[pos+1] << 8 ) | \ | 44 | ((uint32_t)array[pos+2] << 16) | \ |
41 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | 45 | ((uint32_t)array[pos+3] << 24)) |
46 | |||
47 | #define INT322BYTES(array, pos, val) \ | ||
48 | ((array[pos+0] = (uint32_t)(val) >> 0), \ | ||
49 | (array[pos+1] = (uint32_t)(val) >> 8), \ | ||
50 | (array[pos+2] = (uint32_t)(val) >> 16), \ | ||
51 | (array[pos+3] = (uint32_t)(val) >> 24)) | ||
52 | |||
53 | #define BYTES2INT16(array, pos) \ | ||
54 | (((uint32_t)array[pos+0] << 0) | \ | ||
55 | ((uint32_t)array[pos+1] << 8)) | ||
56 | |||
57 | #define INT162BYTES(array, pos, val) \ | ||
58 | ((array[pos+0] = (uint16_t)(val) >> 0), \ | ||
59 | (array[pos+1] = (uint16_t)(val) >> 8)) | ||
42 | 60 | ||
43 | #define FATTYPE_FAT12 0 | 61 | #define FATTYPE_FAT12 0 |
44 | #define FATTYPE_FAT16 1 | 62 | #define FATTYPE_FAT16 1 |
@@ -83,82 +101,129 @@ | |||
83 | 101 | ||
84 | #define BPB_LAST_WORD 510 | 102 | #define BPB_LAST_WORD 510 |
85 | 103 | ||
104 | /* Short and long name directory entry template */ | ||
105 | union raw_dirent | ||
106 | { | ||
107 | struct /* short entry */ | ||
108 | { | ||
109 | uint8_t name[8+3]; /* 0 */ | ||
110 | uint8_t attr; /* 11 */ | ||
111 | uint8_t ntres; /* 12 */ | ||
112 | uint8_t crttimetenth; /* 13 */ | ||
113 | uint16_t crttime; /* 14 */ | ||
114 | uint16_t crtdate; /* 16 */ | ||
115 | uint16_t lstaccdate; /* 18 */ | ||
116 | uint16_t fstclushi; /* 20 */ | ||
117 | uint16_t wrttime; /* 22 */ | ||
118 | uint16_t wrtdate; /* 24 */ | ||
119 | uint16_t fstcluslo; /* 26 */ | ||
120 | uint32_t filesize; /* 28 */ | ||
121 | /* 32 */ | ||
122 | }; | ||
123 | struct /* long entry */ | ||
124 | { | ||
125 | uint8_t ldir_ord; /* 0 */ | ||
126 | uint8_t ldir_name1[10]; /* 1 */ | ||
127 | uint8_t ldir_attr; /* 11 */ | ||
128 | uint8_t ldir_type; /* 12 */ | ||
129 | uint8_t ldir_chksum; /* 13 */ | ||
130 | uint8_t ldir_name2[12]; /* 14 */ | ||
131 | uint16_t ldir_fstcluslo; /* 26 */ | ||
132 | uint8_t ldir_name3[4]; /* 28 */ | ||
133 | /* 32 */ | ||
134 | }; | ||
135 | struct /* raw byte array */ | ||
136 | { | ||
137 | uint8_t data[32]; /* 0 */ | ||
138 | /* 32 */ | ||
139 | }; | ||
140 | }; | ||
141 | |||
142 | |||
143 | /* at most 20 LFN entries */ | ||
144 | #define FATLONG_MAX_ORDER 20 | ||
145 | #define FATLONG_NAME_CHARS 13 | ||
146 | #define FATLONG_ORD_F_LAST 0x40 | ||
86 | 147 | ||
87 | /* attributes */ | 148 | /* attributes */ |
88 | #define FAT_ATTR_LONG_NAME (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ | 149 | #define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | \ |
89 | FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID) | 150 | ATTR_SYSTEM | ATTR_VOLUME_ID) |
90 | #define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ | 151 | #define ATTR_LONG_NAME_MASK (ATTR_READ_ONLY | ATTR_HIDDEN | \ |
91 | FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \ | 152 | ATTR_SYSTEM | ATTR_VOLUME_ID | \ |
92 | FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE ) | 153 | ATTR_DIRECTORY | ATTR_ARCHIVE ) |
154 | |||
155 | #define IS_LDIR_ATTR(attr) \ | ||
156 | (((attr) & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME) | ||
157 | |||
158 | #define IS_VOL_ID_ATTR(attr) \ | ||
159 | (((attr) & (ATTR_VOLUME_ID | ATTR_DIRECTORY)) == ATTR_VOLUME_ID) | ||
93 | 160 | ||
94 | /* NTRES flags */ | 161 | /* NTRES flags */ |
95 | #define FAT_NTRES_LC_NAME 0x08 | 162 | #define FAT_NTRES_LC_NAME 0x08 |
96 | #define FAT_NTRES_LC_EXT 0x10 | 163 | #define FAT_NTRES_LC_EXT 0x10 |
97 | 164 | ||
98 | #define FATDIR_NAME 0 | 165 | #define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) |
99 | #define FATDIR_ATTR 11 | 166 | #define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) |
100 | #define FATDIR_NTRES 12 | 167 | #define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE) |
101 | #define FATDIR_CRTTIMETENTH 13 | 168 | #define DIR_ENTRY_SIZE 32 |
102 | #define FATDIR_CRTTIME 14 | 169 | #define FAT_BAD_MARK 0x0ffffff7 |
103 | #define FATDIR_CRTDATE 16 | 170 | #define FAT_EOF_MARK 0x0ffffff8 |
104 | #define FATDIR_LSTACCDATE 18 | 171 | #define FAT16_BAD_MARK 0xfff7 |
105 | #define FATDIR_FSTCLUSHI 20 | 172 | #define FAT16_EOF_MARK 0xfff8 |
106 | #define FATDIR_WRTTIME 22 | 173 | |
107 | #define FATDIR_WRTDATE 24 | 174 | struct fsinfo |
108 | #define FATDIR_FSTCLUSLO 26 | 175 | { |
109 | #define FATDIR_FILESIZE 28 | 176 | unsigned long freecount; /* last known free cluster count */ |
110 | 177 | unsigned long nextfree; /* first cluster to start looking for free | |
111 | #define FATLONG_ORDER 0 | 178 | clusters, or 0xffffffff for no hint */ |
112 | #define FATLONG_TYPE 12 | ||
113 | #define FATLONG_CHKSUM 13 | ||
114 | #define FATLONG_LAST_LONG_ENTRY 0x40 | ||
115 | #define FATLONG_NAME_BYTES_PER_ENTRY 26 | ||
116 | /* at most 20 LFN entries, keep coherent with fat_dir->longname size ! */ | ||
117 | #define FATLONG_MAX_ORDER 20 | ||
118 | |||
119 | #define FATLONG_NAME_CHUNKS 3 | ||
120 | static unsigned char FATLONG_NAME_POS[FATLONG_NAME_CHUNKS] = {1, 14, 28}; | ||
121 | static unsigned char FATLONG_NAME_SIZE[FATLONG_NAME_CHUNKS] = {10, 12, 4}; | ||
122 | |||
123 | #define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) | ||
124 | #define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) | ||
125 | #define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE) | ||
126 | #define DIR_ENTRY_SIZE 32 | ||
127 | #define NAME_BYTES_PER_ENTRY 13 | ||
128 | #define FAT_BAD_MARK 0x0ffffff7 | ||
129 | #define FAT_EOF_MARK 0x0ffffff8 | ||
130 | #define FAT_LONGNAME_PAD_BYTE 0xff | ||
131 | #define FAT_LONGNAME_PAD_UCS 0xffff | ||
132 | |||
133 | struct fsinfo { | ||
134 | uint32_t freecount; /* last known free cluster count */ | ||
135 | uint32_t nextfree; /* first cluster to start looking for free | ||
136 | clusters, or 0xffffffff for no hint */ | ||
137 | }; | 179 | }; |
138 | /* fsinfo offsets */ | 180 | /* fsinfo offsets */ |
139 | #define FSINFO_FREECOUNT 488 | 181 | #define FSINFO_FREECOUNT 488 |
140 | #define FSINFO_NEXTFREE 492 | 182 | #define FSINFO_NEXTFREE 492 |
141 | 183 | ||
184 | #ifdef HAVE_FAT16SUPPORT | ||
185 | #define BPB_FN_SET16(bpb, fn) (bpb)->fn##__ = fn##16 | ||
186 | #define BPB_FN_SET32(bpb, fn) (bpb)->fn##__ = fn##32 | ||
187 | #define BPB_FN_DECL(fn, args...) (*fn##__)(struct bpb *bpb , ##args) | ||
188 | #define BPB_CALL(fn, bpb, args...) ((bpb)->fn##__(bpb , ##args)) | ||
189 | |||
190 | #define get_next_cluster(bpb, cluster) \ | ||
191 | BPB_CALL(get_next_cluster, (bpb), (cluster)) | ||
192 | #define find_free_cluster(bpb, startcluster) \ | ||
193 | BPB_CALL(find_free_cluster, (bpb), (startcluster)) | ||
194 | #define update_fat_entry(bpb, entry, value) \ | ||
195 | BPB_CALL(update_fat_entry, (bpb), (entry), (value)) | ||
196 | #define fat_recalc_free_internal(bpb) \ | ||
197 | BPB_CALL(fat_recalc_free_internal, (bpb)) | ||
198 | #else /* !HAVE_FAT16SUPPORT */ | ||
199 | #define get_next_cluster get_next_cluster32 | ||
200 | #define find_free_cluster find_free_cluster32 | ||
201 | #define update_fat_entry update_fat_entry32 | ||
202 | #define fat_recalc_free_internal fat_recalc_free_internal32 | ||
203 | #endif /* HAVE_FAT16SUPPORT */ | ||
204 | struct bpb; | ||
205 | static void update_fsinfo32(struct bpb *fat_bpb); | ||
206 | |||
142 | /* Note: This struct doesn't hold the raw values after mounting if | 207 | /* Note: This struct doesn't hold the raw values after mounting if |
143 | * bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte | 208 | * bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte |
144 | * physical sectors. */ | 209 | * physical sectors. */ |
145 | struct bpb | 210 | static struct bpb |
146 | { | 211 | { |
147 | int bpb_bytspersec; /* Bytes per sector, typically 512 */ | 212 | unsigned long bpb_bytspersec; /* Bytes per sector, typically 512 */ |
148 | unsigned int bpb_secperclus; /* Sectors per cluster */ | 213 | unsigned long bpb_secperclus; /* Sectors per cluster */ |
149 | int bpb_rsvdseccnt; /* Number of reserved sectors */ | 214 | unsigned long bpb_rsvdseccnt; /* Number of reserved sectors */ |
150 | int bpb_numfats; /* Number of FAT structures, typically 2 */ | 215 | unsigned long bpb_totsec16; /* Number of sectors on volume (old 16-bit) */ |
151 | int bpb_totsec16; /* Number of sectors on the volume (old 16-bit) */ | 216 | uint8_t bpb_numfats; /* Number of FAT structures, typically 2 */ |
152 | int bpb_media; /* Media type (typically 0xf0 or 0xf8) */ | 217 | uint8_t bpb_media; /* Media type (typically 0xf0 or 0xf8) */ |
153 | int bpb_fatsz16; /* Number of used sectors per FAT structure */ | 218 | uint16_t bpb_fatsz16; /* Number of used sectors per FAT structure */ |
154 | unsigned long bpb_totsec32; /* Number of sectors on the volume | 219 | unsigned long bpb_totsec32; /* Number of sectors on the volume |
155 | (new 32-bit) */ | 220 | (new 32-bit) */ |
156 | unsigned int last_word; /* 0xAA55 */ | 221 | uint16_t last_word; /* 0xAA55 */ |
222 | long bpb_rootclus; | ||
157 | 223 | ||
158 | /**** FAT32 specific *****/ | 224 | /**** FAT32 specific *****/ |
159 | long bpb_fatsz32; | 225 | unsigned long bpb_fatsz32; |
160 | long bpb_rootclus; | 226 | unsigned long bpb_fsinfo; |
161 | long bpb_fsinfo; | ||
162 | 227 | ||
163 | /* variables for internal use */ | 228 | /* variables for internal use */ |
164 | unsigned long fatsize; | 229 | unsigned long fatsize; |
@@ -167,936 +232,1246 @@ struct bpb | |||
167 | unsigned long firstdatasector; | 232 | unsigned long firstdatasector; |
168 | unsigned long startsector; | 233 | unsigned long startsector; |
169 | unsigned long dataclusters; | 234 | unsigned long dataclusters; |
235 | unsigned long fatrgnstart; | ||
236 | unsigned long fatrgnend; | ||
170 | struct fsinfo fsinfo; | 237 | struct fsinfo fsinfo; |
171 | #ifdef HAVE_FAT16SUPPORT | 238 | #ifdef HAVE_FAT16SUPPORT |
172 | int bpb_rootentcnt; /* Number of dir entries in the root */ | 239 | unsigned int bpb_rootentcnt; /* Number of dir entries in the root */ |
173 | /* internals for FAT16 support */ | 240 | /* internals for FAT16 support */ |
174 | bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */ | 241 | unsigned long rootdirsectornum; /* sector offset of root dir relative to start |
175 | unsigned int rootdiroffset; /* sector offset of root dir relative to start | 242 | * of first pseudo cluster */ |
176 | * of first pseudo cluster */ | 243 | #endif /* HAVE_FAT16SUPPORT */ |
177 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | 244 | |
178 | #ifdef HAVE_MULTIVOLUME | 245 | /** Additional information kept for each volume **/ |
179 | #ifdef HAVE_MULTIDRIVE | 246 | #ifdef HAVE_FAT16SUPPORT |
180 | int drive; /* on which physical device is this located */ | 247 | uint8_t is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */ |
181 | #endif | 248 | #endif |
182 | bool mounted; /* flag if this volume is mounted */ | 249 | #ifdef HAVE_MULTIDRIVE |
250 | uint8_t drive; /* on which physical device is this located */ | ||
183 | #endif | 251 | #endif |
184 | }; | ||
185 | |||
186 | static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */ | ||
187 | static bool initialized = false; | ||
188 | |||
189 | static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)); | ||
190 | static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)); | ||
191 | static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)); | ||
192 | static void *cache_fat_sector(IF_MV(struct bpb* fat_bpb,) | ||
193 | long secnum, bool dirty); | ||
194 | static void create_dos_name(const unsigned char *name, unsigned char *newname); | ||
195 | static void randomize_dos_name(unsigned char *name); | ||
196 | static unsigned long find_free_cluster(IF_MV(struct bpb* fat_bpb,) | ||
197 | unsigned long start); | ||
198 | static int transfer(IF_MV(struct bpb* fat_bpb,) unsigned long start, | ||
199 | long count, char* buf, bool write ); | ||
200 | |||
201 | #define FAT_CACHE_SIZE 0x20 | ||
202 | #define FAT_CACHE_MASK (FAT_CACHE_SIZE-1) | ||
203 | |||
204 | struct fat_cache_entry | ||
205 | { | ||
206 | long secnum; | ||
207 | bool inuse; | ||
208 | bool dirty; | ||
209 | #ifdef HAVE_MULTIVOLUME | 252 | #ifdef HAVE_MULTIVOLUME |
210 | struct bpb* fat_vol ; /* shared cache for all volumes */ | 253 | uint8_t volume; /* on which volume is this located (shortcut) */ |
211 | #endif | 254 | #endif |
255 | uint8_t mounted; /* true if volume is mounted, false otherwise */ | ||
256 | #ifdef HAVE_FAT16SUPPORT | ||
257 | /* some functions are different for different FAT types */ | ||
258 | long BPB_FN_DECL(get_next_cluster, long); | ||
259 | long BPB_FN_DECL(find_free_cluster, long); | ||
260 | int BPB_FN_DECL(update_fat_entry, unsigned long, unsigned long); | ||
261 | void BPB_FN_DECL(fat_recalc_free_internal); | ||
262 | #endif /* HAVE_FAT16SUPPORT */ | ||
263 | |||
264 | } fat_bpbs[NUM_VOLUMES]; /* mounted partition info */ | ||
265 | |||
266 | #define IS_FAT_SECTOR(bpb, sector) \ | ||
267 | (!((sector) >= (bpb)->fatrgnend || (sector) < (bpb)->fatrgnstart)) | ||
268 | |||
269 | /* set error code and jump to routine exit */ | ||
270 | #define FAT_ERROR(_rc) \ | ||
271 | ({ __builtin_constant_p(_rc) ? \ | ||
272 | ({ if (_rc != RC) rc = (_rc); }) : \ | ||
273 | ({ rc = (_rc); }); \ | ||
274 | goto fat_error; }) | ||
275 | |||
276 | #define FAT_BPB(volume) \ | ||
277 | ({ struct bpb * _bpb = &fat_bpbs[IF_MV_VOL(volume)]; \ | ||
278 | if (!_bpb->mounted) \ | ||
279 | { \ | ||
280 | DEBUGF("%s() - volume %d not mounted\n", \ | ||
281 | __func__, IF_MV_VOL(volume)); \ | ||
282 | _bpb = NULL; \ | ||
283 | } \ | ||
284 | _bpb; }) | ||
285 | |||
286 | enum add_dir_entry_flags | ||
287 | { | ||
288 | DIRENT_RETURN = 0x01, /* return the new short entry */ | ||
289 | DIRENT_TEMPL = 0x0e, /* all TEMPL flags */ | ||
290 | DIRENT_TEMPL_CRT = 0x02, /* use template crttime */ | ||
291 | DIRENT_TEMPL_WRT = 0x04, /* use template wrttime */ | ||
292 | DIRENT_TEMPL_ACC = 0x08, /* use template lstacc time */ | ||
293 | DIRENT_TEMPL_TIMES = 0x0e, /* keep all time fields */ | ||
212 | }; | 294 | }; |
213 | 295 | ||
214 | static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE] CACHEALIGN_ATTR; | 296 | struct fatlong_parse_state |
215 | static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE]; | 297 | { |
216 | static struct mutex cache_mutex SHAREDBSS_ATTR; | 298 | int ord_max; |
217 | static struct mutex tempbuf_mutex; | 299 | int ord; |
218 | static char fat_tempbuf[SECTOR_SIZE] CACHEALIGN_ATTR; | 300 | uint8_t chksum; |
219 | static bool tempbuf_locked; | 301 | }; |
220 | 302 | ||
221 | #if defined(HAVE_HOTSWAP) | 303 | static void cache_commit(struct bpb *fat_bpb) |
222 | void fat_lock(void) | ||
223 | { | 304 | { |
224 | mutex_lock(&cache_mutex); | 305 | dc_lock_cache(); |
306 | #ifdef HAVE_FAT16SUPPORT | ||
307 | if (!fat_bpb->is_fat16) | ||
308 | #endif | ||
309 | update_fsinfo32(fat_bpb); | ||
310 | dc_commit_all(IF_MV(fat_bpb->volume)); | ||
311 | dc_unlock_cache(); | ||
225 | } | 312 | } |
226 | 313 | ||
227 | void fat_unlock(void) | 314 | static void cache_discard(IF_MV_NONVOID(struct bpb *fat_bpb)) |
228 | { | 315 | { |
229 | mutex_unlock(&cache_mutex); | 316 | dc_lock_cache(); |
317 | dc_discard_all(IF_MV(fat_bpb->volume)); | ||
318 | dc_unlock_cache(); | ||
230 | } | 319 | } |
231 | #endif | ||
232 | 320 | ||
233 | static long cluster2sec(IF_MV(struct bpb* fat_bpb,) long cluster) | 321 | /* caches a FAT or data area sector */ |
322 | static void * cache_sector(struct bpb *fat_bpb, unsigned long secnum) | ||
234 | { | 323 | { |
235 | #ifndef HAVE_MULTIVOLUME | 324 | unsigned int flags; |
236 | struct bpb* fat_bpb = &fat_bpbs[0]; | 325 | void *buf = dc_cache_probe(IF_MV(fat_bpb->volume,) secnum, &flags); |
237 | #endif | ||
238 | #ifdef HAVE_FAT16SUPPORT | ||
239 | /* negative clusters (FAT16 root dir) don't get the 2 offset */ | ||
240 | int zerocluster = cluster < 0 ? 0 : 2; | ||
241 | #else | ||
242 | const long zerocluster = 2; | ||
243 | #endif | ||
244 | 326 | ||
245 | if (cluster > (long)(fat_bpb->dataclusters + 1)) | 327 | if (!flags) |
246 | { | 328 | { |
247 | DEBUGF( "cluster2sec() - Bad cluster number (%ld)\n", cluster); | 329 | int rc = storage_read_sectors(IF_MD(fat_bpb->drive,) |
248 | return -1; | 330 | secnum + fat_bpb->startsector, 1, buf); |
331 | if (UNLIKELY(rc < 0)) | ||
332 | { | ||
333 | DEBUGF("%s() - Could not read sector %ld" | ||
334 | " (error %d)\n", __func__, secnum, rc); | ||
335 | dc_discard_buf(buf); | ||
336 | return NULL; | ||
337 | } | ||
249 | } | 338 | } |
250 | 339 | ||
251 | return (cluster - zerocluster) * fat_bpb->bpb_secperclus | 340 | return buf; |
252 | + fat_bpb->firstdatasector; | ||
253 | } | 341 | } |
254 | 342 | ||
255 | void fat_size(IF_MV(int volume,) unsigned long* size, unsigned long* free) | 343 | /* returns a raw buffer for a sector; buffer counts as INUSE but filesystem |
344 | * contents are NOT loaded before returning - use when completely overwriting | ||
345 | * a sector's contents in order to avoid a fill */ | ||
346 | static void * cache_sector_buffer(IF_MV(struct bpb *fat_bpb,) | ||
347 | unsigned long secnum) | ||
256 | { | 348 | { |
257 | #ifndef HAVE_MULTIVOLUME | 349 | unsigned int flags; |
258 | const int volume = 0; | 350 | return dc_cache_probe(IF_MV(fat_bpb->volume,) secnum, &flags); |
259 | #endif | ||
260 | struct bpb* fat_bpb = &fat_bpbs[volume]; | ||
261 | if (size) | ||
262 | *size = fat_bpb->dataclusters * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024); | ||
263 | if (free) | ||
264 | *free = fat_bpb->fsinfo.freecount * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024); | ||
265 | } | 351 | } |
266 | 352 | ||
267 | void fat_init(void) | 353 | /* flush a cache buffer to storage */ |
354 | void dc_writeback_callback(IF_MV(int volume,) unsigned long sector, void *buf) | ||
268 | { | 355 | { |
269 | unsigned int i; | 356 | struct bpb * const fat_bpb = &fat_bpbs[IF_MV_VOL(volume)]; |
357 | unsigned int copies = !IS_FAT_SECTOR(fat_bpb, sector) ? | ||
358 | 1 : fat_bpb->bpb_numfats; | ||
359 | |||
360 | sector += fat_bpb->startsector; | ||
270 | 361 | ||
271 | if (!initialized) | 362 | while (1) |
272 | { | 363 | { |
273 | initialized = true; | 364 | int rc = storage_write_sectors(IF_MD(fat_bpb->drive,) sector, 1, buf); |
274 | mutex_init(&cache_mutex); | 365 | if (rc < 0) |
275 | mutex_init(&tempbuf_mutex); | 366 | { |
276 | tempbuf_locked = false; | 367 | panicf("%s() - Could not write sector %ld" |
368 | " (error %d)\n", __func__, sector, rc); | ||
369 | } | ||
370 | |||
371 | if (--copies == 0) | ||
372 | break; | ||
373 | |||
374 | /* Update next FAT */ | ||
375 | sector += fat_bpb->fatsize; | ||
277 | } | 376 | } |
377 | } | ||
278 | 378 | ||
279 | #ifdef HAVE_PRIORITY_SCHEDULING | 379 | static void raw_dirent_set_fstclus(union raw_dirent *ent, long fstclus) |
280 | /* Disable this because it is dangerous due to the assumption that | 380 | { |
281 | * mutex_unlock won't yield */ | 381 | ent->fstclushi = htole16(fstclus >> 16); |
282 | mutex_set_preempt(&cache_mutex, false); | 382 | ent->fstcluslo = htole16(fstclus & 0xffff); |
283 | #endif | 383 | } |
284 | 384 | ||
285 | /* mark the FAT cache as unused */ | 385 | static int bpb_is_sane(struct bpb *fat_bpb) |
286 | for(i = 0;i < FAT_CACHE_SIZE;i++) | 386 | { |
387 | if (fat_bpb->bpb_bytspersec % SECTOR_SIZE) | ||
287 | { | 388 | { |
288 | fat_cache[i].secnum = 8; /* We use a "safe" sector just in case */ | 389 | DEBUGF("%s() - Error: sector size is not sane (%lu)\n", |
289 | fat_cache[i].inuse = false; | 390 | __func__, fat_bpb->bpb_bytspersec); |
290 | fat_cache[i].dirty = false; | 391 | return -1; |
291 | #ifdef HAVE_MULTIVOLUME | ||
292 | fat_cache[i].fat_vol = NULL; | ||
293 | #endif | ||
294 | } | 392 | } |
295 | #ifdef HAVE_MULTIVOLUME | 393 | |
296 | /* mark the possible volumes as not mounted */ | 394 | if (fat_bpb->bpb_secperclus * fat_bpb->bpb_bytspersec > 128*1024ul) |
297 | for (i=0; i<NUM_VOLUMES;i++) | ||
298 | { | 395 | { |
299 | fat_bpbs[i].mounted = false; | 396 | DEBUGF("%s() - Error: cluster size is larger than 128K " |
397 | "(%lu * %lu = %lu)\n", __func__, | ||
398 | fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus, | ||
399 | fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus); | ||
400 | return -2; | ||
300 | } | 401 | } |
301 | #endif | ||
302 | } | ||
303 | 402 | ||
304 | /* fat_mount_internal is split out of fat_mount() to avoid having both the sector | 403 | if (fat_bpb->bpb_numfats != 2) |
305 | * buffer used here and the sector buffer used by update_fsinfo() on stack */ | 404 | { |
306 | static int fat_mount_internal(IF_MV(int volume,) IF_MD(int drive,) long startsector) | 405 | DEBUGF("%s() - Warning: NumFATS is not 2 (%u)\n", |
307 | { | 406 | __func__, fat_bpb->bpb_numfats); |
308 | #ifndef HAVE_MULTIVOLUME | 407 | } |
309 | const int volume = 0; | ||
310 | #endif | ||
311 | struct bpb* fat_bpb = &fat_bpbs[volume]; | ||
312 | int rc; | ||
313 | int secmult; | ||
314 | long datasec; | ||
315 | #ifdef HAVE_FAT16SUPPORT | ||
316 | int rootdirsectors; | ||
317 | #endif | ||
318 | 408 | ||
319 | unsigned char* buf = fat_get_sector_buffer(); | 409 | if (fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8) |
320 | /* Read the sector */ | ||
321 | rc = storage_read_sectors(IF_MD(drive,) startsector,1,buf); | ||
322 | if(rc) | ||
323 | { | 410 | { |
324 | fat_release_sector_buffer(); | 411 | DEBUGF("%s() - Warning: Non-standard media type " |
325 | DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)\n", rc); | 412 | "(0x%02x)\n", __func__, fat_bpb->bpb_media); |
326 | return rc * 10 - 1; | ||
327 | } | 413 | } |
328 | 414 | ||
329 | memset(fat_bpb, 0, sizeof(struct bpb)); | 415 | if (fat_bpb->last_word != 0xaa55) |
330 | fat_bpb->startsector = startsector; | 416 | { |
331 | #ifdef HAVE_MULTIDRIVE | 417 | DEBUGF("%s() - Error: Last word is not " |
332 | fat_bpb->drive = drive; | 418 | "0xaa55 (0x%04x)\n", __func__, fat_bpb->last_word); |
333 | #endif | 419 | return -3; |
420 | } | ||
334 | 421 | ||
335 | fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC); | 422 | if (fat_bpb->fsinfo.freecount > |
336 | secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE; | 423 | (fat_bpb->totalsectors - fat_bpb->firstdatasector) / |
337 | /* Sanity check is performed later */ | 424 | fat_bpb->bpb_secperclus) |
425 | { | ||
426 | DEBUGF("%s() - Error: FSInfo.Freecount > disk size " | ||
427 | "(0x%04lx)\n", __func__, | ||
428 | (unsigned long)fat_bpb->fsinfo.freecount); | ||
429 | return -4; | ||
430 | } | ||
338 | 431 | ||
339 | fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS]; | 432 | return 0; |
340 | fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf,BPB_RSVDSECCNT); | 433 | } |
341 | fat_bpb->bpb_numfats = buf[BPB_NUMFATS]; | ||
342 | fat_bpb->bpb_media = buf[BPB_MEDIA]; | ||
343 | fat_bpb->bpb_fatsz16 = secmult * BYTES2INT16(buf,BPB_FATSZ16); | ||
344 | fat_bpb->bpb_fatsz32 = secmult * BYTES2INT32(buf,BPB_FATSZ32); | ||
345 | fat_bpb->bpb_totsec16 = secmult * BYTES2INT16(buf,BPB_TOTSEC16); | ||
346 | fat_bpb->bpb_totsec32 = secmult * BYTES2INT32(buf,BPB_TOTSEC32); | ||
347 | fat_bpb->last_word = BYTES2INT16(buf,BPB_LAST_WORD); | ||
348 | 434 | ||
349 | /* calculate a few commonly used values */ | 435 | static uint8_t shortname_checksum(const unsigned char *shortname) |
350 | if (fat_bpb->bpb_fatsz16 != 0) | 436 | { |
351 | fat_bpb->fatsize = fat_bpb->bpb_fatsz16; | 437 | /* calculate shortname checksum */ |
352 | else | 438 | uint8_t chksum = 0; |
353 | fat_bpb->fatsize = fat_bpb->bpb_fatsz32; | ||
354 | 439 | ||
355 | if (fat_bpb->bpb_totsec16 != 0) | 440 | for (unsigned int i = 0; i < 11; i++) |
356 | fat_bpb->totalsectors = fat_bpb->bpb_totsec16; | 441 | chksum = (chksum << 7) + (chksum >> 1) + shortname[i]; |
357 | else | ||
358 | fat_bpb->totalsectors = fat_bpb->bpb_totsec32; | ||
359 | 442 | ||
360 | #ifdef HAVE_FAT16SUPPORT | 443 | return chksum; |
361 | fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT); | 444 | } |
362 | if (!fat_bpb->bpb_bytspersec) | ||
363 | { | ||
364 | fat_release_sector_buffer(); | ||
365 | return -2; | ||
366 | } | ||
367 | rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE | ||
368 | + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec); | ||
369 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
370 | 445 | ||
371 | fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt | 446 | static void parse_short_direntry(const union raw_dirent *ent, |
372 | #ifdef HAVE_FAT16SUPPORT | 447 | struct fat_direntry *fatent) |
373 | + rootdirsectors | 448 | { |
374 | #endif | 449 | fatent->attr = ent->attr; |
375 | + fat_bpb->bpb_numfats * fat_bpb->fatsize; | 450 | fatent->crttimetenth = ent->crttimetenth; |
451 | fatent->crttime = letoh16(ent->crttime); | ||
452 | fatent->crtdate = letoh16(ent->crtdate); | ||
453 | fatent->lstaccdate = letoh16(ent->lstaccdate); | ||
454 | fatent->wrttime = letoh16(ent->wrttime); | ||
455 | fatent->wrtdate = letoh16(ent->wrtdate); | ||
456 | fatent->filesize = letoh32(ent->filesize); | ||
457 | fatent->firstcluster = ((uint32_t)letoh16(ent->fstcluslo) ) | | ||
458 | ((uint32_t)letoh16(ent->fstclushi) << 16); | ||
376 | 459 | ||
377 | /* Determine FAT type */ | 460 | /* fix the name */ |
378 | datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector; | 461 | bool lowercase = ent->ntres & FAT_NTRES_LC_NAME; |
379 | if (fat_bpb->bpb_secperclus) | 462 | unsigned char c = ent->name[0]; |
380 | fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus; | 463 | |
381 | else | 464 | if (c == 0x05) /* special kanji char */ |
465 | c = 0xe5; | ||
466 | |||
467 | int j = 0; | ||
468 | |||
469 | for (int i = 0; c != ' '; c = ent->name[i]) | ||
382 | { | 470 | { |
383 | fat_release_sector_buffer(); | 471 | fatent->shortname[j++] = lowercase ? tolower(c) : c; |
384 | return -2; | ||
385 | } | ||
386 | 472 | ||
387 | #ifdef TEST_FAT | 473 | if (++i >= 8) |
388 | /* | 474 | break; |
389 | we are sometimes testing with "illegally small" fat32 images, | ||
390 | so we don't use the proper fat32 test case for test code | ||
391 | */ | ||
392 | if ( fat_bpb->bpb_fatsz16 ) | ||
393 | #else | ||
394 | if ( fat_bpb->dataclusters < 65525 ) | ||
395 | #endif | ||
396 | { /* FAT16 */ | ||
397 | #ifdef HAVE_FAT16SUPPORT | ||
398 | fat_bpb->is_fat16 = true; | ||
399 | if (fat_bpb->dataclusters < 4085) | ||
400 | { /* FAT12 */ | ||
401 | fat_release_sector_buffer(); | ||
402 | DEBUGF("This is FAT12. Go away!\n"); | ||
403 | return -2; | ||
404 | } | ||
405 | #else /* #ifdef HAVE_FAT16SUPPORT */ | ||
406 | fat_release_sector_buffer(); | ||
407 | DEBUGF("This is not FAT32. Go away!\n"); | ||
408 | return -2; | ||
409 | #endif /* #ifndef HAVE_FAT16SUPPORT */ | ||
410 | } | 475 | } |
411 | 476 | ||
412 | #ifdef HAVE_FAT16SUPPORT | 477 | if (ent->name[8] != ' ') |
413 | if (fat_bpb->is_fat16) | 478 | { |
414 | { /* FAT16 specific part of BPB */ | 479 | lowercase = ent->ntres & FAT_NTRES_LC_EXT; |
415 | int dirclusters; | 480 | fatent->shortname[j++] = '.'; |
416 | fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt | 481 | |
417 | + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16; | 482 | for (int i = 8; i < 11 && (c = ent->name[i]) != ' '; i++) |
418 | dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1) | 483 | fatent->shortname[j++] = lowercase ? tolower(c) : c; |
419 | / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */ | ||
420 | /* I assign negative pseudo cluster numbers for the root directory, | ||
421 | their range is counted upward until -1. */ | ||
422 | fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data*/ | ||
423 | fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus | ||
424 | - rootdirsectors; | ||
425 | } | ||
426 | else | ||
427 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
428 | { /* FAT32 specific part of BPB */ | ||
429 | fat_bpb->bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS); | ||
430 | fat_bpb->bpb_fsinfo = secmult * BYTES2INT16(buf,BPB_FSINFO); | ||
431 | fat_bpb->rootdirsector = cluster2sec(IF_MV(fat_bpb,) | ||
432 | fat_bpb->bpb_rootclus); | ||
433 | } | 484 | } |
434 | 485 | ||
435 | rc = bpb_is_sane(IF_MV(fat_bpb)); | 486 | fatent->shortname[j] = 0; |
436 | if (rc < 0) | 487 | } |
488 | |||
489 | static unsigned char char2dos(unsigned char c, int *np) | ||
490 | { | ||
491 | /* FIXME: needs conversion to OEM charset FIRST but there is currently | ||
492 | no unicode function for that! */ | ||
493 | |||
494 | /* smallest tables with one-step lookup that directly map the lists; | ||
495 | here we're only concerned with what gets through the longname | ||
496 | filter (check_longname will have been called earlier so common | ||
497 | illegal chars are neither in these tables nor checked for) */ | ||
498 | static const unsigned char remove_chars_tbl[3] = | ||
499 | { 0, '.', ' ' }; | ||
500 | |||
501 | static const unsigned char replace_chars_tbl[11] = | ||
502 | { ',', 0, 0, '[', ';', ']', '=', 0, 0, 0, '+' }; | ||
503 | |||
504 | if (remove_chars_tbl[c % 3] == c) | ||
437 | { | 505 | { |
438 | fat_release_sector_buffer(); | 506 | /* Illegal char, remove */ |
439 | DEBUGF( "fat_mount() - BPB is not sane\n"); | 507 | c = 0; |
440 | return rc * 10 - 3; | 508 | *np = 0; |
441 | } | 509 | } |
442 | 510 | else if (c >= 0x80 || replace_chars_tbl[c % 11] == c) | |
443 | #ifdef HAVE_FAT16SUPPORT | ||
444 | if (fat_bpb->is_fat16) | ||
445 | { | 511 | { |
446 | fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */ | 512 | /* Illegal char, replace (note: NTFS behavior for extended chars) */ |
447 | fat_bpb->fsinfo.nextfree = 0xffffffff; | 513 | c = '_'; |
514 | *np = 0; | ||
448 | } | 515 | } |
449 | else | 516 | else |
450 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
451 | { | 517 | { |
452 | /* Read the fsinfo sector */ | 518 | c = toupper(c); |
453 | rc = storage_read_sectors(IF_MD(drive,) | ||
454 | startsector + fat_bpb->bpb_fsinfo, 1, buf); | ||
455 | if (rc < 0) | ||
456 | { | ||
457 | fat_release_sector_buffer(); | ||
458 | DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)\n", rc); | ||
459 | return rc * 10 - 4; | ||
460 | } | ||
461 | fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT); | ||
462 | fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); | ||
463 | } | 519 | } |
464 | fat_release_sector_buffer(); | ||
465 | return 0; | ||
466 | } | ||
467 | 520 | ||
468 | void* fat_get_sector_buffer() | 521 | return c; |
469 | { | ||
470 | mutex_lock(&tempbuf_mutex); | ||
471 | if (tempbuf_locked) | ||
472 | panicf("FAT: Tried to lock temporary sector buffer twice!"); | ||
473 | tempbuf_locked = true; | ||
474 | return fat_tempbuf; | ||
475 | } | 522 | } |
476 | 523 | ||
477 | void fat_release_sector_buffer() | 524 | /* convert long name into dos name, possibly recommending randomization */ |
525 | static void create_dos_name(unsigned char *basisname, | ||
526 | const unsigned char *name, int *np) | ||
478 | { | 527 | { |
479 | tempbuf_locked = false; | 528 | int i; |
480 | mutex_unlock(&tempbuf_mutex); | 529 | |
530 | /* FIXME: needs conversion to OEM charset FIRST but there is currently | ||
531 | no unicode function for that! */ | ||
532 | |||
533 | /* as per FAT spec, set "lossy conversion" flag if any destructive | ||
534 | alterations to the name occur other than case changes */ | ||
535 | *np = -1; | ||
536 | |||
537 | /* find extension part */ | ||
538 | unsigned char *ext = strrchr(name, '.'); | ||
539 | if (ext && (ext == name || strchr(ext, ' '))) | ||
540 | ext = NULL; /* handle .dotnames / extensions cannot have spaces */ | ||
541 | |||
542 | /* name part */ | ||
543 | for (i = 0; *name && (!ext || name < ext) && (i < 8); name++) | ||
544 | { | ||
545 | unsigned char c = char2dos(*name, np); | ||
546 | if (c) | ||
547 | basisname[i++] = c; | ||
548 | } | ||
549 | |||
550 | /* pad both name and extension */ | ||
551 | while (i < 11) | ||
552 | basisname[i++] = ' '; | ||
553 | |||
554 | if (basisname[0] == 0xe5) /* special kanji character */ | ||
555 | basisname[0] = 0x05; | ||
556 | |||
557 | /* extension part */ | ||
558 | if (!ext++) | ||
559 | return; /* no extension */ | ||
560 | |||
561 | for (i = 8; *ext && i < 11; ext++) | ||
562 | { | ||
563 | unsigned char c = char2dos(*ext, np); | ||
564 | if (c) | ||
565 | basisname[i++] = c; | ||
566 | } | ||
567 | |||
568 | if (*ext) | ||
569 | *np = 0; /* extension too long */ | ||
481 | } | 570 | } |
482 | 571 | ||
483 | #ifdef MAX_LOG_SECTOR_SIZE | 572 | static void randomize_dos_name(unsigned char *dosname, |
484 | int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume)) | 573 | const unsigned char *basisname, |
574 | int *np) | ||
485 | { | 575 | { |
486 | #ifdef HAVE_MULTIVOLUME | 576 | int n = *np; |
487 | if(!fat_bpbs[volume].mounted) | 577 | |
488 | return 0; | 578 | memcpy(dosname, basisname, 11); |
489 | return fat_bpbs[volume].bpb_bytspersec; | 579 | |
490 | #else | 580 | if (n < 0) |
491 | return fat_bpbs[0].bpb_bytspersec; | 581 | { |
492 | #endif | 582 | /* first one just copies */ |
583 | *np = 0; | ||
584 | return; | ||
585 | } | ||
586 | |||
587 | /* the "~n" string can range from "~1" to "~999999" | ||
588 | of course a directory can have at most 65536 entries which means | ||
589 | the numbers will never be required to get that big in order to map | ||
590 | to a unique name */ | ||
591 | if (++n > 999999) | ||
592 | n = 1; | ||
593 | |||
594 | unsigned char numtail[8]; /* holds "~n" */ | ||
595 | unsigned int numtaillen = snprintf(numtail, 8, "~%d", n); | ||
596 | |||
597 | unsigned int basislen = 0; | ||
598 | while (basislen < 8 && basisname[basislen] != ' ') | ||
599 | basislen++; | ||
600 | |||
601 | memcpy(dosname + MIN(8 - numtaillen, basislen), numtail, numtaillen); | ||
602 | |||
603 | *np = n; | ||
493 | } | 604 | } |
494 | #endif | ||
495 | 605 | ||
496 | int fat_mount(IF_MV(int volume,) IF_MD(int drive,) long startsector) | 606 | /* check long filename for validity */ |
607 | static int check_longname(const unsigned char *name) | ||
497 | { | 608 | { |
498 | #ifndef HAVE_MULTIVOLUME | 609 | /* smallest table with one-step lookup that directly maps the list */ |
499 | const int volume = 0; | 610 | static const unsigned char invalid_chars_tbl[19] = |
500 | #endif | 611 | { |
501 | struct bpb* fat_bpb = &fat_bpbs[volume]; | 612 | 0, ':', 0, '<', '*', '>', '?', 0, 0, |
502 | int rc; | 613 | '/', '|', 0, 0, 0x7f, 0, '"', '\\', 0, 0 |
614 | }; | ||
503 | 615 | ||
504 | rc = fat_mount_internal(IF_MV(volume,) IF_MD(drive,) startsector); | 616 | if (!name) |
617 | return -1; | ||
505 | 618 | ||
506 | if(rc!=0) return rc; | 619 | unsigned int c = *name; |
507 | 620 | ||
508 | /* calculate freecount if unset */ | 621 | do |
509 | if ( fat_bpb->fsinfo.freecount == 0xffffffff ) | ||
510 | { | 622 | { |
511 | fat_recalc_free(IF_MV(volume)); | 623 | if (c < 0x20 || invalid_chars_tbl[c % 19] == c) |
624 | return -2; | ||
512 | } | 625 | } |
626 | while ((c = *++name)); | ||
513 | 627 | ||
514 | LDEBUGF("Freecount: %ld\n",(unsigned long)fat_bpb->fsinfo.freecount); | 628 | /* check trailing space(s) and periods */ |
515 | LDEBUGF("Nextfree: 0x%lx\n",(unsigned long)fat_bpb->fsinfo.nextfree); | 629 | c = *--name; |
516 | LDEBUGF("Cluster count: 0x%lx\n",(unsigned long)fat_bpb->dataclusters); | 630 | if (c == ' ' || c == '.') |
517 | LDEBUGF("Sectors per cluster: %d\n",fat_bpb->bpb_secperclus); | 631 | return -3; |
518 | LDEBUGF("FAT sectors: 0x%lx\n",(unsigned long)fat_bpb->fatsize); | ||
519 | |||
520 | #ifdef HAVE_MULTIVOLUME | ||
521 | fat_bpb->mounted = true; | ||
522 | #endif | ||
523 | 632 | ||
524 | return 0; | 633 | return 0; |
525 | } | 634 | } |
526 | 635 | ||
527 | int fat_unmount(int volume, bool flush) | 636 | /* Get first longname entry name offset */ |
637 | static inline unsigned int longent_char_first(void) | ||
528 | { | 638 | { |
529 | int rc; | 639 | return 1; |
530 | #ifdef HAVE_MULTIVOLUME | 640 | } |
531 | struct bpb* fat_bpb = &fat_bpbs[volume]; | ||
532 | #else | ||
533 | (void)volume; | ||
534 | #endif | ||
535 | 641 | ||
536 | if(flush) | 642 | /* Get the next longname entry offset or 0 if the end is reached */ |
643 | static inline unsigned int longent_char_next(unsigned int i) | ||
644 | { | ||
645 | switch (i += 2) | ||
537 | { | 646 | { |
538 | rc = flush_fat(IF_MV(fat_bpb)); /* the clean way, while still alive */ | 647 | case 26: i -= 1; /* return 28 */ |
648 | case 11: i += 3; /* return 14 */ | ||
539 | } | 649 | } |
540 | else | 650 | |
541 | { /* volume is not accessible any more, e.g. MMC removed */ | 651 | return i < 32 ? i : 0; |
542 | int i; | ||
543 | mutex_lock(&cache_mutex); | ||
544 | for(i = 0;i < FAT_CACHE_SIZE;i++) | ||
545 | { | ||
546 | struct fat_cache_entry *fce = &fat_cache[i]; | ||
547 | if(fce->inuse | ||
548 | #ifdef HAVE_MULTIVOLUME | ||
549 | && fce->fat_vol == fat_bpb | ||
550 | #endif | ||
551 | ) | ||
552 | { | ||
553 | fce->inuse = false; /* discard all from that volume */ | ||
554 | fce->dirty = false; | ||
555 | } | ||
556 | } | ||
557 | mutex_unlock(&cache_mutex); | ||
558 | rc = 0; | ||
559 | } | ||
560 | #ifdef HAVE_MULTIVOLUME | ||
561 | fat_bpb->mounted = false; | ||
562 | #endif | ||
563 | return rc; | ||
564 | } | 652 | } |
565 | 653 | ||
566 | void fat_recalc_free(IF_MV_NONVOID(int volume)) | 654 | /* initialize the parse state; call before parsing first long entry */ |
655 | static void NO_INLINE fatlong_parse_start(struct fatlong_parse_state *lnparse) | ||
567 | { | 656 | { |
568 | #ifndef HAVE_MULTIVOLUME | 657 | /* no inline so gcc can't figure out what isn't initialized here; |
569 | const int volume = 0; | 658 | ord_max is king as to the validity of all other fields */ |
570 | #endif | 659 | lnparse->ord_max = -1; /* one resync per parse operation */ |
571 | struct bpb* fat_bpb = &fat_bpbs[volume]; | 660 | } |
572 | long free = 0; | 661 | |
573 | unsigned long i; | 662 | /* convert the FAT long name entry to a contiguous segment */ |
574 | #ifdef HAVE_FAT16SUPPORT | 663 | static bool fatlong_parse_entry(struct fatlong_parse_state *lnparse, |
575 | if (fat_bpb->is_fat16) | 664 | const union raw_dirent *ent, |
665 | struct fat_direntry *fatent) | ||
666 | { | ||
667 | int ord = ent->ldir_ord; | ||
668 | |||
669 | if (ord & FATLONG_ORD_F_LAST) | ||
576 | { | 670 | { |
577 | for (i = 0; i<fat_bpb->fatsize; i++) { | 671 | /* this entry is the first long entry (first in order but |
578 | unsigned int j; | 672 | containing last part) */ |
579 | uint16_t* fat = cache_fat_sector(IF_MV(fat_bpb,) i, false); | 673 | ord &= ~FATLONG_ORD_F_LAST; |
580 | for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { | 674 | |
581 | unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j; | 675 | if (ord == 0 || ord > FATLONG_MAX_ORDER) |
582 | if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ | 676 | { |
583 | break; | 677 | lnparse->ord_max = 0; |
584 | 678 | return true; | |
585 | if (letoh16(fat[j]) == 0x0000) { | ||
586 | free++; | ||
587 | if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) | ||
588 | fat_bpb->fsinfo.nextfree = c; | ||
589 | } | ||
590 | } | ||
591 | } | 679 | } |
680 | |||
681 | lnparse->ord_max = ord; | ||
682 | lnparse->ord = ord; | ||
683 | lnparse->chksum = ent->ldir_chksum; | ||
592 | } | 684 | } |
593 | else | 685 | else |
594 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | 686 | { |
595 | { | 687 | /* valid ordinals yet? */ |
596 | for (i = 0; i<fat_bpb->fatsize; i++) { | 688 | if (lnparse->ord_max <= 0) |
597 | unsigned int j; | 689 | { |
598 | uint32_t* fat = cache_fat_sector(IF_MV(fat_bpb,) i, false); | 690 | if (lnparse->ord_max == 0) |
599 | for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { | 691 | return true; |
600 | unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j; | 692 | |
601 | if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ | 693 | lnparse->ord_max = 0; |
602 | break; | 694 | return false; /* try resync */ |
603 | 695 | } | |
604 | if (!(letoh32(fat[j]) & 0x0fffffff)) { | 696 | |
605 | free++; | 697 | /* check ordinal continuity and that the checksum matches the |
606 | if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) | 698 | one stored in the last entry */ |
607 | fat_bpb->fsinfo.nextfree = c; | 699 | if (ord == 0 || ord != lnparse->ord - 1 || |
608 | } | 700 | lnparse->chksum != ent->ldir_chksum) |
609 | } | 701 | { |
702 | lnparse->ord_max = 0; | ||
703 | return true; | ||
610 | } | 704 | } |
611 | } | 705 | } |
612 | fat_bpb->fsinfo.freecount = free; | ||
613 | update_fsinfo(IF_MV(fat_bpb)); | ||
614 | } | ||
615 | 706 | ||
616 | static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)) | 707 | /* so far so good; save entry information */ |
617 | { | 708 | lnparse->ord = ord; |
618 | #ifndef HAVE_MULTIVOLUME | 709 | |
619 | struct bpb* fat_bpb = &fat_bpbs[0]; | 710 | uint16_t *ucsp = fatent->ucssegs[ord - 1 + 5]; |
620 | #endif | 711 | unsigned int i = longent_char_first(); |
621 | if(fat_bpb->bpb_bytspersec % SECTOR_SIZE) | 712 | |
713 | while ((*ucsp++ = BYTES2INT16(ent->data, i))) | ||
622 | { | 714 | { |
623 | DEBUGF( "bpb_is_sane() - Error: sector size is not sane (%d)\n", | 715 | if (!(i = longent_char_next(i))) |
624 | fat_bpb->bpb_bytspersec); | 716 | return true; |
625 | return -1; | ||
626 | } | ||
627 | if((long)fat_bpb->bpb_secperclus * SECTOR_SIZE > 128L*1024L) | ||
628 | { | ||
629 | /* We don't multiply by bpb_bytspersec here, because | ||
630 | * back in fat_mount_internal() bpb_secperclus has been | ||
631 | * "normalised" to 512 byte clusters, by multiplying with | ||
632 | * secmult. */ | ||
633 | DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K " | ||
634 | "(%d * %d = %d)\n", | ||
635 | fat_bpb->bpb_bytspersec, | ||
636 | fat_bpb->bpb_secperclus / (fat_bpb->bpb_bytspersec / SECTOR_SIZE), | ||
637 | fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus / | ||
638 | (fat_bpb->bpb_bytspersec / SECTOR_SIZE)); | ||
639 | return -2; | ||
640 | } | 717 | } |
641 | if(fat_bpb->bpb_numfats != 2) | 718 | |
719 | /* segment may end early only in last entry */ | ||
720 | if (ord == lnparse->ord_max) | ||
642 | { | 721 | { |
643 | DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n", | 722 | /* the only valid padding, if any, is 0xffff */ |
644 | fat_bpb->bpb_numfats); | 723 | do |
724 | { | ||
725 | if (!(i = longent_char_next(i))) | ||
726 | return true; | ||
727 | } | ||
728 | while (BYTES2INT16(ent->data, i) == 0xffff); | ||
645 | } | 729 | } |
646 | if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8) | 730 | |
731 | /* long filename is corrupt */ | ||
732 | lnparse->ord_max = 0; | ||
733 | return true; | ||
734 | } | ||
735 | |||
736 | /* finish parsing of the longname entries and do the conversion to | ||
737 | UTF-8 if we have all the segments */ | ||
738 | static bool fatlong_parse_finish(struct fatlong_parse_state *lnparse, | ||
739 | const union raw_dirent *ent, | ||
740 | struct fat_direntry *fatent) | ||
741 | { | ||
742 | parse_short_direntry(ent, fatent); | ||
743 | |||
744 | /* ord_max must not have been set to <= 0 because of any earlier problems | ||
745 | and the ordinal immediately before the shortname entry must be 1 */ | ||
746 | if (lnparse->ord_max <= 0 || lnparse->ord != 1) | ||
747 | return false; | ||
748 | |||
749 | /* check the longname checksums against the shortname checksum */ | ||
750 | if (lnparse->chksum != shortname_checksum(ent->name)) | ||
751 | return false; | ||
752 | |||
753 | /* longname is good so far so convert all the segments to UTF-8 */ | ||
754 | unsigned char * const name = fatent->name; | ||
755 | unsigned char *p = name; | ||
756 | |||
757 | /* ensure the last segment is NULL-terminated if it is filled */ | ||
758 | fatent->ucssegs[lnparse->ord_max + 5][0] = 0x0000; | ||
759 | |||
760 | for (uint16_t *ucsp = fatent->ucssegs[5], ucc = *ucsp; | ||
761 | ucc; ucc = *++ucsp) | ||
647 | { | 762 | { |
648 | DEBUGF( "bpb_is_sane() - Warning: Non-standard " | 763 | /* end should be hit before ever seeing padding */ |
649 | "media type (0x%02x)\n", | 764 | if (ucc == 0xffff) |
650 | fat_bpb->bpb_media); | 765 | return false; |
766 | |||
767 | if ((p = utf8encode(ucc, p)) - name > FAT_DIRENTRY_NAME_MAX) | ||
768 | return false; | ||
651 | } | 769 | } |
652 | if(fat_bpb->last_word != 0xaa55) | 770 | |
771 | /* longname ok */ | ||
772 | *p = '\0'; | ||
773 | return true; | ||
774 | } | ||
775 | |||
776 | static unsigned long cluster2sec(struct bpb *fat_bpb, long cluster) | ||
777 | { | ||
778 | long zerocluster = 2; | ||
779 | |||
780 | /* negative clusters (FAT16 root dir) don't get the 2 offset */ | ||
781 | #ifdef HAVE_FAT16SUPPORT | ||
782 | if (fat_bpb->is_fat16 && cluster < 0) | ||
653 | { | 783 | { |
654 | DEBUGF( "bpb_is_sane() - Error: Last word is not " | 784 | zerocluster = 0; |
655 | "0xaa55 (0x%04x)\n", fat_bpb->last_word); | ||
656 | return -3; | ||
657 | } | 785 | } |
658 | 786 | else | |
659 | if (fat_bpb->fsinfo.freecount > | 787 | #endif /* HAVE_FAT16SUPPORT */ |
660 | (fat_bpb->totalsectors - fat_bpb->firstdatasector)/ | 788 | if ((unsigned long)cluster > fat_bpb->dataclusters + 1) |
661 | fat_bpb->bpb_secperclus) | ||
662 | { | 789 | { |
663 | DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size " | 790 | DEBUGF( "%s() - Bad cluster number (%ld)\n", __func__, cluster); |
664 | "(0x%04lx)\n", (unsigned long)fat_bpb->fsinfo.freecount); | 791 | return 0; |
665 | return -4; | ||
666 | } | 792 | } |
667 | 793 | ||
668 | return 0; | 794 | return (unsigned long)(cluster - zerocluster)*fat_bpb->bpb_secperclus |
795 | + fat_bpb->firstdatasector; | ||
669 | } | 796 | } |
670 | 797 | ||
671 | static void flush_fat_sector(struct fat_cache_entry *fce, | 798 | #ifdef HAVE_FAT16SUPPORT |
672 | unsigned char *sectorbuf) | 799 | static long get_next_cluster16(struct bpb *fat_bpb, long startcluster) |
673 | { | 800 | { |
674 | int rc; | 801 | /* if FAT16 root dir, dont use the FAT */ |
675 | long secnum; | 802 | if (startcluster < 0) |
803 | return startcluster + 1; | ||
676 | 804 | ||
677 | /* With multivolume, use only the FAT info from the cached sector! */ | 805 | unsigned long entry = startcluster; |
678 | #ifdef HAVE_MULTIVOLUME | 806 | unsigned long sector = entry / CLUSTERS_PER_FAT16_SECTOR; |
679 | secnum = fce->secnum + fce->fat_vol->startsector; | 807 | unsigned long offset = entry % CLUSTERS_PER_FAT16_SECTOR; |
680 | #else | 808 | |
681 | secnum = fce->secnum + fat_bpbs[0].startsector; | 809 | dc_lock_cache(); |
682 | #endif | ||
683 | 810 | ||
684 | /* Write to the first FAT */ | 811 | uint16_t *sec = cache_sector(fat_bpb, sector + fat_bpb->fatrgnstart); |
685 | rc = storage_write_sectors(IF_MD(fce->fat_vol->drive,) | 812 | if (!sec) |
686 | secnum, 1, | ||
687 | sectorbuf); | ||
688 | if(rc < 0) | ||
689 | { | 813 | { |
690 | panicf("flush_fat_sector() - Could not write sector %ld" | 814 | dc_unlock_cache(); |
691 | " (error %d)\n", | 815 | DEBUGF("%s: Could not cache sector %d\n", __func__, sector); |
692 | secnum, rc); | 816 | return -1; |
693 | } | 817 | } |
694 | #ifdef HAVE_MULTIVOLUME | 818 | |
695 | if(fce->fat_vol->bpb_numfats > 1) | 819 | long next = letoh16(sec[offset]); |
696 | #else | 820 | |
697 | if(fat_bpbs[0].bpb_numfats > 1) | 821 | /* is this last cluster in chain? */ |
698 | #endif | 822 | if (next >= FAT16_EOF_MARK) |
823 | next = 0; | ||
824 | |||
825 | dc_unlock_cache(); | ||
826 | return next; | ||
827 | } | ||
828 | |||
829 | static long find_free_cluster16(struct bpb *fat_bpb, long startcluster) | ||
830 | { | ||
831 | unsigned long entry = startcluster; | ||
832 | unsigned long sector = entry / CLUSTERS_PER_FAT16_SECTOR; | ||
833 | unsigned long offset = entry % CLUSTERS_PER_FAT16_SECTOR; | ||
834 | |||
835 | for (unsigned long i = 0; i < fat_bpb->fatsize; i++) | ||
699 | { | 836 | { |
700 | /* Write to the second FAT */ | 837 | unsigned long nr = (i + sector) % fat_bpb->fatsize; |
701 | #ifdef HAVE_MULTIVOLUME | 838 | uint16_t *sec = cache_sector(fat_bpb, nr + fat_bpb->fatrgnstart); |
702 | secnum += fce->fat_vol->fatsize; | 839 | if (!sec) |
703 | #else | 840 | break; |
704 | secnum += fat_bpbs[0].fatsize; | 841 | |
705 | #endif | 842 | for (unsigned long j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) |
706 | rc = storage_write_sectors(IF_MD(fce->fat_vol->drive,) | ||
707 | secnum, 1, sectorbuf); | ||
708 | if(rc < 0) | ||
709 | { | 843 | { |
710 | panicf("flush_fat_sector() - Could not write sector %ld" | 844 | unsigned long k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR; |
711 | " (error %d)\n", | 845 | |
712 | secnum, rc); | 846 | if (letoh16(sec[k]) == 0x0000) |
847 | { | ||
848 | unsigned long c = nr * CLUSTERS_PER_FAT16_SECTOR + k; | ||
849 | /* Ignore the reserved clusters 0 & 1, and also | ||
850 | cluster numbers out of bounds */ | ||
851 | if (c < 2 || c > fat_bpb->dataclusters + 1) | ||
852 | continue; | ||
853 | |||
854 | DEBUGF("%s(%lx) == %x\n", __func__, startcluster, c); | ||
855 | |||
856 | fat_bpb->fsinfo.nextfree = c; | ||
857 | return c; | ||
858 | } | ||
713 | } | 859 | } |
860 | |||
861 | offset = 0; | ||
714 | } | 862 | } |
715 | fce->dirty = false; | 863 | |
864 | DEBUGF("%s(%lx) == 0\n", __func__, startcluster); | ||
865 | return 0; /* 0 is an illegal cluster number */ | ||
716 | } | 866 | } |
717 | 867 | ||
718 | /* Note: The returned pointer is only safely valid until the next | 868 | static int update_fat_entry16(struct bpb *fat_bpb, unsigned long entry, |
719 | task switch! (Any subsequent ata read/write may yield.) */ | 869 | unsigned long val) |
720 | static void *cache_fat_sector(IF_MV(struct bpb* fat_bpb,) | ||
721 | long fatsector, bool dirty) | ||
722 | { | 870 | { |
723 | #ifndef HAVE_MULTIVOLUME | 871 | unsigned long sector = entry / CLUSTERS_PER_FAT16_SECTOR; |
724 | struct bpb* fat_bpb = &fat_bpbs[0]; | 872 | unsigned long offset = entry % CLUSTERS_PER_FAT16_SECTOR; |
725 | #endif | ||
726 | long secnum = fatsector + fat_bpb->bpb_rsvdseccnt; | ||
727 | int cache_index = secnum & FAT_CACHE_MASK; | ||
728 | struct fat_cache_entry *fce = &fat_cache[cache_index]; | ||
729 | unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0]; | ||
730 | int rc; | ||
731 | 873 | ||
732 | mutex_lock(&cache_mutex); /* make changes atomic */ | 874 | val &= 0xFFFF; |
733 | 875 | ||
734 | /* Delete the cache entry if it isn't the sector we want */ | 876 | DEBUGF("%s(entry:%lx,val:%lx)\n", __func__, entry, val); |
735 | if(fce->inuse && (fce->secnum != secnum | 877 | |
736 | #ifdef HAVE_MULTIVOLUME | 878 | if (entry == val) |
737 | || fce->fat_vol != fat_bpb | 879 | panicf("Creating FAT16 loop: %lx,%lx\n", entry, val); |
738 | #endif | 880 | |
739 | )) | 881 | if (entry < 2) |
882 | panicf("Updating reserved FAT16 entry %lu\n", entry); | ||
883 | |||
884 | dc_lock_cache(); | ||
885 | |||
886 | int16_t *sec = cache_sector(fat_bpb, sector + fat_bpb->fatrgnstart); | ||
887 | if (!sec) | ||
740 | { | 888 | { |
741 | /* Write back if it is dirty */ | 889 | dc_unlock_cache(); |
742 | if(fce->dirty) | 890 | DEBUGF("Could not cache sector %u\n", sector); |
743 | { | 891 | return -1; |
744 | flush_fat_sector(fce, sectorbuf); | ||
745 | } | ||
746 | fce->inuse = false; | ||
747 | } | 892 | } |
748 | 893 | ||
749 | /* Load the sector if it is not cached */ | 894 | uint16_t curval = letoh16(sec[offset]); |
750 | if(!fce->inuse) | 895 | |
896 | if (val) | ||
751 | { | 897 | { |
752 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) | 898 | /* being allocated */ |
753 | secnum + fat_bpb->startsector,1, | 899 | if (curval == 0x0000 && fat_bpb->fsinfo.freecount > 0) |
754 | sectorbuf); | 900 | fat_bpb->fsinfo.freecount--; |
755 | if(rc < 0) | ||
756 | { | ||
757 | DEBUGF( "cache_fat_sector() - Could not read sector %ld" | ||
758 | " (error %d)\n", secnum, rc); | ||
759 | mutex_unlock(&cache_mutex); | ||
760 | return NULL; | ||
761 | } | ||
762 | fce->inuse = true; | ||
763 | fce->secnum = secnum; | ||
764 | #ifdef HAVE_MULTIVOLUME | ||
765 | fce->fat_vol = fat_bpb; | ||
766 | #endif | ||
767 | } | 901 | } |
768 | if (dirty) | 902 | else |
769 | fce->dirty = true; /* dirt remains, sticky until flushed */ | 903 | { |
770 | mutex_unlock(&cache_mutex); | 904 | /* being freed */ |
771 | return sectorbuf; | 905 | if (curval != 0x0000) |
906 | fat_bpb->fsinfo.freecount++; | ||
907 | } | ||
908 | |||
909 | DEBUGF("%lu free clusters\n", (unsigned long)fat_bpb->fsinfo.freecount); | ||
910 | |||
911 | sec[offset] = htole16(val); | ||
912 | dc_dirty_buf(sec); | ||
913 | |||
914 | dc_unlock_cache(); | ||
915 | return 0; | ||
772 | } | 916 | } |
773 | 917 | ||
774 | static unsigned long find_free_cluster(IF_MV(struct bpb* fat_bpb,) | 918 | static void fat_recalc_free_internal16(struct bpb *fat_bpb) |
775 | unsigned long startcluster) | ||
776 | { | 919 | { |
777 | #ifndef HAVE_MULTIVOLUME | 920 | unsigned long free = 0; |
778 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
779 | #endif | ||
780 | unsigned long sector; | ||
781 | unsigned long offset; | ||
782 | unsigned long i; | ||
783 | 921 | ||
784 | #ifdef HAVE_FAT16SUPPORT | 922 | for (unsigned long i = 0; i < fat_bpb->fatsize; i++) |
785 | if (fat_bpb->is_fat16) | ||
786 | { | 923 | { |
787 | sector = startcluster / CLUSTERS_PER_FAT16_SECTOR; | 924 | uint16_t *sec = cache_sector(fat_bpb, i + fat_bpb->fatrgnstart); |
788 | offset = startcluster % CLUSTERS_PER_FAT16_SECTOR; | 925 | if (!sec) |
926 | break; | ||
789 | 927 | ||
790 | for (i = 0; i<fat_bpb->fatsize; i++) { | 928 | for (unsigned long j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) |
791 | unsigned int j; | 929 | { |
792 | unsigned int nr = (i + sector) % fat_bpb->fatsize; | 930 | unsigned long c = i * CLUSTERS_PER_FAT16_SECTOR + j; |
793 | uint16_t* fat = cache_fat_sector(IF_MV(fat_bpb,) nr, false); | 931 | |
794 | if ( !fat ) | 932 | if (c < 2 || c > fat_bpb->dataclusters + 1) /* nr 0 is unused */ |
795 | break; | 933 | continue; |
796 | for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { | 934 | |
797 | int k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR; | 935 | if (letoh16(sec[j]) != 0x0000) |
798 | if (letoh16(fat[k]) == 0x0000) { | 936 | continue; |
799 | unsigned int c = nr * CLUSTERS_PER_FAT16_SECTOR + k; | 937 | |
800 | /* Ignore the reserved clusters 0 & 1, and also | 938 | free++; |
801 | cluster numbers out of bounds */ | 939 | if (fat_bpb->fsinfo.nextfree == 0xffffffff) |
802 | if ( c < 2 || c > fat_bpb->dataclusters+1 ) | 940 | fat_bpb->fsinfo.nextfree = c; |
803 | continue; | ||
804 | LDEBUGF("find_free_cluster(%lx) == %x\n",startcluster,c); | ||
805 | fat_bpb->fsinfo.nextfree = c; | ||
806 | return c; | ||
807 | } | ||
808 | } | ||
809 | offset = 0; | ||
810 | } | 941 | } |
811 | } | 942 | } |
812 | else | ||
813 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
814 | { | ||
815 | sector = startcluster / CLUSTERS_PER_FAT_SECTOR; | ||
816 | offset = startcluster % CLUSTERS_PER_FAT_SECTOR; | ||
817 | 943 | ||
818 | for (i = 0; i<fat_bpb->fatsize; i++) { | 944 | fat_bpb->fsinfo.freecount = free; |
819 | unsigned int j; | 945 | } |
820 | unsigned long nr = (i + sector) % fat_bpb->fatsize; | 946 | #endif /* HAVE_FAT16SUPPORT */ |
821 | uint32_t* fat = cache_fat_sector(IF_MV(fat_bpb,) nr, false); | 947 | |
822 | if ( !fat ) | 948 | static void update_fsinfo32(struct bpb *fat_bpb) |
823 | break; | 949 | { |
824 | for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { | 950 | uint8_t *fsinfo = cache_sector(fat_bpb, fat_bpb->bpb_fsinfo); |
825 | int k = (j + offset) % CLUSTERS_PER_FAT_SECTOR; | 951 | if (!fsinfo) |
826 | if (!(letoh32(fat[k]) & 0x0fffffff)) { | 952 | { |
827 | unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k; | 953 | DEBUGF("%s() - Couldn't read FSInfo" |
828 | /* Ignore the reserved clusters 0 & 1, and also | 954 | " (err code %d)", __func__, rc); |
829 | cluster numbers out of bounds */ | 955 | return; |
830 | if ( c < 2 || c > fat_bpb->dataclusters+1 ) | ||
831 | continue; | ||
832 | LDEBUGF("find_free_cluster(%lx) == %lx\n",startcluster,c); | ||
833 | fat_bpb->fsinfo.nextfree = c; | ||
834 | return c; | ||
835 | } | ||
836 | } | ||
837 | offset = 0; | ||
838 | } | ||
839 | } | 956 | } |
840 | 957 | ||
841 | LDEBUGF("find_free_cluster(%lx) == 0\n",startcluster); | 958 | INT322BYTES(fsinfo, FSINFO_FREECOUNT, fat_bpb->fsinfo.freecount); |
842 | return 0; /* 0 is an illegal cluster number */ | 959 | INT322BYTES(fsinfo, FSINFO_NEXTFREE, fat_bpb->fsinfo.nextfree); |
960 | dc_dirty_buf(fsinfo); | ||
843 | } | 961 | } |
844 | 962 | ||
845 | static int update_fat_entry(IF_MV(struct bpb* fat_bpb,) unsigned long entry, | 963 | static long get_next_cluster32(struct bpb *fat_bpb, long startcluster) |
846 | unsigned long val) | ||
847 | { | 964 | { |
848 | #ifndef HAVE_MULTIVOLUME | 965 | unsigned long entry = startcluster; |
849 | struct bpb* fat_bpb = &fat_bpbs[0]; | 966 | unsigned long sector = entry / CLUSTERS_PER_FAT_SECTOR; |
850 | #endif | 967 | unsigned long offset = entry % CLUSTERS_PER_FAT_SECTOR; |
851 | #ifdef HAVE_FAT16SUPPORT | 968 | |
852 | if (fat_bpb->is_fat16) | 969 | dc_lock_cache(); |
970 | |||
971 | uint32_t *sec = cache_sector(fat_bpb, sector + fat_bpb->fatrgnstart); | ||
972 | if (!sec) | ||
853 | { | 973 | { |
854 | int sector = entry / CLUSTERS_PER_FAT16_SECTOR; | 974 | dc_unlock_cache(); |
855 | int offset = entry % CLUSTERS_PER_FAT16_SECTOR; | 975 | DEBUGF("%s: Could not cache sector %d\n", __func__, sector); |
856 | unsigned short* sec; | 976 | return -1; |
977 | } | ||
857 | 978 | ||
858 | val &= 0xFFFF; | 979 | long next = letoh32(sec[offset]) & 0x0fffffff; |
859 | 980 | ||
860 | LDEBUGF("update_fat_entry(%lx,%lx)\n",entry,val); | 981 | /* is this last cluster in chain? */ |
982 | if (next >= FAT_EOF_MARK) | ||
983 | next = 0; | ||
861 | 984 | ||
862 | if (entry==val) | 985 | dc_unlock_cache(); |
863 | panicf("Creating FAT loop: %lx,%lx\n",entry,val); | 986 | return next; |
987 | } | ||
864 | 988 | ||
865 | if ( entry < 2 ) | 989 | static long find_free_cluster32(struct bpb *fat_bpb, long startcluster) |
866 | panicf("Updating reserved FAT entry %ld.\n",entry); | 990 | { |
991 | unsigned long entry = startcluster; | ||
992 | unsigned long sector = entry / CLUSTERS_PER_FAT_SECTOR; | ||
993 | unsigned long offset = entry % CLUSTERS_PER_FAT_SECTOR; | ||
867 | 994 | ||
868 | sec = cache_fat_sector(IF_MV(fat_bpb,) sector, true); | 995 | for (unsigned long i = 0; i < fat_bpb->fatsize; i++) |
996 | { | ||
997 | unsigned long nr = (i + sector) % fat_bpb->fatsize; | ||
998 | uint32_t *sec = cache_sector(fat_bpb, nr + fat_bpb->fatrgnstart); | ||
869 | if (!sec) | 999 | if (!sec) |
1000 | break; | ||
1001 | |||
1002 | for (unsigned long j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) | ||
870 | { | 1003 | { |
871 | DEBUGF( "update_fat_entry() - Could not cache sector %d\n", sector); | 1004 | unsigned long k = (j + offset) % CLUSTERS_PER_FAT_SECTOR; |
872 | return -1; | ||
873 | } | ||
874 | 1005 | ||
875 | if ( val ) { | 1006 | if (!(letoh32(sec[k]) & 0x0fffffff)) |
876 | if (letoh16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0) | 1007 | { |
877 | fat_bpb->fsinfo.freecount--; | 1008 | unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k; |
878 | } | 1009 | /* Ignore the reserved clusters 0 & 1, and also |
879 | else { | 1010 | cluster numbers out of bounds */ |
880 | if (letoh16(sec[offset])) | 1011 | if (c < 2 || c > fat_bpb->dataclusters + 1) |
881 | fat_bpb->fsinfo.freecount++; | 1012 | continue; |
1013 | |||
1014 | DEBUGF("%s(%lx) == %lx\n", __func__, startcluster, c); | ||
1015 | |||
1016 | fat_bpb->fsinfo.nextfree = c; | ||
1017 | return c; | ||
1018 | } | ||
882 | } | 1019 | } |
883 | 1020 | ||
884 | LDEBUGF("update_fat_entry: %lu free clusters\n", | 1021 | offset = 0; |
885 | (unsigned long)fat_bpb->fsinfo.freecount); | 1022 | } |
1023 | |||
1024 | DEBUGF("%s(%lx) == 0\n", __func__, startcluster); | ||
1025 | return 0; /* 0 is an illegal cluster number */ | ||
1026 | } | ||
1027 | |||
1028 | static int update_fat_entry32(struct bpb *fat_bpb, unsigned long entry, | ||
1029 | unsigned long val) | ||
1030 | { | ||
1031 | unsigned long sector = entry / CLUSTERS_PER_FAT_SECTOR; | ||
1032 | unsigned long offset = entry % CLUSTERS_PER_FAT_SECTOR; | ||
1033 | |||
1034 | DEBUGF("%s(entry:%lx,val:%lx)\n", __func__, entry, val); | ||
1035 | |||
1036 | if (entry == val) | ||
1037 | panicf("Creating FAT32 loop: %lx,%lx\n", entry, val); | ||
1038 | |||
1039 | if (entry < 2) | ||
1040 | panicf("Updating reserved FAT32 entry %lu\n", entry); | ||
1041 | |||
1042 | dc_lock_cache(); | ||
1043 | |||
1044 | uint32_t *sec = cache_sector(fat_bpb, sector + fat_bpb->fatrgnstart); | ||
1045 | if (!sec) | ||
1046 | { | ||
1047 | dc_unlock_cache(); | ||
1048 | DEBUGF("Could not cache sector %u\n", sector); | ||
1049 | return -1; | ||
1050 | } | ||
886 | 1051 | ||
887 | sec[offset] = htole16(val); | 1052 | uint32_t curval = letoh32(sec[offset]); |
1053 | |||
1054 | if (val) | ||
1055 | { | ||
1056 | /* being allocated */ | ||
1057 | if (!(curval & 0x0fffffff) && fat_bpb->fsinfo.freecount > 0) | ||
1058 | fat_bpb->fsinfo.freecount--; | ||
888 | } | 1059 | } |
889 | else | 1060 | else |
890 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
891 | { | 1061 | { |
892 | long sector = entry / CLUSTERS_PER_FAT_SECTOR; | 1062 | /* being freed */ |
893 | int offset = entry % CLUSTERS_PER_FAT_SECTOR; | 1063 | if (curval & 0x0fffffff) |
894 | uint32_t* sec; | 1064 | fat_bpb->fsinfo.freecount++; |
1065 | } | ||
1066 | |||
1067 | DEBUGF("%lu free clusters\n", (unsigned long)fat_bpb->fsinfo.freecount); | ||
895 | 1068 | ||
896 | LDEBUGF("update_fat_entry(%lx,%lx)\n",entry,val); | 1069 | /* don't change top 4 bits */ |
1070 | sec[offset] = htole32((curval & 0xf0000000) | (val & 0x0fffffff)); | ||
1071 | dc_dirty_buf(sec); | ||
897 | 1072 | ||
898 | if (entry==val) | 1073 | dc_unlock_cache(); |
899 | panicf("Creating FAT loop: %lx,%lx\n",entry,val); | 1074 | return 0; |
1075 | } | ||
900 | 1076 | ||
901 | if ( entry < 2 ) | 1077 | static void fat_recalc_free_internal32(struct bpb *fat_bpb) |
902 | panicf("Updating reserved FAT entry %ld.\n",entry); | 1078 | { |
1079 | unsigned long free = 0; | ||
903 | 1080 | ||
904 | sec = cache_fat_sector(IF_MV(fat_bpb,) sector, true); | 1081 | for (unsigned long i = 0; i < fat_bpb->fatsize; i++) |
1082 | { | ||
1083 | uint32_t *sec = cache_sector(fat_bpb, i + fat_bpb->fatrgnstart); | ||
905 | if (!sec) | 1084 | if (!sec) |
1085 | break; | ||
1086 | |||
1087 | for (unsigned long j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) | ||
906 | { | 1088 | { |
907 | DEBUGF("update_fat_entry() - Could not cache sector %ld\n", sector); | 1089 | unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j; |
908 | return -1; | ||
909 | } | ||
910 | 1090 | ||
911 | if ( val ) { | 1091 | if (c < 2 || c > fat_bpb->dataclusters + 1) /* nr 0 is unused */ |
912 | if (!(letoh32(sec[offset]) & 0x0fffffff) && | 1092 | continue; |
913 | fat_bpb->fsinfo.freecount > 0) | ||
914 | fat_bpb->fsinfo.freecount--; | ||
915 | } | ||
916 | else { | ||
917 | if (letoh32(sec[offset]) & 0x0fffffff) | ||
918 | fat_bpb->fsinfo.freecount++; | ||
919 | } | ||
920 | 1093 | ||
921 | LDEBUGF("update_fat_entry: %ld free clusters\n", | 1094 | if (letoh32(sec[j]) & 0x0fffffff) |
922 | (unsigned long)fat_bpb->fsinfo.freecount); | 1095 | continue; |
923 | 1096 | ||
924 | /* don't change top 4 bits */ | 1097 | free++; |
925 | sec[offset] &= htole32(0xf0000000); | 1098 | if (fat_bpb->fsinfo.nextfree == 0xffffffff) |
926 | sec[offset] |= htole32(val & 0x0fffffff); | 1099 | fat_bpb->fsinfo.nextfree = c; |
1100 | } | ||
927 | } | 1101 | } |
928 | 1102 | ||
929 | return 0; | 1103 | fat_bpb->fsinfo.freecount = free; |
1104 | update_fsinfo32(fat_bpb); | ||
930 | } | 1105 | } |
931 | 1106 | ||
932 | static long read_fat_entry(IF_MV(struct bpb* fat_bpb,) unsigned long entry) | 1107 | static int fat_mount_internal(struct bpb *fat_bpb) |
933 | { | 1108 | { |
1109 | int rc; | ||
1110 | /* safe to grab buffer: bpb is irrelevant and no sector will be cached | ||
1111 | for this volume since it isn't mounted */ | ||
1112 | uint8_t * const buf = dc_get_buffer(); | ||
1113 | if (!buf) | ||
1114 | FAT_ERROR(-1); | ||
1115 | |||
1116 | /* Read the sector */ | ||
1117 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) fat_bpb->startsector, | ||
1118 | 1, buf); | ||
1119 | if(rc) | ||
1120 | { | ||
1121 | DEBUGF("%s() - Couldn't read BPB" | ||
1122 | " (err %d)\n", __func__, rc); | ||
1123 | FAT_ERROR(rc * 10 - 2); | ||
1124 | } | ||
1125 | |||
1126 | fat_bpb->bpb_bytspersec = BYTES2INT16(buf, BPB_BYTSPERSEC); | ||
1127 | unsigned long secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE; | ||
1128 | /* Sanity check is performed later */ | ||
1129 | |||
1130 | fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS]; | ||
1131 | fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf, BPB_RSVDSECCNT); | ||
1132 | fat_bpb->bpb_numfats = buf[BPB_NUMFATS]; | ||
1133 | fat_bpb->bpb_media = buf[BPB_MEDIA]; | ||
1134 | fat_bpb->bpb_fatsz16 = secmult * BYTES2INT16(buf, BPB_FATSZ16); | ||
1135 | fat_bpb->bpb_fatsz32 = secmult * BYTES2INT32(buf, BPB_FATSZ32); | ||
1136 | fat_bpb->bpb_totsec16 = secmult * BYTES2INT16(buf, BPB_TOTSEC16); | ||
1137 | fat_bpb->bpb_totsec32 = secmult * BYTES2INT32(buf, BPB_TOTSEC32); | ||
1138 | fat_bpb->last_word = BYTES2INT16(buf, BPB_LAST_WORD); | ||
1139 | |||
1140 | /* calculate a few commonly used values */ | ||
1141 | if (fat_bpb->bpb_fatsz16 != 0) | ||
1142 | fat_bpb->fatsize = fat_bpb->bpb_fatsz16; | ||
1143 | else | ||
1144 | fat_bpb->fatsize = fat_bpb->bpb_fatsz32; | ||
1145 | |||
1146 | fat_bpb->fatrgnstart = fat_bpb->bpb_rsvdseccnt; | ||
1147 | fat_bpb->fatrgnend = fat_bpb->bpb_rsvdseccnt + fat_bpb->fatsize; | ||
1148 | |||
1149 | if (fat_bpb->bpb_totsec16 != 0) | ||
1150 | fat_bpb->totalsectors = fat_bpb->bpb_totsec16; | ||
1151 | else | ||
1152 | fat_bpb->totalsectors = fat_bpb->bpb_totsec32; | ||
1153 | |||
1154 | unsigned int rootdirsectors = 0; | ||
1155 | #ifdef HAVE_FAT16SUPPORT | ||
1156 | fat_bpb->bpb_rootentcnt = BYTES2INT16(buf, BPB_ROOTENTCNT); | ||
1157 | |||
1158 | if (!fat_bpb->bpb_bytspersec) | ||
1159 | FAT_ERROR(-3); | ||
1160 | |||
1161 | rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE | ||
1162 | + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec); | ||
1163 | #endif /* HAVE_FAT16SUPPORT */ | ||
1164 | |||
1165 | fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt | ||
1166 | + fat_bpb->bpb_numfats * fat_bpb->fatsize | ||
1167 | + rootdirsectors; | ||
1168 | |||
1169 | /* Determine FAT type */ | ||
1170 | unsigned long datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector; | ||
1171 | |||
1172 | if (!fat_bpb->bpb_secperclus) | ||
1173 | FAT_ERROR(-4); | ||
1174 | |||
1175 | fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus; | ||
1176 | |||
1177 | #ifdef TEST_FAT | ||
1178 | /* | ||
1179 | we are sometimes testing with "illegally small" fat32 images, | ||
1180 | so we don't use the proper fat32 test case for test code | ||
1181 | */ | ||
1182 | if (fat_bpb->bpb_fatsz16) | ||
1183 | #else /* !TEST_FAT */ | ||
1184 | if (fat_bpb->dataclusters < 65525) | ||
1185 | #endif /* TEST_FAT */ | ||
1186 | { /* FAT16 */ | ||
1187 | #ifdef HAVE_FAT16SUPPORT | ||
1188 | fat_bpb->is_fat16 = true; | ||
1189 | if (fat_bpb->dataclusters < 4085) | ||
1190 | { /* FAT12 */ | ||
1191 | DEBUGF("This is FAT12. Go away!\n"); | ||
1192 | FAT_ERROR(-5); | ||
1193 | } | ||
1194 | #else /* !HAVE_FAT16SUPPORT */ | ||
1195 | DEBUGF("This is not FAT32. Go away!\n"); | ||
1196 | FAT_ERROR(-6); | ||
1197 | #endif /* HAVE_FAT16SUPPORT */ | ||
1198 | } | ||
1199 | |||
934 | #ifdef HAVE_FAT16SUPPORT | 1200 | #ifdef HAVE_FAT16SUPPORT |
935 | #ifndef HAVE_MULTIVOLUME | ||
936 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
937 | #endif | ||
938 | if (fat_bpb->is_fat16) | 1201 | if (fat_bpb->is_fat16) |
939 | { | 1202 | { |
940 | int sector = entry / CLUSTERS_PER_FAT16_SECTOR; | 1203 | /* FAT16 specific part of BPB */ |
941 | int offset = entry % CLUSTERS_PER_FAT16_SECTOR; | 1204 | fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt |
942 | unsigned short* sec; | 1205 | + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16; |
1206 | long dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1) | ||
1207 | / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */ | ||
1208 | /* I assign negative pseudo cluster numbers for the root directory, | ||
1209 | their range is counted upward until -1. */ | ||
1210 | fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data */ | ||
1211 | fat_bpb->rootdirsectornum = dirclusters * fat_bpb->bpb_secperclus | ||
1212 | - rootdirsectors; | ||
1213 | } | ||
1214 | else | ||
1215 | #endif /* HAVE_FAT16SUPPORT */ | ||
1216 | { | ||
1217 | /* FAT32 specific part of BPB */ | ||
1218 | fat_bpb->bpb_rootclus = BYTES2INT32(buf, BPB_ROOTCLUS); | ||
1219 | fat_bpb->bpb_fsinfo = secmult * BYTES2INT16(buf, BPB_FSINFO); | ||
1220 | fat_bpb->rootdirsector = cluster2sec(fat_bpb, fat_bpb->bpb_rootclus); | ||
1221 | } | ||
943 | 1222 | ||
944 | sec = cache_fat_sector(IF_MV(fat_bpb,) sector, false); | 1223 | rc = bpb_is_sane(fat_bpb); |
945 | if (!sec) | 1224 | if (rc < 0) |
946 | { | 1225 | { |
947 | DEBUGF( "read_fat_entry() - Could not cache sector %d\n", sector); | 1226 | DEBUGF("%s: BPB is insane!\n", __func__); |
948 | return -1; | 1227 | FAT_ERROR(rc * 10 - 7); |
949 | } | 1228 | } |
950 | 1229 | ||
951 | return letoh16(sec[offset]); | 1230 | #ifdef HAVE_FAT16SUPPORT |
1231 | if (fat_bpb->is_fat16) | ||
1232 | { | ||
1233 | fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc later */ | ||
1234 | fat_bpb->fsinfo.nextfree = 0xffffffff; | ||
952 | } | 1235 | } |
953 | else | 1236 | else |
954 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | 1237 | #endif /* HAVE_FAT16SUPPORT */ |
955 | { | 1238 | { |
956 | long sector = entry / CLUSTERS_PER_FAT_SECTOR; | 1239 | /* Read the fsinfo sector */ |
957 | int offset = entry % CLUSTERS_PER_FAT_SECTOR; | 1240 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) |
958 | uint32_t* sec; | 1241 | fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1, buf); |
959 | 1242 | ||
960 | sec = cache_fat_sector(IF_MV(fat_bpb,) sector, false); | 1243 | if (rc < 0) |
961 | if (!sec) | ||
962 | { | 1244 | { |
963 | DEBUGF( "read_fat_entry() - Could not cache sector %ld\n", sector); | 1245 | DEBUGF("%s() - Couldn't read FSInfo" |
964 | return -1; | 1246 | " (error code %d)\n", __func__, rc); |
1247 | FAT_ERROR(rc * 10 - 8); | ||
965 | } | 1248 | } |
966 | 1249 | ||
967 | return letoh32(sec[offset]) & 0x0fffffff; | 1250 | fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT); |
1251 | fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); | ||
968 | } | 1252 | } |
969 | } | ||
970 | |||
971 | static long get_next_cluster(IF_MV(struct bpb* fat_bpb,) long cluster) | ||
972 | { | ||
973 | long next_cluster; | ||
974 | long eof_mark = FAT_EOF_MARK; | ||
975 | 1253 | ||
976 | #ifdef HAVE_FAT16SUPPORT | 1254 | #ifdef HAVE_FAT16SUPPORT |
977 | #ifndef HAVE_MULTIVOLUME | 1255 | /* Fix up calls that change per FAT type */ |
978 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
979 | #endif | ||
980 | if (fat_bpb->is_fat16) | 1256 | if (fat_bpb->is_fat16) |
981 | { | 1257 | { |
982 | eof_mark &= 0xFFFF; /* only 16 bit */ | 1258 | BPB_FN_SET16(fat_bpb, get_next_cluster); |
983 | if (cluster < 0) /* FAT16 root dir */ | 1259 | BPB_FN_SET16(fat_bpb, find_free_cluster); |
984 | return cluster + 1; /* don't use the FAT */ | 1260 | BPB_FN_SET16(fat_bpb, update_fat_entry); |
1261 | BPB_FN_SET16(fat_bpb, fat_recalc_free_internal); | ||
985 | } | 1262 | } |
986 | #endif | ||
987 | next_cluster = read_fat_entry(IF_MV(fat_bpb,) cluster); | ||
988 | |||
989 | /* is this last cluster in chain? */ | ||
990 | if ( next_cluster >= eof_mark ) | ||
991 | return 0; | ||
992 | else | 1263 | else |
993 | return next_cluster; | 1264 | { |
1265 | BPB_FN_SET32(fat_bpb, get_next_cluster); | ||
1266 | BPB_FN_SET32(fat_bpb, find_free_cluster); | ||
1267 | BPB_FN_SET32(fat_bpb, update_fat_entry); | ||
1268 | BPB_FN_SET32(fat_bpb, fat_recalc_free_internal); | ||
1269 | } | ||
1270 | #endif /* HAVE_FAT16SUPPORT */ | ||
1271 | |||
1272 | rc = 0; | ||
1273 | fat_error: | ||
1274 | dc_release_buffer(buf); | ||
1275 | return rc; | ||
994 | } | 1276 | } |
995 | 1277 | ||
996 | static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)) | 1278 | static union raw_dirent * cache_direntry(struct bpb *fat_bpb, |
1279 | struct fat_filestr *filestr, | ||
1280 | unsigned int entry) | ||
997 | { | 1281 | { |
998 | #ifndef HAVE_MULTIVOLUME | 1282 | filestr->eof = false; |
999 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
1000 | #endif | ||
1001 | uint32_t* intptr; | ||
1002 | int rc; | ||
1003 | 1283 | ||
1004 | #ifdef HAVE_FAT16SUPPORT | 1284 | if (entry >= MAX_DIRENTRIES) |
1005 | if (fat_bpb->is_fat16) | 1285 | { |
1006 | return 0; /* FAT16 has no FsInfo */ | 1286 | DEBUGF("%s() - Dir is too large (entry %u)\n", __func__, entry); |
1007 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | 1287 | return NULL; |
1288 | } | ||
1008 | 1289 | ||
1009 | unsigned char* fsinfo = fat_get_sector_buffer(); | 1290 | unsigned long sector = entry / DIR_ENTRIES_PER_SECTOR; |
1010 | /* update fsinfo */ | 1291 | |
1011 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) | 1292 | if (fat_query_sectornum(filestr) != sector + 1) |
1012 | fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo); | ||
1013 | if (rc < 0) | ||
1014 | { | 1293 | { |
1015 | fat_release_sector_buffer(); | 1294 | int rc = fat_seek(filestr, sector + 1); |
1016 | DEBUGF( "update_fsinfo() - Couldn't read FSInfo (error code %d)", rc); | 1295 | if (rc < 0) |
1017 | return rc * 10 - 1; | 1296 | { |
1297 | if (rc == FAT_SEEK_EOF) | ||
1298 | { | ||
1299 | DEBUGF("%s() - End of dir (entry %u)\n", __func__, entry); | ||
1300 | fat_seek(filestr, sector); | ||
1301 | filestr->eof = true; | ||
1302 | } | ||
1303 | |||
1304 | return NULL; | ||
1305 | } | ||
1018 | } | 1306 | } |
1019 | intptr = (uint32_t*)&(fsinfo[FSINFO_FREECOUNT]); | ||
1020 | *intptr = htole32(fat_bpb->fsinfo.freecount); | ||
1021 | 1307 | ||
1022 | intptr = (uint32_t*)&(fsinfo[FSINFO_NEXTFREE]); | 1308 | union raw_dirent *ent = cache_sector(fat_bpb, filestr->lastsector); |
1023 | *intptr = htole32(fat_bpb->fsinfo.nextfree); | ||
1024 | 1309 | ||
1025 | rc = storage_write_sectors(IF_MD(fat_bpb->drive,) | 1310 | if (ent) |
1026 | fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo); | 1311 | ent += entry % DIR_ENTRIES_PER_SECTOR; |
1027 | fat_release_sector_buffer(); | 1312 | |
1028 | if (rc < 0) | 1313 | return ent; |
1314 | } | ||
1315 | |||
1316 | static long next_write_cluster(struct bpb *fat_bpb, long oldcluster) | ||
1317 | { | ||
1318 | DEBUGF("%s(old:%lx)\n", __func__, oldcluster); | ||
1319 | |||
1320 | long cluster = 0; | ||
1321 | |||
1322 | /* cluster already allocated? */ | ||
1323 | if (oldcluster) | ||
1324 | cluster = get_next_cluster(fat_bpb, oldcluster); | ||
1325 | |||
1326 | if (!cluster) | ||
1029 | { | 1327 | { |
1030 | DEBUGF( "update_fsinfo() - Couldn't write FSInfo (error code %d)", rc); | 1328 | /* passed end of existing entries and now need to append */ |
1031 | return rc * 10 - 2; | 1329 | #ifdef HAVE_FAT16SUPPORT |
1330 | if (UNLIKELY(oldcluster < 0)) | ||
1331 | return 0; /* negative, pseudo-cluster of the root dir */ | ||
1332 | /* impossible to append something to the root */ | ||
1333 | #endif /* HAVE_FAT16SUPPORT */ | ||
1334 | |||
1335 | dc_lock_cache(); | ||
1336 | |||
1337 | long findstart = oldcluster > 0 ? | ||
1338 | oldcluster + 1 : (long)fat_bpb->fsinfo.nextfree; | ||
1339 | |||
1340 | cluster = find_free_cluster(fat_bpb, findstart); | ||
1341 | |||
1342 | if (cluster) | ||
1343 | { | ||
1344 | /* create the cluster chain */ | ||
1345 | if (oldcluster) | ||
1346 | update_fat_entry(fat_bpb, oldcluster, cluster); | ||
1347 | |||
1348 | update_fat_entry(fat_bpb, cluster, FAT_EOF_MARK); | ||
1349 | } | ||
1350 | else | ||
1351 | { | ||
1352 | #ifdef TEST_FAT | ||
1353 | if (fat_bpb->fsinfo.freecount > 0) | ||
1354 | panicf("There is free space, but find_free_cluster() " | ||
1355 | "didn't find it!\n"); | ||
1356 | #endif | ||
1357 | DEBUGF("Disk full!\n"); | ||
1358 | } | ||
1359 | |||
1360 | dc_unlock_cache(); | ||
1032 | } | 1361 | } |
1033 | 1362 | ||
1034 | return 0; | 1363 | return cluster; |
1035 | } | 1364 | } |
1036 | 1365 | ||
1037 | static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)) | 1366 | /* extend dir file by one cluster and clear it; file position should be at the |
1367 | current last cluster before calling and size of dir checked */ | ||
1368 | static int fat_extend_dir(struct bpb *fat_bpb, struct fat_filestr *dirstr) | ||
1038 | { | 1369 | { |
1039 | int i; | 1370 | DEBUGF("%s()\n", __func__); |
1371 | |||
1040 | int rc; | 1372 | int rc; |
1041 | unsigned char *sec; | ||
1042 | LDEBUGF("flush_fat()\n"); | ||
1043 | 1373 | ||
1044 | mutex_lock(&cache_mutex); | 1374 | long cluster = dirstr->lastcluster; |
1045 | for(i = 0;i < FAT_CACHE_SIZE;i++) | 1375 | long newcluster = next_write_cluster(fat_bpb, cluster); |
1376 | |||
1377 | if (!newcluster) | ||
1046 | { | 1378 | { |
1047 | struct fat_cache_entry *fce = &fat_cache[i]; | 1379 | /* no more room or something went wrong */ |
1048 | if(fce->inuse | 1380 | DEBUGF("Out of space\n"); |
1049 | #ifdef HAVE_MULTIVOLUME | 1381 | FAT_ERROR(FAT_RC_ENOSPC); |
1050 | && fce->fat_vol == fat_bpb | 1382 | } |
1051 | #endif | 1383 | |
1052 | && fce->dirty) | 1384 | /* we must clear whole clusters */ |
1385 | unsigned long startsector = cluster2sec(fat_bpb, newcluster); | ||
1386 | unsigned long sector = startsector - 1; | ||
1387 | |||
1388 | if (startsector == 0) | ||
1389 | FAT_ERROR(-1); | ||
1390 | |||
1391 | for (unsigned int i = 0; i < fat_bpb->bpb_secperclus; i++) | ||
1392 | { | ||
1393 | dc_lock_cache(); | ||
1394 | |||
1395 | void *sec = cache_sector_buffer(IF_MV(fat_bpb,) ++sector); | ||
1396 | if (!sec) | ||
1053 | { | 1397 | { |
1054 | sec = fat_cache_sectors[i]; | 1398 | dc_unlock_cache(); |
1055 | flush_fat_sector(fce, sec); | 1399 | DEBUGF("Cannot clear cluster %ld\n", newcluster); |
1400 | update_fat_entry(fat_bpb, newcluster, 0); | ||
1401 | FAT_ERROR(-2); | ||
1056 | } | 1402 | } |
1403 | |||
1404 | memset(sec, 0, SECTOR_SIZE); | ||
1405 | dc_dirty_buf(sec); | ||
1406 | dc_unlock_cache(); | ||
1057 | } | 1407 | } |
1058 | mutex_unlock(&cache_mutex); | ||
1059 | 1408 | ||
1060 | rc = update_fsinfo(IF_MV(fat_bpb)); | 1409 | if (!dirstr->fatfilep->firstcluster) |
1061 | if (rc < 0) | 1410 | dirstr->fatfilep->firstcluster = newcluster; |
1062 | return rc * 10 - 3; | ||
1063 | 1411 | ||
1064 | return 0; | 1412 | dirstr->lastcluster = newcluster; |
1413 | dirstr->lastsector = sector; | ||
1414 | dirstr->clusternum++; | ||
1415 | dirstr->sectornum = sector - startsector; | ||
1416 | dirstr->eof = false; | ||
1417 | |||
1418 | rc = 0; | ||
1419 | fat_error: | ||
1420 | return rc; | ||
1065 | } | 1421 | } |
1066 | 1422 | ||
1067 | static void fat_time(unsigned short* date, | 1423 | static void fat_open_internal(IF_MV(int volume,) long startcluster, |
1068 | unsigned short* time, | 1424 | struct fat_file *file) |
1069 | unsigned short* tenth ) | ||
1070 | { | 1425 | { |
1426 | #ifdef HAVE_MULTIVOLUME | ||
1427 | file->volume = volume; | ||
1428 | #endif | ||
1429 | file->firstcluster = startcluster; | ||
1430 | file->dircluster = 0; | ||
1431 | file->e.entry = 0; | ||
1432 | file->e.entries = 0; | ||
1433 | } | ||
1434 | |||
1071 | #if CONFIG_RTC | 1435 | #if CONFIG_RTC |
1072 | struct tm* tm = get_time(); | 1436 | static void fat_time(uint16_t *date, uint16_t *time, int16_t *tenth) |
1437 | { | ||
1438 | struct tm *tm = get_time(); | ||
1073 | 1439 | ||
1074 | if (date) | 1440 | if (date) |
1441 | { | ||
1075 | *date = ((tm->tm_year - 80) << 9) | | 1442 | *date = ((tm->tm_year - 80) << 9) | |
1076 | ((tm->tm_mon + 1) << 5) | | 1443 | ((tm->tm_mon + 1) << 5) | |
1077 | tm->tm_mday; | 1444 | tm->tm_mday; |
1445 | } | ||
1078 | 1446 | ||
1079 | if (time) | 1447 | if (time) |
1448 | { | ||
1080 | *time = (tm->tm_hour << 11) | | 1449 | *time = (tm->tm_hour << 11) | |
1081 | (tm->tm_min << 5) | | 1450 | (tm->tm_min << 5) | |
1082 | (tm->tm_sec >> 1); | 1451 | (tm->tm_sec >> 1); |
1452 | } | ||
1083 | 1453 | ||
1084 | if (tenth) | 1454 | if (tenth) |
1085 | *tenth = (tm->tm_sec & 1) * 100; | 1455 | *tenth = (tm->tm_sec & 1) * 100; |
1086 | #else | 1456 | } |
1457 | |||
1458 | #else /* !CONFIG_RTC */ | ||
1459 | |||
1460 | static void fat_time(uint16_t *date, uint16_t *time, int16_t *tenth) | ||
1461 | { | ||
1087 | /* non-RTC version returns an increment from the supplied time, or a | 1462 | /* non-RTC version returns an increment from the supplied time, or a |
1088 | * fixed standard time/date if no time given as input */ | 1463 | * fixed standard time/date if no time given as input */ |
1089 | 1464 | ||
1090 | /* Macros to convert a 2-digit string to a decimal constant. | 1465 | /* Macros to convert a 2-digit string to a decimal constant. |
1091 | (YEAR), MONTH and DAY are set by the date command, which outputs | 1466 | (YEAR), MONTH and DAY are set by the date command, which outputs |
1092 | DAY as 00..31 and MONTH as 01..12. The leading zero would lead to | 1467 | DAY as 00..31 and MONTH as 01..12. The leading zero would lead to |
1093 | misinterpretation as an octal constant. */ | 1468 | misinterpretation as an octal constant. */ |
1094 | #define S100(x) 1 ## x | 1469 | #define S100(x) 1 ## x |
1095 | #define C2DIG2DEC(x) (S100(x)-100) | 1470 | #define C2DIG2DEC(x) (S100(x)-100) |
1096 | /* The actual build date, as FAT date constant */ | 1471 | /* The actual build date, as FAT date constant */ |
1097 | #define BUILD_DATE_FAT (((YEAR - 1980) << 9) \ | 1472 | #define BUILD_DATE_FAT (((YEAR - 1980) << 9) \ |
1098 | | (C2DIG2DEC(MONTH) << 5) \ | 1473 | | (C2DIG2DEC(MONTH) << 5) \ |
1099 | | C2DIG2DEC(DAY)) | 1474 | | C2DIG2DEC(DAY)) |
1100 | 1475 | ||
1101 | bool date_forced = false; | 1476 | bool date_forced = false; |
1102 | bool next_day = false; | 1477 | bool next_day = false; |
@@ -1107,7 +1482,7 @@ static void fat_time(unsigned short* date, | |||
1107 | *date = BUILD_DATE_FAT; | 1482 | *date = BUILD_DATE_FAT; |
1108 | date_forced = true; | 1483 | date_forced = true; |
1109 | } | 1484 | } |
1110 | 1485 | ||
1111 | if (time) | 1486 | if (time) |
1112 | { | 1487 | { |
1113 | time2 = *time << 1; | 1488 | time2 = *time << 1; |
@@ -1119,8 +1494,8 @@ static void fat_time(unsigned short* date, | |||
1119 | { | 1494 | { |
1120 | unsigned mins = (time2 >> 6) & 0x3f; | 1495 | unsigned mins = (time2 >> 6) & 0x3f; |
1121 | unsigned hours = (time2 >> 12) & 0x1f; | 1496 | unsigned hours = (time2 >> 12) & 0x1f; |
1122 | 1497 | ||
1123 | mins = 11 * ((mins/11) + 1); /* advance to next multiple of 11 */ | 1498 | mins = 11 * ((mins / 11) + 1); /* advance to next multiple of 11 */ |
1124 | if (mins > 59) | 1499 | if (mins > 59) |
1125 | { | 1500 | { |
1126 | mins = 11; /* 00 would be a bad marker */ | 1501 | mins = 11; /* 00 would be a bad marker */ |
@@ -1134,14 +1509,14 @@ static void fat_time(unsigned short* date, | |||
1134 | } | 1509 | } |
1135 | *time = time2 >> 1; | 1510 | *time = time2 >> 1; |
1136 | } | 1511 | } |
1137 | 1512 | ||
1138 | if (tenth) | 1513 | if (tenth) |
1139 | *tenth = (time2 & 1) * 100; | 1514 | *tenth = (time2 & 1) * 100; |
1140 | 1515 | ||
1141 | if (date && next_day) | 1516 | if (date && next_day) |
1142 | { | 1517 | { |
1143 | static const unsigned char daysinmonth[] = | 1518 | static const unsigned char daysinmonth[] = |
1144 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | 1519 | { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
1145 | unsigned day = *date & 0x1f; | 1520 | unsigned day = *date & 0x1f; |
1146 | unsigned month = (*date >> 5) & 0x0f; | 1521 | unsigned month = (*date >> 5) & 0x0f; |
1147 | unsigned year = (*date >> 9) & 0x7f; | 1522 | unsigned year = (*date >> 9) & 0x7f; |
@@ -1156,1514 +1531,1393 @@ static void fat_time(unsigned short* date, | |||
1156 | year++; | 1531 | year++; |
1157 | } | 1532 | } |
1158 | } | 1533 | } |
1534 | |||
1159 | *date = (year << 9) | (month << 5) | day; | 1535 | *date = (year << 9) | (month << 5) | day; |
1160 | } | 1536 | } |
1161 | |||
1162 | #endif /* CONFIG_RTC */ | ||
1163 | } | 1537 | } |
1538 | #endif /* CONFIG_RTC */ | ||
1164 | 1539 | ||
1165 | static int write_long_name(struct fat_file* file, | 1540 | static int write_longname(struct bpb *fat_bpb, struct fat_filestr *parentstr, |
1166 | unsigned int firstentry, | 1541 | struct fat_file *file, const unsigned char *name, |
1167 | unsigned int numentries, | 1542 | unsigned long ucslen, const unsigned char *shortname, |
1168 | const unsigned char* name, | 1543 | union raw_dirent *srcent, uint8_t attr, |
1169 | const unsigned char* shortname, | 1544 | unsigned int flags) |
1170 | bool is_directory) | ||
1171 | { | 1545 | { |
1172 | unsigned char* entry; | 1546 | DEBUGF("%s(file:%lx, first:%d, num:%d, name:%s)\n", __func__, |
1173 | unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR; | 1547 | parent->info->firstcluster, firstentry, numentries, name); |
1174 | unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR; | 1548 | |
1175 | unsigned char chksum = 0; | ||
1176 | unsigned int i, j=0; | ||
1177 | unsigned int nameidx=0, namelen = utf8length(name); | ||
1178 | int rc; | 1549 | int rc; |
1179 | unsigned short name_utf16[namelen + 1]; | 1550 | union raw_dirent *ent; |
1551 | |||
1552 | uint16_t date = 0, time = 0, tenth = 0; | ||
1553 | fat_time(&date, &time, &tenth); | ||
1554 | time = htole16(time); | ||
1555 | date = htole16(date); | ||
1180 | 1556 | ||
1181 | LDEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)\n", | 1557 | /* shortname checksum saved in each longname entry */ |
1182 | file->firstcluster, firstentry, numentries, name); | 1558 | uint8_t chksum = shortname_checksum(shortname); |
1183 | 1559 | ||
1184 | rc = fat_seek(file, sector); | 1560 | /* we need to convert the name first since the entries are written in |
1185 | if (rc<0) | 1561 | reverse order */ |
1186 | return rc * 10 - 1; | 1562 | unsigned long ucspadlen = ALIGN_UP(ucslen, FATLONG_NAME_CHARS); |
1563 | uint16_t ucsname[ucspadlen]; | ||
1187 | 1564 | ||
1188 | unsigned char* buf = fat_get_sector_buffer(); | 1565 | for (unsigned long i = 0; i < ucspadlen; i++) |
1189 | rc = fat_readwrite(file, 1, buf, false); | ||
1190 | if (rc<1) | ||
1191 | { | 1566 | { |
1192 | fat_release_sector_buffer(); | 1567 | if (i < ucslen) |
1193 | return rc * 10 - 2; | 1568 | name = utf8decode(name, &ucsname[i]); |
1569 | else if (i == ucslen) | ||
1570 | ucsname[i] = 0x0000; /* name doesn't fill last block */ | ||
1571 | else /* i > ucslen */ | ||
1572 | ucsname[i] = 0xffff; /* pad-out to end */ | ||
1194 | } | 1573 | } |
1195 | 1574 | ||
1196 | /* calculate shortname checksum */ | 1575 | dc_lock_cache(); |
1197 | for (i=11; i>0; i--) | ||
1198 | chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++]; | ||
1199 | |||
1200 | /* calc position of last name segment */ | ||
1201 | if ( namelen > NAME_BYTES_PER_ENTRY ) | ||
1202 | for (nameidx=0; | ||
1203 | nameidx < (namelen - NAME_BYTES_PER_ENTRY); | ||
1204 | nameidx += NAME_BYTES_PER_ENTRY); | ||
1205 | |||
1206 | /* we need to convert the name first */ | ||
1207 | /* since it is written in reverse order */ | ||
1208 | for (i = 0; i <= namelen; i++) | ||
1209 | name = utf8decode(name, &name_utf16[i]); | ||
1210 | |||
1211 | for (i=0; i < numentries; i++) { | ||
1212 | /* new sector? */ | ||
1213 | if ( idx >= DIR_ENTRIES_PER_SECTOR ) { | ||
1214 | /* update current sector */ | ||
1215 | rc = fat_seek(file, sector); | ||
1216 | if (rc<0) | ||
1217 | { | ||
1218 | fat_release_sector_buffer(); | ||
1219 | return rc * 10 - 3; | ||
1220 | } | ||
1221 | |||
1222 | rc = fat_readwrite(file, 1, buf, true); | ||
1223 | if (rc<1) | ||
1224 | { | ||
1225 | fat_release_sector_buffer(); | ||
1226 | return rc * 10 - 4; | ||
1227 | } | ||
1228 | |||
1229 | /* read next sector */ | ||
1230 | rc = fat_readwrite(file, 1, buf, false); | ||
1231 | if (rc<0) { | ||
1232 | fat_release_sector_buffer(); | ||
1233 | LDEBUGF("Failed writing new sector: %d\n",rc); | ||
1234 | return rc * 10 - 5; | ||
1235 | } | ||
1236 | if (rc==0) | ||
1237 | /* end of dir */ | ||
1238 | memset(buf, 0, SECTOR_SIZE); | ||
1239 | 1576 | ||
1240 | sector++; | 1577 | const unsigned int longentries = file->e.entries - 1; |
1241 | idx = 0; | 1578 | const unsigned int firstentry = file->e.entry - longentries; |
1242 | } | ||
1243 | 1579 | ||
1244 | entry = buf + idx * DIR_ENTRY_SIZE; | 1580 | /* longame entries */ |
1581 | for (unsigned int i = 0; i < longentries; i++) | ||
1582 | { | ||
1583 | ent = cache_direntry(fat_bpb, parentstr, firstentry + i); | ||
1584 | if (!ent) | ||
1585 | FAT_ERROR(-2); | ||
1245 | 1586 | ||
1246 | /* verify this entry is free */ | 1587 | /* verify this entry is free */ |
1247 | if (entry[0] && entry[0] != 0xe5 ) | 1588 | if (ent->name[0] && ent->name[0] != 0xe5) |
1248 | { | 1589 | { |
1249 | fat_release_sector_buffer(); | ||
1250 | panicf("Dir entry %d in sector %x is not free! " | 1590 | panicf("Dir entry %d in sector %x is not free! " |
1251 | "%02x %02x %02x %02x", | 1591 | "%02x %02x %02x %02x", |
1252 | idx, sector, | 1592 | i + firstentry, (unsigned)parentstr->lastsector, |
1253 | entry[0], entry[1], entry[2], entry[3]); | 1593 | (unsigned)ent->data[0], (unsigned)ent->data[1], |
1594 | (unsigned)ent->data[2], (unsigned)ent->data[3]); | ||
1254 | } | 1595 | } |
1255 | 1596 | ||
1256 | memset(entry, 0, DIR_ENTRY_SIZE); | 1597 | memset(ent->data, 0, DIR_ENTRY_SIZE); |
1257 | if ( i+1 < numentries ) { | ||
1258 | /* longname entry */ | ||
1259 | unsigned int k, l = nameidx; | ||
1260 | |||
1261 | entry[FATLONG_ORDER] = numentries-i-1; | ||
1262 | if (i==0) { | ||
1263 | /* mark this as last long entry */ | ||
1264 | entry[FATLONG_ORDER] |= FATLONG_LAST_LONG_ENTRY; | ||
1265 | |||
1266 | /* pad name with 0xffff */ | ||
1267 | for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | ||
1268 | for (k=14; k<26; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | ||
1269 | for (k=28; k<32; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | ||
1270 | }; | ||
1271 | /* set name */ | ||
1272 | for (k=0; k<5 && l <= namelen; k++) { | ||
1273 | entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff); | ||
1274 | entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8); | ||
1275 | } | ||
1276 | for (k=0; k<6 && l <= namelen; k++) { | ||
1277 | entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff); | ||
1278 | entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8); | ||
1279 | } | ||
1280 | for (k=0; k<2 && l <= namelen; k++) { | ||
1281 | entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff); | ||
1282 | entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8); | ||
1283 | } | ||
1284 | 1598 | ||
1285 | entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME; | 1599 | unsigned int ord = longentries - i; |
1286 | entry[FATDIR_FSTCLUSLO] = 0; | 1600 | |
1287 | entry[FATLONG_TYPE] = 0; | 1601 | ent->ldir_ord = ord | (i == 0 ? FATLONG_ORD_F_LAST : 0); |
1288 | entry[FATLONG_CHKSUM] = chksum; | 1602 | ent->ldir_attr = ATTR_LONG_NAME; |
1289 | LDEBUGF("Longname entry %d: %s\n", idx, name+nameidx); | 1603 | ent->ldir_chksum = chksum; |
1290 | } | 1604 | |
1291 | else { | 1605 | /* set name */ |
1292 | /* shortname entry */ | 1606 | uint16_t *ucsptr = &ucsname[(ord - 1) * FATLONG_NAME_CHARS]; |
1293 | unsigned short date=0, time=0, tenth=0; | 1607 | for (unsigned j = longent_char_first(); j; j = longent_char_next(j)) |
1294 | LDEBUGF("Shortname entry: %s\n", shortname); | 1608 | { |
1295 | memcpy(entry + FATDIR_NAME, shortname, 11); | 1609 | uint16_t ucs = *ucsptr++; |
1296 | entry[FATDIR_ATTR] = is_directory?FAT_ATTR_DIRECTORY:0; | 1610 | INT162BYTES(ent->data, j, ucs); |
1297 | entry[FATDIR_NTRES] = 0; | ||
1298 | |||
1299 | fat_time(&date, &time, &tenth); | ||
1300 | entry[FATDIR_CRTTIMETENTH] = tenth; | ||
1301 | *(unsigned short*)(entry + FATDIR_CRTTIME) = htole16(time); | ||
1302 | *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time); | ||
1303 | *(unsigned short*)(entry + FATDIR_CRTDATE) = htole16(date); | ||
1304 | *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date); | ||
1305 | *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date); | ||
1306 | } | 1611 | } |
1307 | idx++; | 1612 | |
1308 | nameidx -= NAME_BYTES_PER_ENTRY; | 1613 | dc_dirty_buf(ent); |
1614 | DEBUGF("Longname entry %d\n", ord); | ||
1309 | } | 1615 | } |
1310 | 1616 | ||
1311 | /* update last sector */ | 1617 | /* shortname entry */ |
1312 | rc = fat_seek(file, sector); | 1618 | DEBUGF("Shortname '%s'\n", shortname); |
1313 | if (rc<0) | 1619 | |
1620 | ent = cache_direntry(fat_bpb, parentstr, file->e.entry); | ||
1621 | if (!ent) | ||
1622 | FAT_ERROR(-2); | ||
1623 | |||
1624 | if (srcent && (flags & DIRENT_TEMPL)) | ||
1314 | { | 1625 | { |
1315 | fat_release_sector_buffer(); | 1626 | /* srcent points to short entry template */ |
1316 | return rc * 10 - 6; | 1627 | *ent = *srcent; |
1628 | } | ||
1629 | else | ||
1630 | { | ||
1631 | /* make our own short entry */ | ||
1632 | memset(ent->data, 0, DIR_ENTRY_SIZE); | ||
1633 | ent->attr = attr; | ||
1317 | } | 1634 | } |
1318 | 1635 | ||
1319 | rc = fat_readwrite(file, 1, buf, true); | 1636 | /* short name may change even if just moving */ |
1320 | fat_release_sector_buffer(); | 1637 | memcpy(ent->name, shortname, 11); |
1321 | if (rc<1) | 1638 | raw_dirent_set_fstclus(ent, file->firstcluster); |
1322 | return rc * 10 - 7; | ||
1323 | 1639 | ||
1324 | return 0; | 1640 | if (!(flags & DIRENT_TEMPL_CRT)) |
1325 | } | ||
1326 | |||
1327 | static int fat_checkname(const unsigned char* newname) | ||
1328 | { | ||
1329 | static const char invalid_chars[] = "\"*/:<>?\\|"; | ||
1330 | int len = strlen(newname); | ||
1331 | /* More sanity checks are probably needed */ | ||
1332 | if (len > 255 || newname[len - 1] == '.') | ||
1333 | { | 1641 | { |
1334 | return -1; | 1642 | ent->crttimetenth = tenth; |
1643 | ent->crttime = time; | ||
1644 | ent->crtdate = date; | ||
1335 | } | 1645 | } |
1336 | while (*newname) | 1646 | |
1647 | if (!(flags & DIRENT_TEMPL_WRT)) | ||
1337 | { | 1648 | { |
1338 | if (*newname < ' ' || strchr(invalid_chars, *newname) != NULL) | 1649 | ent->wrttime = time; |
1339 | return -1; | 1650 | ent->wrtdate = date; |
1340 | newname++; | ||
1341 | } | 1651 | } |
1342 | /* check trailing space(s) */ | ||
1343 | if(*(--newname) == ' ') | ||
1344 | return -1; | ||
1345 | 1652 | ||
1346 | return 0; | 1653 | if (!(flags & DIRENT_TEMPL_ACC)) |
1654 | ent->lstaccdate = date; | ||
1655 | |||
1656 | if (srcent && (flags & DIRENT_RETURN)) | ||
1657 | *srcent = *ent; /* caller wants */ | ||
1658 | |||
1659 | dc_dirty_buf(ent); | ||
1660 | |||
1661 | rc = 0; | ||
1662 | fat_error: | ||
1663 | dc_unlock_cache(); | ||
1664 | return rc; | ||
1347 | } | 1665 | } |
1348 | 1666 | ||
1349 | static int add_dir_entry(struct fat_dir* dir, | 1667 | static int add_dir_entry(struct bpb *fat_bpb, struct fat_filestr *parentstr, |
1350 | struct fat_file* file, | 1668 | struct fat_file *file, const char *name, |
1351 | const char* name, | 1669 | union raw_dirent *srcent, uint8_t attr, |
1352 | bool is_directory, | 1670 | unsigned int flags) |
1353 | bool dotdir) | ||
1354 | { | 1671 | { |
1355 | #ifdef HAVE_MULTIVOLUME | 1672 | DEBUGF("%s(name:\"%s\",first:%lx)\n", __func__, name, |
1356 | struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; | 1673 | file->firstcluster); |
1357 | #else | ||
1358 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
1359 | #endif | ||
1360 | unsigned char shortname[12]; | ||
1361 | int rc; | ||
1362 | unsigned int sector; | ||
1363 | bool done = false; | ||
1364 | int entries_needed, entries_found = 0; | ||
1365 | int firstentry; | ||
1366 | |||
1367 | LDEBUGF( "add_dir_entry(%s,%lx)\n", | ||
1368 | name, file->firstcluster); | ||
1369 | |||
1370 | /* Don't check dotdirs name for validity */ | ||
1371 | if (dotdir == false) { | ||
1372 | rc = fat_checkname(name); | ||
1373 | if (rc < 0) { | ||
1374 | /* filename is invalid */ | ||
1375 | return rc * 10 - 1; | ||
1376 | } | ||
1377 | } | ||
1378 | 1674 | ||
1379 | #ifdef HAVE_MULTIVOLUME | 1675 | int rc; |
1380 | file->volume = dir->file.volume; /* inherit the volume, to make sure */ | ||
1381 | #endif | ||
1382 | 1676 | ||
1383 | /* The "." and ".." directory entries must not be long names */ | 1677 | unsigned char basisname[11], shortname[11]; |
1384 | if(dotdir) { | 1678 | int n; |
1385 | int i; | 1679 | int entries_needed; |
1386 | strlcpy(shortname, name, 12); | 1680 | unsigned long ucslen = 0; |
1387 | for(i = strlen(shortname); i < 12; i++) | ||
1388 | shortname[i] = ' '; | ||
1389 | 1681 | ||
1682 | if (is_dotdir_name(name) && (attr & ATTR_DIRECTORY)) | ||
1683 | { | ||
1684 | /* The "." and ".." directory entries must not be long names */ | ||
1685 | int dots = strlcpy(shortname, name, 11); | ||
1686 | memset(&shortname[dots], ' ', 11 - dots); | ||
1390 | entries_needed = 1; | 1687 | entries_needed = 1; |
1391 | } else { | 1688 | } |
1392 | create_dos_name(name, shortname); | 1689 | else |
1690 | { | ||
1691 | rc = check_longname(name); | ||
1692 | if (rc < 0) | ||
1693 | FAT_ERROR(rc * 10 - 1); /* filename is invalid */ | ||
1694 | |||
1695 | create_dos_name(basisname, name, &n); | ||
1696 | randomize_dos_name(shortname, basisname, &n); | ||
1393 | 1697 | ||
1394 | /* one dir entry needed for every 13 bytes of filename, | 1698 | /* one dir entry needed for every 13 characters of filename, |
1395 | plus one entry for the short name */ | 1699 | plus one entry for the short name */ |
1396 | entries_needed = (utf8length(name) + (NAME_BYTES_PER_ENTRY-1)) | 1700 | ucslen = utf8length(name); |
1397 | / NAME_BYTES_PER_ENTRY + 1; | 1701 | if (ucslen > 255) |
1702 | FAT_ERROR(-2); /* name is too long */ | ||
1703 | |||
1704 | entries_needed = (ucslen + FATLONG_NAME_CHARS - 1) | ||
1705 | / FATLONG_NAME_CHARS + 1; | ||
1398 | } | 1706 | } |
1399 | 1707 | ||
1400 | unsigned char* buf = fat_get_sector_buffer(); | 1708 | int entry = 0, entries_found = 0, firstentry = -1; |
1401 | restart: | 1709 | const int entperclus = DIR_ENTRIES_PER_SECTOR*fat_bpb->bpb_secperclus; |
1402 | firstentry = -1; | ||
1403 | 1710 | ||
1404 | rc = fat_seek(&dir->file, 0); | 1711 | /* step 1: search for a sufficiently-long run of free entries and check |
1405 | if (rc < 0) | 1712 | for duplicate shortname */ |
1406 | { | 1713 | dc_lock_cache(); |
1407 | fat_release_sector_buffer(); | ||
1408 | return rc * 10 - 2; | ||
1409 | } | ||
1410 | 1714 | ||
1411 | /* step 1: search for free entries and check for duplicate shortname */ | 1715 | for (bool done = false; !done;) |
1412 | for (sector = 0; !done; sector++) | ||
1413 | { | 1716 | { |
1414 | unsigned int i; | 1717 | union raw_dirent *ent = cache_direntry(fat_bpb, parentstr, entry); |
1415 | 1718 | ||
1416 | rc = fat_readwrite(&dir->file, 1, buf, false); | 1719 | if (!ent) |
1417 | if (rc < 0) { | 1720 | { |
1418 | fat_release_sector_buffer(); | 1721 | if (parentstr->eof) |
1419 | DEBUGF( "add_dir_entry() - Couldn't read dir" | 1722 | { |
1420 | " (error code %d)\n", rc); | 1723 | DEBUGF("End of dir (entry %d)\n", entry); |
1421 | return rc * 10 - 3; | 1724 | break; |
1422 | } | 1725 | } |
1423 | 1726 | ||
1424 | if (rc == 0) { /* current end of dir reached */ | 1727 | DEBUGF("Couldn't read dir (entry %d)\n", entry); |
1425 | LDEBUGF("End of dir on cluster boundary\n"); | 1728 | dc_unlock_cache(); |
1426 | break; | 1729 | FAT_ERROR(-3); |
1427 | } | 1730 | } |
1428 | 1731 | ||
1429 | /* look for free slots */ | 1732 | switch (ent->name[0]) |
1430 | for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++) | ||
1431 | { | 1733 | { |
1432 | switch (buf[i * DIR_ENTRY_SIZE]) { | 1734 | case 0: /* all remaining entries in cluster are free */ |
1433 | case 0: | 1735 | DEBUGF("Found end of dir %d\n", entry); |
1434 | entries_found += DIR_ENTRIES_PER_SECTOR - i; | 1736 | int found = entperclus - (entry % entperclus); |
1435 | LDEBUGF("Found end of dir %d\n", | 1737 | entries_found += found; |
1436 | sector * DIR_ENTRIES_PER_SECTOR + i); | 1738 | entry += found; /* move entry passed end of cluster */ |
1437 | i = DIR_ENTRIES_PER_SECTOR - 1; | 1739 | done = true; |
1438 | done = true; | 1740 | break; |
1439 | break; | ||
1440 | 1741 | ||
1441 | case 0xe5: | 1742 | case 0xe5: /* individual free entry */ |
1442 | entries_found++; | 1743 | entries_found++; |
1443 | LDEBUGF("Found free entry %d (%d/%d)\n", | 1744 | entry++; |
1444 | sector * DIR_ENTRIES_PER_SECTOR + i, | 1745 | DEBUGF("Found free entry %d (%d/%d)\n", |
1445 | entries_found, entries_needed); | 1746 | entry, entries_found, entries_needed); |
1446 | break; | 1747 | break; |
1447 | 1748 | ||
1448 | default: | 1749 | default: /* occupied */ |
1449 | entries_found = 0; | 1750 | entries_found = 0; |
1751 | entry++; | ||
1450 | 1752 | ||
1451 | /* check that our intended shortname doesn't already exist */ | 1753 | if ((ent->ldir_attr & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME) |
1452 | if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 11)) { | 1754 | break; /* ignore long name entry */ |
1453 | /* shortname exists already, make a new one */ | ||
1454 | randomize_dos_name(shortname); | ||
1455 | LDEBUGF("Duplicate shortname, changing to %s\n", | ||
1456 | shortname); | ||
1457 | 1755 | ||
1458 | /* name has changed, we need to restart search */ | 1756 | /* check that our intended shortname doesn't already exist */ |
1459 | goto restart; | 1757 | if (!strncmp(shortname, ent->name, 11)) |
1460 | } | 1758 | { |
1461 | break; | 1759 | /* shortname exists already, make a new one */ |
1760 | DEBUGF("Duplicate shortname '%.11s'", shortname); | ||
1761 | randomize_dos_name(shortname, basisname, &n); | ||
1762 | DEBUGF(", changing to '%.11s'\n", shortname); | ||
1763 | |||
1764 | /* name has changed, we need to restart search */ | ||
1765 | entry = 0; | ||
1766 | firstentry = -1; | ||
1462 | } | 1767 | } |
1463 | if (firstentry < 0 && (entries_found >= entries_needed)) | 1768 | break; |
1464 | firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1 | 1769 | } |
1465 | - entries_found; | 1770 | |
1771 | if (firstentry < 0 && entries_found >= entries_needed) | ||
1772 | { | ||
1773 | /* found adequate space; point to initial free entry */ | ||
1774 | firstentry = entry - entries_found; | ||
1466 | } | 1775 | } |
1467 | } | 1776 | } |
1468 | 1777 | ||
1778 | dc_unlock_cache(); | ||
1779 | |||
1469 | /* step 2: extend the dir if necessary */ | 1780 | /* step 2: extend the dir if necessary */ |
1470 | if (firstentry < 0) | 1781 | if (firstentry < 0) |
1471 | { | 1782 | { |
1472 | LDEBUGF("Adding new sector(s) to dir\n"); | 1783 | DEBUGF("Adding new cluster(s) to dir\n"); |
1473 | rc = fat_seek(&dir->file, sector); | 1784 | |
1474 | if (rc < 0) | 1785 | if (entry + entries_needed - entries_found > MAX_DIRENTRIES) |
1475 | { | 1786 | { |
1476 | fat_release_sector_buffer(); | 1787 | /* FAT specification allows no more than 65536 entries (2MB) |
1477 | return rc * 10 - 4; | 1788 | per directory */ |
1789 | DEBUGF("Directory would be too large.\n"); | ||
1790 | FAT_ERROR(-4); | ||
1478 | } | 1791 | } |
1479 | memset(buf, 0, SECTOR_SIZE); | ||
1480 | 1792 | ||
1481 | /* we must clear whole clusters */ | 1793 | while (entries_found < entries_needed) |
1482 | for (; (entries_found < entries_needed) || | ||
1483 | (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); sector++) | ||
1484 | { | 1794 | { |
1485 | if (sector >= (65536/DIR_ENTRIES_PER_SECTOR)) | 1795 | rc = fat_extend_dir(fat_bpb, parentstr); |
1486 | { | 1796 | if (rc == FAT_RC_ENOSPC) |
1487 | fat_release_sector_buffer(); | 1797 | FAT_ERROR(RC); |
1488 | return -5; /* dir too large -- FAT specification */ | 1798 | else if (rc < 0) |
1489 | } | 1799 | FAT_ERROR(rc * 10 - 5); |
1490 | 1800 | ||
1491 | rc = fat_readwrite(&dir->file, 1, buf, true); | 1801 | entries_found += entperclus; |
1492 | if (rc < 1) /* No more room or something went wrong */ | 1802 | entry += entperclus; |
1493 | { | ||
1494 | fat_release_sector_buffer(); | ||
1495 | return rc * 10 - 6; | ||
1496 | } | ||
1497 | |||
1498 | entries_found += DIR_ENTRIES_PER_SECTOR; | ||
1499 | } | 1803 | } |
1500 | 1804 | ||
1501 | firstentry = sector * DIR_ENTRIES_PER_SECTOR - entries_found; | 1805 | firstentry = entry - entries_found; |
1502 | } | 1806 | } |
1503 | fat_release_sector_buffer(); | ||
1504 | 1807 | ||
1505 | /* step 3: add entry */ | 1808 | /* remember the parent directory entry information */ |
1506 | sector = firstentry / DIR_ENTRIES_PER_SECTOR; | 1809 | #ifdef HAVE_MULTIVOLUME |
1507 | LDEBUGF("Adding longname to entry %d in sector %d\n", | 1810 | file->volume = parentstr->fatfilep->volume; |
1508 | firstentry, sector); | 1811 | #endif |
1812 | file->dircluster = parentstr->fatfilep->firstcluster; | ||
1813 | file->e.entry = firstentry + entries_needed - 1; | ||
1814 | file->e.entries = entries_needed; | ||
1509 | 1815 | ||
1510 | rc = write_long_name(&dir->file, firstentry, | 1816 | /* step 3: add entry */ |
1511 | entries_needed, name, | 1817 | DEBUGF("Adding longname to entry %d\n", firstentry); |
1512 | shortname, is_directory); | 1818 | rc = write_longname(fat_bpb, parentstr, file, name, ucslen, |
1819 | shortname, srcent, attr, flags); | ||
1513 | if (rc < 0) | 1820 | if (rc < 0) |
1514 | return rc * 10 - 7; | 1821 | FAT_ERROR(rc * 10 - 6); |
1515 | 1822 | ||
1516 | /* remember where the shortname dir entry is located */ | 1823 | DEBUGF("Added new dir entry %u; using %u entries\n", |
1517 | file->direntry = firstentry + entries_needed - 1; | 1824 | file->e.entry, file->e.entries); |
1518 | file->direntries = entries_needed; | ||
1519 | file->dircluster = dir->file.firstcluster; | ||
1520 | LDEBUGF("Added new dir entry %d, using %d slots.\n", | ||
1521 | file->direntry, file->direntries); | ||
1522 | 1825 | ||
1523 | return 0; | 1826 | rc = 0; |
1827 | fat_error: | ||
1828 | return rc; | ||
1524 | } | 1829 | } |
1525 | 1830 | ||
1526 | static unsigned char char2dos(unsigned char c, int* randomize) | 1831 | static int update_short_entry(struct bpb *fat_bpb, struct fat_file *file, |
1832 | uint32_t size, struct fat_direntry *fatent) | ||
1527 | { | 1833 | { |
1528 | static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|"; | 1834 | DEBUGF("%s(cluster:%lx entry:%d size:%ld)\n", |
1529 | 1835 | __func__, file->firstcluster, file->e.entry, size); | |
1530 | if (c <= 0x20) | ||
1531 | c = 0; /* Illegal char, remove */ | ||
1532 | else if (strchr(invalid_chars, c) != NULL) | ||
1533 | { | ||
1534 | /* Illegal char, replace */ | ||
1535 | c = '_'; | ||
1536 | *randomize = 1; /* as per FAT spec */ | ||
1537 | } | ||
1538 | else | ||
1539 | c = toupper(c); | ||
1540 | 1836 | ||
1541 | return c; | 1837 | int rc; |
1542 | } | ||
1543 | |||
1544 | static void create_dos_name(const unsigned char *name, unsigned char *newname) | ||
1545 | { | ||
1546 | int i; | ||
1547 | unsigned char *ext; | ||
1548 | int randomize = 0; | ||
1549 | 1838 | ||
1550 | /* Find extension part */ | 1839 | #if CONFIG_RTC |
1551 | ext = strrchr(name, '.'); | 1840 | uint16_t time = 0; |
1552 | if (ext == name) /* handle .dotnames */ | 1841 | uint16_t date = 0; |
1553 | ext = NULL; | 1842 | #else |
1843 | /* get old time to increment from */ | ||
1844 | uint16_t time = letoh16(fatent->wrttime); | ||
1845 | uint16_t date = letoh16(fatent->wrtdate); | ||
1846 | #endif | ||
1847 | fat_time(&date, &time, NULL); | ||
1848 | date = htole16(date); | ||
1849 | time = htole16(time); | ||
1554 | 1850 | ||
1555 | /* needs to randomize? */ | 1851 | /* open the parent directory */ |
1556 | if((ext && (strlen(ext) > 4)) || | 1852 | struct fat_file parent; |
1557 | ((ext ? (unsigned int)(ext-name) : strlen(name)) > 8) ) | 1853 | fat_open_internal(IF_MV(file->volume,) file->dircluster, &parent); |
1558 | randomize = 1; | ||
1559 | 1854 | ||
1560 | /* Name part */ | 1855 | struct fat_filestr parentstr; |
1561 | for (i = 0; *name && (!ext || name < ext) && (i < 8); name++) | 1856 | fat_filestr_init(&parentstr, &parent); |
1562 | { | ||
1563 | unsigned char c = char2dos(*name, &randomize); | ||
1564 | if (c) | ||
1565 | newname[i++] = c; | ||
1566 | } | ||
1567 | 1857 | ||
1568 | /* Pad both name and extension */ | 1858 | dc_lock_cache(); |
1569 | while (i < 11) | ||
1570 | newname[i++] = ' '; | ||
1571 | 1859 | ||
1572 | if (newname[0] == 0xe5) /* Special kanji character */ | 1860 | union raw_dirent *ent = cache_direntry(fat_bpb, &parentstr, file->e.entry); |
1573 | newname[0] = 0x05; | 1861 | if (!ent) |
1862 | FAT_ERROR(-1); | ||
1574 | 1863 | ||
1575 | if (ext) | 1864 | if (!ent->name[0] || ent->name[0] == 0xe5) |
1576 | { /* Extension part */ | 1865 | panicf("Updating size on empty dir entry %d\n", file->e.entry); |
1577 | ext++; | ||
1578 | for (i = 8; *ext && (i < 11); ext++) | ||
1579 | { | ||
1580 | unsigned char c = char2dos(*ext, &randomize); | ||
1581 | if (c) | ||
1582 | newname[i++] = c; | ||
1583 | } | ||
1584 | } | ||
1585 | 1866 | ||
1586 | if(randomize) | 1867 | /* basic file data */ |
1587 | randomize_dos_name(newname); | 1868 | raw_dirent_set_fstclus(ent, file->firstcluster); |
1588 | } | 1869 | ent->filesize = htole32(size); |
1589 | 1870 | ||
1590 | static void randomize_dos_name(unsigned char *name) | 1871 | /* time and date info */ |
1591 | { | 1872 | ent->wrttime = time; |
1592 | unsigned char* tilde = NULL; /* ~ location */ | 1873 | ent->wrtdate = date; |
1593 | unsigned char* lastpt = NULL; /* last point of filename */ | 1874 | ent->lstaccdate = date; |
1594 | unsigned char* nameptr = name; /* working copy of name pointer */ | ||
1595 | unsigned char num[9]; /* holds number as string */ | ||
1596 | int i = 0; | ||
1597 | int cnt = 1; | ||
1598 | int numlen; | ||
1599 | int offset; | ||
1600 | 1875 | ||
1601 | while(i++ < 8) | 1876 | if (fatent) |
1602 | { | 1877 | { |
1603 | /* hunt for ~ and where to put it */ | 1878 | fatent->name[0] = '\0'; /* not gonna bother here */ |
1604 | if(!tilde && *nameptr == '~') | 1879 | parse_short_direntry(ent, fatent); |
1605 | tilde = nameptr; | ||
1606 | if(!lastpt && (*nameptr == ' ' || *nameptr == '~')) | ||
1607 | lastpt = nameptr; | ||
1608 | nameptr++; | ||
1609 | } | 1880 | } |
1610 | if(tilde) | ||
1611 | { | ||
1612 | /* extract current count and increment */ | ||
1613 | memcpy(num,tilde+1,7-(unsigned int)(tilde-name)); | ||
1614 | num[7-(unsigned int)(tilde-name)] = 0; | ||
1615 | cnt = atoi(num) + 1; | ||
1616 | } | ||
1617 | cnt %= 10000000; /* protection */ | ||
1618 | snprintf(num, 9, "~%d", cnt); /* allow room for trailing zero */ | ||
1619 | numlen = strlen(num); /* required space */ | ||
1620 | offset = (unsigned int)(lastpt ? lastpt - name : 8); /* prev startpoint */ | ||
1621 | if(offset > (8-numlen)) offset = 8-numlen; /* correct for new numlen */ | ||
1622 | 1881 | ||
1623 | memcpy(&name[offset], num, numlen); | 1882 | dc_dirty_buf(ent); |
1624 | 1883 | ||
1625 | /* in special case of counter overflow: pad with spaces */ | 1884 | rc = 0; |
1626 | for(offset = offset+numlen; offset < 8; offset++) | 1885 | fat_error: |
1627 | name[offset] = ' '; | 1886 | dc_unlock_cache(); |
1887 | return rc; | ||
1628 | } | 1888 | } |
1629 | 1889 | ||
1630 | static int update_short_entry( struct fat_file* file, long size, int attr ) | 1890 | static int free_direntries(struct bpb *fat_bpb, struct fat_file *file) |
1631 | { | 1891 | { |
1632 | int sector = file->direntry / DIR_ENTRIES_PER_SECTOR; | 1892 | /* open the parent directory */ |
1633 | uint32_t* sizeptr; | 1893 | struct fat_file parent; |
1634 | uint16_t* clusptr; | 1894 | fat_open_internal(IF_MV(file->volume,) file->dircluster, &parent); |
1635 | struct fat_file dir; | ||
1636 | int rc; | ||
1637 | 1895 | ||
1638 | LDEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)\n", | 1896 | struct fat_filestr parentstr; |
1639 | file->firstcluster, file->direntry, size); | 1897 | fat_filestr_init(&parentstr, &parent); |
1640 | 1898 | ||
1641 | /* create a temporary file handle for the dir holding this file */ | 1899 | for (unsigned int entries = file->e.entries, |
1642 | rc = fat_open(IF_MV(file->volume,) file->dircluster, &dir, NULL); | 1900 | entry = file->e.entry - entries + 1; |
1643 | if (rc < 0) | 1901 | entries; entries--, entry++) |
1644 | return rc * 10 - 1; | ||
1645 | |||
1646 | rc = fat_seek( &dir, sector ); | ||
1647 | if (rc<0) | ||
1648 | return rc * 10 - 2; | ||
1649 | |||
1650 | unsigned char* buf = fat_get_sector_buffer(); | ||
1651 | unsigned char* entry = | ||
1652 | buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR); | ||
1653 | rc = fat_readwrite(&dir, 1, buf, false); | ||
1654 | if (rc < 1) | ||
1655 | { | 1902 | { |
1656 | fat_release_sector_buffer(); | 1903 | DEBUGF("Clearing dir entry %d (%d/%d)\n", |
1657 | return rc * 10 - 3; | 1904 | entry, entry - numentries + 1, numentries); |
1658 | } | ||
1659 | 1905 | ||
1660 | if (!entry[0] || entry[0] == 0xe5) | 1906 | dc_lock_cache(); |
1661 | { | ||
1662 | fat_release_sector_buffer(); | ||
1663 | panicf("Updating size on empty dir entry %d\n", file->direntry); | ||
1664 | } | ||
1665 | 1907 | ||
1666 | entry[FATDIR_ATTR] = attr & 0xFF; | 1908 | union raw_dirent *ent = cache_direntry(fat_bpb, &parentstr, entry); |
1909 | if (!ent) | ||
1910 | { | ||
1911 | dc_unlock_cache(); | ||
1667 | 1912 | ||
1668 | clusptr = (uint16_t*)(entry + FATDIR_FSTCLUSHI); | 1913 | if (entries == file->e.entries) |
1669 | *clusptr = htole16(file->firstcluster >> 16); | 1914 | return -1; /* nothing at all freed */ |
1670 | 1915 | ||
1671 | clusptr = (uint16_t*)(entry + FATDIR_FSTCLUSLO); | 1916 | /* longname already destroyed; revert to shortname */ |
1672 | *clusptr = htole16(file->firstcluster & 0xffff); | 1917 | file->e.entries = 1; |
1918 | return 0; | ||
1919 | } | ||
1673 | 1920 | ||
1674 | sizeptr = (uint32_t*)(entry + FATDIR_FILESIZE); | 1921 | ent->data[0] = 0xe5; |
1675 | *sizeptr = htole32(size); | ||
1676 | 1922 | ||
1677 | { | 1923 | dc_dirty_buf(ent); |
1678 | #if CONFIG_RTC | 1924 | dc_unlock_cache(); |
1679 | uint16_t time = 0; | ||
1680 | uint16_t date = 0; | ||
1681 | #else | ||
1682 | /* get old time to increment from */ | ||
1683 | uint16_t time = htole16(*(uint16_t*)(entry+FATDIR_WRTTIME)); | ||
1684 | uint16_t date = htole16(*(uint16_t*)(entry+FATDIR_WRTDATE)); | ||
1685 | #endif | ||
1686 | fat_time(&date, &time, NULL); | ||
1687 | *(uint16_t*)(entry + FATDIR_WRTTIME) = htole16(time); | ||
1688 | *(uint16_t*)(entry + FATDIR_WRTDATE) = htole16(date); | ||
1689 | *(uint16_t*)(entry + FATDIR_LSTACCDATE) = htole16(date); | ||
1690 | } | 1925 | } |
1691 | 1926 | ||
1692 | rc = fat_seek( &dir, sector ); | 1927 | /* directory entry info is now gone */ |
1693 | if (rc < 0) | 1928 | file->dircluster = 0; |
1694 | { | 1929 | file->e.entry = FAT_RW_VAL; |
1695 | fat_release_sector_buffer(); | 1930 | file->e.entries = 0; |
1696 | return rc * 10 - 4; | ||
1697 | } | ||
1698 | |||
1699 | rc = fat_readwrite(&dir, 1, buf, true); | ||
1700 | fat_release_sector_buffer(); | ||
1701 | if (rc < 1) | ||
1702 | return rc * 10 - 5; | ||
1703 | 1931 | ||
1704 | return 0; | 1932 | return 1; |
1705 | } | 1933 | } |
1706 | 1934 | ||
1707 | static int parse_direntry(struct fat_direntry *de, const unsigned char *buf) | 1935 | static int free_cluster_chain(struct bpb *fat_bpb, long startcluster) |
1708 | { | 1936 | { |
1709 | int i=0,j=0; | 1937 | for (long last = startcluster, next; last; last = next) |
1710 | unsigned char c; | 1938 | { |
1711 | bool lowercase; | 1939 | next = get_next_cluster(fat_bpb, last); |
1940 | int rc = update_fat_entry(fat_bpb, last, 0); | ||
1941 | if (LIKELY(rc >= 0 && !startcluster)) | ||
1942 | continue; | ||
1712 | 1943 | ||
1713 | memset(de, 0, sizeof(struct fat_direntry)); | 1944 | if (rc < 0) |
1714 | de->attr = buf[FATDIR_ATTR]; | 1945 | return startcluster ? -1 : 0; |
1715 | de->crttimetenth = buf[FATDIR_CRTTIMETENTH]; | ||
1716 | de->crtdate = BYTES2INT16(buf,FATDIR_CRTDATE); | ||
1717 | de->crttime = BYTES2INT16(buf,FATDIR_CRTTIME); | ||
1718 | de->wrtdate = BYTES2INT16(buf,FATDIR_WRTDATE); | ||
1719 | de->wrttime = BYTES2INT16(buf,FATDIR_WRTTIME); | ||
1720 | de->filesize = BYTES2INT32(buf,FATDIR_FILESIZE); | ||
1721 | de->firstcluster = ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSLO)) | | ||
1722 | ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16); | ||
1723 | /* The double cast is to prevent a sign-extension to be done on CalmRISC16. | ||
1724 | (the result of the shift is always considered signed) */ | ||
1725 | 1946 | ||
1726 | /* fix the name */ | 1947 | startcluster = 0; |
1727 | lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME); | ||
1728 | c = buf[FATDIR_NAME]; | ||
1729 | if (c == 0x05) /* special kanji char */ | ||
1730 | c = 0xe5; | ||
1731 | i = 0; | ||
1732 | while (c != ' ') { | ||
1733 | de->name[j++] = lowercase ? tolower(c) : c; | ||
1734 | if (++i >= 8) | ||
1735 | break; | ||
1736 | c = buf[FATDIR_NAME+i]; | ||
1737 | } | ||
1738 | if (buf[FATDIR_NAME+8] != ' ') { | ||
1739 | lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT); | ||
1740 | de->name[j++] = '.'; | ||
1741 | for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++) | ||
1742 | de->name[j++] = lowercase ? tolower(c) : c; | ||
1743 | } | 1948 | } |
1949 | |||
1744 | return 1; | 1950 | return 1; |
1745 | } | 1951 | } |
1746 | 1952 | ||
1747 | int fat_open(IF_MV(int volume,) | ||
1748 | long startcluster, | ||
1749 | struct fat_file *file, | ||
1750 | const struct fat_dir* dir) | ||
1751 | { | ||
1752 | /* Remember where the file's dir entry is located | ||
1753 | * Do it before assigning other fields so that fat_open | ||
1754 | * can be called with file == &dir->file (see fat_opendir) */ | ||
1755 | if ( dir ) { | ||
1756 | file->direntry = dir->entry - 1; | ||
1757 | file->direntries = dir->entrycount; | ||
1758 | file->dircluster = dir->file.firstcluster; | ||
1759 | } | ||
1760 | |||
1761 | file->firstcluster = startcluster; | ||
1762 | file->lastcluster = startcluster; | ||
1763 | file->lastsector = 0; | ||
1764 | file->clusternum = 0; | ||
1765 | file->sectornum = 0; | ||
1766 | file->eof = false; | ||
1767 | #ifdef HAVE_MULTIVOLUME | ||
1768 | file->volume = volume; | ||
1769 | /* fixme: remove error check when done */ | ||
1770 | if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted) | ||
1771 | { | ||
1772 | LDEBUGF("fat_open() illegal volume %d\n", volume); | ||
1773 | return -1; | ||
1774 | } | ||
1775 | #endif | ||
1776 | 1953 | ||
1777 | LDEBUGF("fat_open(%lx), entry %d\n",startcluster,file->direntry); | 1954 | /** File entity functions **/ |
1778 | return 0; | ||
1779 | } | ||
1780 | 1955 | ||
1781 | int fat_create_file(const char* name, | 1956 | int fat_create_file(struct fat_file *parent, const char *name, |
1782 | struct fat_file* file, | 1957 | uint8_t attr, struct fat_file *file, |
1783 | struct fat_dir* dir) | 1958 | struct fat_direntry *fatent) |
1784 | { | 1959 | { |
1785 | int rc; | 1960 | DEBUGF("%s(\"%s\",%lx,%lx)\n", __func__, name, (long)file, (long)dir); |
1786 | 1961 | struct bpb * const fat_bpb = FAT_BPB(parent->volume); | |
1787 | LDEBUGF("fat_create_file(\"%s\",%lx,%lx)\n",name,(long)file,(long)dir); | 1962 | if (!fat_bpb) |
1788 | rc = add_dir_entry(dir, file, name, false, false); | 1963 | return -1; |
1789 | if (!rc) { | ||
1790 | file->firstcluster = 0; | ||
1791 | file->lastcluster = 0; | ||
1792 | file->lastsector = 0; | ||
1793 | file->clusternum = 0; | ||
1794 | file->sectornum = 0; | ||
1795 | file->eof = false; | ||
1796 | } | ||
1797 | 1964 | ||
1798 | return rc; | 1965 | int rc; |
1799 | } | ||
1800 | 1966 | ||
1801 | /* noinline because this is only split out of fat_create_dir to make sure | 1967 | fat_open_internal(IF_MV(parent->volume,) 0, file); |
1802 | * the sector buffer doesn't remain on the stack, to avoid nasty stack | ||
1803 | * overflows later on (when flush_fat() is called) */ | ||
1804 | static __attribute__((noinline)) int fat_clear_cluster(int sector, | ||
1805 | struct bpb *fat_bpb) | ||
1806 | { | ||
1807 | unsigned char* buf = fat_get_sector_buffer(); | ||
1808 | int i,rc; | ||
1809 | memset(buf, 0, SECTOR_SIZE); | ||
1810 | for(i = 0;i < (int)fat_bpb->bpb_secperclus;i++) { | ||
1811 | rc = transfer(IF_MV(fat_bpb,) sector + i, 1, buf, true ); | ||
1812 | if (rc < 0) | ||
1813 | { | ||
1814 | fat_release_sector_buffer(); | ||
1815 | return rc * 10 - 2; | ||
1816 | } | ||
1817 | } | ||
1818 | fat_release_sector_buffer(); | ||
1819 | return 0; | ||
1820 | } | ||
1821 | 1968 | ||
1822 | int fat_create_dir(const char* name, | 1969 | struct fat_filestr parentstr; |
1823 | struct fat_dir* newdir, | 1970 | fat_filestr_init(&parentstr, parent); |
1824 | struct fat_dir* dir) | ||
1825 | { | ||
1826 | #ifdef HAVE_MULTIVOLUME | ||
1827 | struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; | ||
1828 | #else | ||
1829 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
1830 | #endif | ||
1831 | long sector; | ||
1832 | int rc; | ||
1833 | struct fat_file dummyfile; | ||
1834 | 1971 | ||
1835 | LDEBUGF("fat_create_dir(\"%s\",%lx,%lx)\n",name,(long)newdir,(long)dir); | 1972 | const bool isdir = attr & ATTR_DIRECTORY; |
1973 | unsigned int addflags = fatent ? DIRENT_RETURN : 0; | ||
1974 | union raw_dirent *newentp = (isdir || fatent) ? | ||
1975 | alloca(sizeof (union raw_dirent)) : NULL; | ||
1836 | 1976 | ||
1837 | memset(newdir, 0, sizeof(struct fat_dir)); | 1977 | if (isdir) |
1838 | memset(&dummyfile, 0, sizeof(struct fat_file)); | 1978 | { |
1979 | struct fat_filestr dirstr; | ||
1980 | fat_filestr_init(&dirstr, file); | ||
1839 | 1981 | ||
1840 | /* First, add the entry in the parent directory */ | 1982 | /* create the first cluster */ |
1841 | rc = add_dir_entry(dir, &newdir->file, name, true, false); | 1983 | rc = fat_extend_dir(fat_bpb, &dirstr); |
1842 | if (rc < 0) | 1984 | if (rc == FAT_RC_ENOSPC) |
1843 | return rc * 10 - 1; | 1985 | FAT_ERROR(RC); |
1986 | else if (rc < 0) | ||
1987 | FAT_ERROR(rc * 10 - 2); | ||
1844 | 1988 | ||
1845 | /* Allocate a new cluster for the directory */ | 1989 | struct fat_file dummyfile; |
1846 | newdir->file.firstcluster = find_free_cluster(IF_MV(fat_bpb,) | ||
1847 | fat_bpb->fsinfo.nextfree); | ||
1848 | if(newdir->file.firstcluster == 0) | ||
1849 | return -1; | ||
1850 | 1990 | ||
1851 | update_fat_entry(IF_MV(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK); | 1991 | /* add the "." entry */ |
1992 | fat_open_internal(IF_MV(0,) file->firstcluster, &dummyfile); | ||
1852 | 1993 | ||
1853 | /* Clear the entire cluster */ | 1994 | /* this returns the short entry template for the remaining entries */ |
1854 | sector = cluster2sec(IF_MV(fat_bpb,) newdir->file.firstcluster); | 1995 | rc = add_dir_entry(fat_bpb, &dirstr, &dummyfile, ".", newentp, |
1855 | rc = fat_clear_cluster(sector,fat_bpb); | 1996 | attr, DIRENT_RETURN); |
1856 | if (rc < 0) | 1997 | if (rc < 0) |
1857 | return rc; | 1998 | FAT_ERROR(rc * 10 - 3); |
1858 | 1999 | ||
2000 | /* and the ".." entry */ | ||
2001 | /* the root cluster is cluster 0 in the ".." entry */ | ||
2002 | fat_open_internal(IF_MV(0,) | ||
2003 | parent->firstcluster == fat_bpb->bpb_rootclus ? | ||
2004 | 0 : parent->firstcluster, &dummyfile); | ||
1859 | 2005 | ||
1860 | /* Then add the "." entry */ | 2006 | rc = add_dir_entry(fat_bpb, &dirstr, &dummyfile, "..", newentp, |
1861 | rc = add_dir_entry(newdir, &dummyfile, ".", true, true); | 2007 | attr, DIRENT_TEMPL_TIMES); |
1862 | if (rc < 0) | 2008 | if (rc < 0) |
1863 | return rc * 10 - 3; | 2009 | FAT_ERROR(rc * 10 - 4); |
1864 | dummyfile.firstcluster = newdir->file.firstcluster; | ||
1865 | update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY); | ||
1866 | 2010 | ||
1867 | /* and the ".." entry */ | 2011 | addflags |= DIRENT_TEMPL_TIMES; |
1868 | rc = add_dir_entry(newdir, &dummyfile, "..", true, true); | 2012 | } |
1869 | if (rc < 0) | ||
1870 | return rc * 10 - 4; | ||
1871 | 2013 | ||
1872 | /* The root cluster is cluster 0 in the ".." entry */ | 2014 | /* lastly, add the entry in the parent directory */ |
1873 | if(dir->file.firstcluster == fat_bpb->bpb_rootclus) | 2015 | rc = add_dir_entry(fat_bpb, &parentstr, file, name, newentp, |
1874 | dummyfile.firstcluster = 0; | 2016 | attr, addflags); |
1875 | else | 2017 | if (rc == FAT_RC_ENOSPC) |
1876 | dummyfile.firstcluster = dir->file.firstcluster; | 2018 | FAT_ERROR(RC); |
1877 | update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY); | 2019 | else if (rc < 0) |
2020 | FAT_ERROR(rc * 10 - 5); | ||
1878 | 2021 | ||
1879 | /* Set the firstcluster field in the direntry */ | 2022 | if (fatent) |
1880 | update_short_entry(&newdir->file, 0, FAT_ATTR_DIRECTORY); | 2023 | { |
2024 | strcpy(fatent->name, name); | ||
2025 | parse_short_direntry(newentp, fatent); | ||
2026 | } | ||
1881 | 2027 | ||
1882 | rc = flush_fat(IF_MV(fat_bpb)); | 2028 | rc = 0; |
2029 | fat_error: | ||
1883 | if (rc < 0) | 2030 | if (rc < 0) |
1884 | return rc * 10 - 5; | 2031 | free_cluster_chain(fat_bpb, file->firstcluster); |
1885 | 2032 | ||
2033 | cache_commit(fat_bpb); | ||
1886 | return rc; | 2034 | return rc; |
1887 | } | 2035 | } |
1888 | 2036 | ||
1889 | int fat_truncate(const struct fat_file *file) | 2037 | bool fat_dir_is_parent(const struct fat_file *dir, const struct fat_file *file) |
2038 | { | ||
2039 | /* if the directory file's first cluster is the same as the file's | ||
2040 | directory cluster and they're on the same volume, 'dir' is its parent | ||
2041 | directory; the file must also have a dircluster (ie. not removed) */ | ||
2042 | long dircluster = file->dircluster; | ||
2043 | return dircluster && dircluster == dir->firstcluster | ||
2044 | IF_MV( && dir->volume == file->volume ); | ||
2045 | } | ||
2046 | |||
2047 | bool fat_file_is_same(const struct fat_file *file1, | ||
2048 | const struct fat_file *file2) | ||
2049 | { | ||
2050 | /* first, triviality */ | ||
2051 | if (file1 == file2) | ||
2052 | return true; | ||
2053 | |||
2054 | /* if the directory info matches and the volumes are the same, file1 and | ||
2055 | file2 refer to the same file/directory */ | ||
2056 | return file1->dircluster == file2->dircluster | ||
2057 | && file1->e.entry == file2->e.entry | ||
2058 | IF_MV( && file1->volume == file2->volume ); | ||
2059 | } | ||
2060 | |||
2061 | int fat_open(const struct fat_file *parent, long startcluster, | ||
2062 | struct fat_file *file) | ||
1890 | { | 2063 | { |
1891 | /* truncate trailing clusters */ | 2064 | if (!parent) |
1892 | long next; | 2065 | return -2; /* this does _not_ open any root */ |
1893 | long last = file->lastcluster; | 2066 | |
2067 | struct bpb * const fat_bpb = FAT_BPB(parent->volume); | ||
2068 | if (!fat_bpb) | ||
2069 | return -1; | ||
2070 | |||
2071 | /* inherit basic parent information; dirscan info is expected to have been | ||
2072 | initialized beforehand (usually via scanning for the entry ;) */ | ||
1894 | #ifdef HAVE_MULTIVOLUME | 2073 | #ifdef HAVE_MULTIVOLUME |
1895 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2074 | file->volume = parent->volume; |
1896 | #endif | 2075 | #endif |
2076 | file->firstcluster = startcluster; | ||
2077 | file->dircluster = parent->firstcluster; | ||
1897 | 2078 | ||
1898 | LDEBUGF("fat_truncate(%lx, %lx)\n", file->firstcluster, last); | 2079 | return 0; |
2080 | } | ||
1899 | 2081 | ||
1900 | for ( last = get_next_cluster(IF_MV(fat_bpb,) last); last; last = next ) { | 2082 | int fat_open_rootdir(IF_MV(int volume,) struct fat_file *dir) |
1901 | next = get_next_cluster(IF_MV(fat_bpb,) last); | 2083 | { |
1902 | update_fat_entry(IF_MV(fat_bpb,) last,0); | 2084 | struct bpb * const fat_bpb = FAT_BPB(volume); |
1903 | } | 2085 | if (!fat_bpb) |
1904 | if (file->lastcluster) | 2086 | return -1; |
1905 | update_fat_entry(IF_MV(fat_bpb,) file->lastcluster,FAT_EOF_MARK); | ||
1906 | 2087 | ||
2088 | fat_open_internal(IF_MV(volume,) fat_bpb->bpb_rootclus, dir); | ||
1907 | return 0; | 2089 | return 0; |
1908 | } | 2090 | } |
1909 | 2091 | ||
1910 | int fat_closewrite(struct fat_file *file, long size, int attr) | 2092 | int fat_remove(struct fat_file *file, enum fat_remove_op what) |
1911 | { | 2093 | { |
2094 | struct bpb * const fat_bpb = FAT_BPB(file->volume); | ||
2095 | if (!fat_bpb) | ||
2096 | return -1; | ||
2097 | |||
1912 | int rc; | 2098 | int rc; |
1913 | #ifdef HAVE_MULTIVOLUME | ||
1914 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | ||
1915 | #endif | ||
1916 | LDEBUGF("fat_closewrite(size=%ld)\n",size); | ||
1917 | 2099 | ||
1918 | if (!size) { | 2100 | if (file->firstcluster == fat_bpb->bpb_rootclus) |
1919 | /* empty file */ | 2101 | { |
1920 | if ( file->firstcluster ) { | 2102 | DEBUGF("Trying to remove root of volume %d\n", |
1921 | update_fat_entry(IF_MV(fat_bpb,) file->firstcluster, 0); | 2103 | IF_MV_VOL(info->volume)); |
1922 | file->firstcluster = 0; | 2104 | FAT_ERROR(-2); |
1923 | } | ||
1924 | } | 2105 | } |
1925 | 2106 | ||
1926 | if (file->dircluster) { | 2107 | if (file->dircluster && (what & FAT_RM_DIRENTRIES)) |
1927 | rc = update_short_entry(file, size, attr); | 2108 | { |
1928 | if (rc < 0) | 2109 | /* free everything in the parent directory */ |
1929 | return rc * 10 - 1; | 2110 | DEBUGF("Removing dir entries: %lX\n", info->dircluster); |
2111 | rc = free_direntries(fat_bpb, file); | ||
2112 | if (rc <= 0) | ||
2113 | FAT_ERROR(rc * 10 - 3); | ||
1930 | } | 2114 | } |
1931 | 2115 | ||
1932 | flush_fat(IF_MV(fat_bpb)); | 2116 | if (file->firstcluster && (what & FAT_RM_DATA)) |
2117 | { | ||
2118 | /* mark all clusters in the chain as free */ | ||
2119 | DEBUGF("Removing cluster chain: %lX\n", file->firstcluster); | ||
2120 | rc = free_cluster_chain(fat_bpb, file->firstcluster); | ||
2121 | if (rc < 0) | ||
2122 | FAT_ERROR(rc * 10 - 4); | ||
1933 | 2123 | ||
1934 | #ifdef TEST_FAT | 2124 | /* at least the first cluster was freed */ |
1935 | if ( file->firstcluster ) { | 2125 | file->firstcluster = 0; |
1936 | /* debug */ | 2126 | |
1937 | #ifdef HAVE_MULTIVOLUME | 2127 | if (rc == 0) |
1938 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2128 | FAT_ERROR(-5); |
1939 | #else | ||
1940 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
1941 | #endif | ||
1942 | long count = 0; | ||
1943 | long len; | ||
1944 | long next; | ||
1945 | for ( next = file->firstcluster; next; | ||
1946 | next = get_next_cluster(IF_MV(fat_bpb,) next) ) { | ||
1947 | LDEBUGF("cluster %ld: %lx\n", count, next); | ||
1948 | count++; | ||
1949 | } | ||
1950 | len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE; | ||
1951 | LDEBUGF("File is %ld clusters (chainlen=%ld, size=%ld)\n", | ||
1952 | count, len, size ); | ||
1953 | if ( len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE) | ||
1954 | panicf("Cluster chain is too long\n"); | ||
1955 | if ( len < size ) | ||
1956 | panicf("Cluster chain is too short\n"); | ||
1957 | } | 2129 | } |
1958 | #endif | ||
1959 | 2130 | ||
1960 | return 0; | 2131 | rc = 0; |
2132 | fat_error: | ||
2133 | cache_commit(fat_bpb); | ||
2134 | return rc; | ||
1961 | } | 2135 | } |
1962 | 2136 | ||
1963 | static int free_direntries(struct fat_file* file) | 2137 | int fat_rename(struct fat_file *parent, struct fat_file *file, |
2138 | const unsigned char *newname) | ||
1964 | { | 2139 | { |
1965 | struct fat_file dir; | 2140 | struct bpb * const fat_bpb = FAT_BPB(parent->volume); |
1966 | int numentries = file->direntries; | 2141 | if (!fat_bpb) |
1967 | unsigned int entry = file->direntry - numentries + 1; | 2142 | return -1; |
1968 | unsigned int sector = entry / DIR_ENTRIES_PER_SECTOR; | 2143 | |
1969 | int i; | ||
1970 | int rc; | 2144 | int rc; |
2145 | /* save old file; don't change it unless everything succeeds */ | ||
2146 | struct fat_file newfile = *file; | ||
1971 | 2147 | ||
1972 | /* create a temporary file handle for the dir holding this file */ | 2148 | #ifdef HAVE_MULTIVOLUME |
1973 | rc = fat_open(IF_MV(file->volume,) file->dircluster, &dir, NULL); | 2149 | /* rename only works on the same volume */ |
1974 | if (rc < 0) | 2150 | if (file->volume != parent->volume) |
1975 | return rc * 10 - 1; | 2151 | { |
2152 | DEBUGF("No rename across volumes!\n"); | ||
2153 | FAT_ERROR(-2); | ||
2154 | } | ||
2155 | #endif | ||
1976 | 2156 | ||
1977 | rc = fat_seek( &dir, sector ); | 2157 | /* root directories can't be renamed */ |
1978 | if (rc < 0) | 2158 | if (file->firstcluster == fat_bpb->bpb_rootclus) |
1979 | return rc * 10 - 2; | 2159 | { |
2160 | DEBUGF("Trying to rename root of volume %d\n", | ||
2161 | IF_MV_VOL(file->volume)); | ||
2162 | FAT_ERROR(-3); | ||
2163 | } | ||
1980 | 2164 | ||
1981 | unsigned char* buf = fat_get_sector_buffer(); | 2165 | if (!file->dircluster) |
1982 | rc = fat_readwrite(&dir, 1, buf, false); | ||
1983 | if (rc < 1) | ||
1984 | { | 2166 | { |
1985 | fat_release_sector_buffer(); | 2167 | /* file was removed but is still open */ |
1986 | return rc * 10 - 3; | 2168 | DEBUGF("File has no dir cluster!\n"); |
2169 | FAT_ERROR(-4); | ||
1987 | } | 2170 | } |
1988 | 2171 | ||
1989 | for (i=0; i < numentries; i++) { | 2172 | struct fat_file dir; |
1990 | LDEBUGF("Clearing dir entry %d (%d/%d)\n", | 2173 | struct fat_filestr dirstr; |
1991 | entry, i+1, numentries); | ||
1992 | buf[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5; | ||
1993 | entry++; | ||
1994 | 2174 | ||
1995 | if ( (entry % DIR_ENTRIES_PER_SECTOR) == 0 ) { | 2175 | /* open old parent */ |
1996 | /* flush this sector */ | 2176 | fat_open_internal(IF_MV(file->volume,) file->dircluster, &dir); |
1997 | rc = fat_seek(&dir, sector); | 2177 | fat_filestr_init(&dirstr, &dir); |
1998 | if (rc < 0) | ||
1999 | { | ||
2000 | fat_release_sector_buffer(); | ||
2001 | return rc * 10 - 4; | ||
2002 | } | ||
2003 | 2178 | ||
2004 | rc = fat_readwrite(&dir, 1, buf, true); | 2179 | /* fetch a copy of the existing short entry */ |
2005 | if (rc < 1) | 2180 | dc_lock_cache(); |
2006 | { | ||
2007 | fat_release_sector_buffer(); | ||
2008 | return rc * 10 - 5; | ||
2009 | } | ||
2010 | 2181 | ||
2011 | if ( i+1 < numentries ) { | 2182 | union raw_dirent *ent = cache_direntry(fat_bpb, &dirstr, file->e.entry); |
2012 | /* read next sector */ | 2183 | if (!ent) |
2013 | rc = fat_readwrite(&dir, 1, buf, false); | 2184 | { |
2014 | if (rc < 1) | 2185 | dc_unlock_cache(); |
2015 | { | 2186 | FAT_ERROR(-5); |
2016 | fat_release_sector_buffer(); | ||
2017 | return rc * 10 - 6; | ||
2018 | } | ||
2019 | } | ||
2020 | sector++; | ||
2021 | } | ||
2022 | } | 2187 | } |
2023 | 2188 | ||
2024 | if ( entry % DIR_ENTRIES_PER_SECTOR ) { | 2189 | union raw_dirent rawent = *ent; |
2025 | /* flush this sector */ | 2190 | |
2026 | rc = fat_seek(&dir, sector); | 2191 | dc_unlock_cache(); |
2027 | if (rc < 0) | 2192 | |
2193 | /* create new name in new parent directory */ | ||
2194 | fat_filestr_init(&dirstr, parent); | ||
2195 | rc = add_dir_entry(fat_bpb, &dirstr, &newfile, newname, &rawent, | ||
2196 | 0, DIRENT_TEMPL_CRT | DIRENT_TEMPL_WRT); | ||
2197 | if (rc == FAT_RC_ENOSPC) | ||
2198 | FAT_ERROR(RC); | ||
2199 | else if (rc < 0) | ||
2200 | FAT_ERROR(rc * 10 - 6); | ||
2201 | |||
2202 | /* if renaming a directory and it was a move, update the '..' entry to | ||
2203 | keep it pointing to its parent directory */ | ||
2204 | if ((rawent.attr & ATTR_DIRECTORY) && newfile.dircluster != file->dircluster) | ||
2205 | { | ||
2206 | /* open the dir that was renamed */ | ||
2207 | fat_open_internal(IF_MV(newfile.volume,) newfile.firstcluster, &dir); | ||
2208 | fat_filestr_init(&dirstr, &dir); | ||
2209 | |||
2210 | /* obtain dot-dot directory entry */ | ||
2211 | dc_lock_cache(); | ||
2212 | ent = cache_direntry(fat_bpb, &dirstr, 1); | ||
2213 | if (!ent) | ||
2028 | { | 2214 | { |
2029 | fat_release_sector_buffer(); | 2215 | dc_unlock_cache(); |
2030 | return rc * 10 - 7; | 2216 | FAT_ERROR(-7); |
2031 | } | 2217 | } |
2032 | 2218 | ||
2033 | rc = fat_readwrite(&dir, 1, buf, true); | 2219 | if (strncmp(".. ", ent->name, 11)) |
2034 | if (rc < 1) | ||
2035 | { | 2220 | { |
2036 | fat_release_sector_buffer(); | 2221 | /* .. entry must be second entry according to FAT spec (p.29) */ |
2037 | return rc * 10 - 8; | 2222 | DEBUGF("Second dir entry is not double-dot!\n"); |
2223 | dc_unlock_cache(); | ||
2224 | FAT_ERROR(-8); | ||
2038 | } | 2225 | } |
2039 | } | ||
2040 | fat_release_sector_buffer(); | ||
2041 | 2226 | ||
2042 | return 0; | 2227 | /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */ |
2043 | } | 2228 | long parentcluster = 0; |
2044 | 2229 | if (parent->firstcluster != fat_bpb->bpb_rootclus) | |
2045 | int fat_remove(struct fat_file* file) | 2230 | parentcluster = parent->firstcluster; |
2046 | { | ||
2047 | long next, last = file->firstcluster; | ||
2048 | int rc; | ||
2049 | #ifdef HAVE_MULTIVOLUME | ||
2050 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | ||
2051 | #endif | ||
2052 | 2231 | ||
2053 | LDEBUGF("fat_remove(%lx)\n",last); | 2232 | raw_dirent_set_fstclus(ent, parentcluster); |
2054 | 2233 | ||
2055 | while ( last ) { | 2234 | dc_dirty_buf(ent); |
2056 | next = get_next_cluster(IF_MV(fat_bpb,) last); | 2235 | dc_unlock_cache(); |
2057 | update_fat_entry(IF_MV(fat_bpb,) last,0); | ||
2058 | last = next; | ||
2059 | } | 2236 | } |
2060 | 2237 | ||
2061 | if ( file->dircluster ) { | 2238 | /* remove old name */ |
2062 | rc = free_direntries(file); | 2239 | rc = free_direntries(fat_bpb, file); |
2063 | if (rc < 0) | 2240 | if (rc <= 0) |
2064 | return rc * 10 - 1; | 2241 | FAT_ERROR(rc * 10 - 9); |
2065 | } | ||
2066 | 2242 | ||
2067 | file->firstcluster = 0; | 2243 | /* finally, update old file with new directory entry info */ |
2068 | file->dircluster = 0; | 2244 | *file = newfile; |
2069 | 2245 | ||
2070 | rc = flush_fat(IF_MV(fat_bpb)); | 2246 | rc = 0; |
2071 | if (rc < 0) | 2247 | fat_error: |
2072 | return rc * 10 - 2; | 2248 | if (rc < 0 && !fat_file_is_same(&newfile, file)) |
2249 | free_direntries(fat_bpb, &newfile); | ||
2073 | 2250 | ||
2074 | return 0; | 2251 | cache_commit(fat_bpb); |
2252 | return rc; | ||
2075 | } | 2253 | } |
2076 | 2254 | ||
2077 | int fat_rename(struct fat_file* file, | ||
2078 | struct fat_dir* dir, | ||
2079 | const unsigned char* newname, | ||
2080 | long size, | ||
2081 | int attr) | ||
2082 | { | ||
2083 | int rc; | ||
2084 | struct fat_file olddir_file; | ||
2085 | struct fat_file newfile = *file; | ||
2086 | unsigned char* entry = NULL; | ||
2087 | unsigned short* clusptr = NULL; | ||
2088 | unsigned int parentcluster; | ||
2089 | #ifdef HAVE_MULTIVOLUME | ||
2090 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | ||
2091 | 2255 | ||
2092 | if (file->volume != dir->file.volume) { | 2256 | /** File stream functions **/ |
2093 | DEBUGF("No rename across volumes!\n"); | ||
2094 | return -1; | ||
2095 | } | ||
2096 | #else | ||
2097 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
2098 | #endif | ||
2099 | |||
2100 | if ( !file->dircluster ) { | ||
2101 | DEBUGF("File has no dir cluster!\n"); | ||
2102 | return -2; | ||
2103 | } | ||
2104 | |||
2105 | /* create new name */ | ||
2106 | rc = add_dir_entry(dir, &newfile, newname, false, false); | ||
2107 | if (rc < 0) | ||
2108 | return rc * 10 - 2; | ||
2109 | |||
2110 | /* write size and cluster link */ | ||
2111 | rc = update_short_entry(&newfile, size, attr); | ||
2112 | if (rc < 0) | ||
2113 | return rc * 10 - 3; | ||
2114 | 2257 | ||
2115 | /* remove old name */ | 2258 | int fat_closewrite(struct fat_filestr *filestr, uint32_t size, |
2116 | rc = free_direntries(file); | 2259 | struct fat_direntry *fatentp) |
2117 | if (rc < 0) | 2260 | { |
2118 | return rc * 10 - 4; | 2261 | DEBUGF("%s(size=%ld)\n", __func__, size); |
2262 | struct fat_file * const file = filestr->fatfilep; | ||
2263 | struct bpb * const fat_bpb = FAT_BPB(file->volume); | ||
2264 | if (!fat_bpb) | ||
2265 | return -1; | ||
2119 | 2266 | ||
2120 | rc = flush_fat(IF_MV(fat_bpb)); | 2267 | int rc; |
2121 | if (rc < 0) | ||
2122 | return rc * 10 - 5; | ||
2123 | 2268 | ||
2124 | /* if renaming a directory, update the .. entry to make sure | 2269 | if (!size && file->firstcluster) |
2125 | it points to its parent directory (we don't check if it was a move) */ | 2270 | { |
2126 | if(FAT_ATTR_DIRECTORY == attr) { | 2271 | /* empty file */ |
2127 | /* open the dir that was renamed, we re-use the olddir_file struct */ | 2272 | rc = update_fat_entry(fat_bpb, file->firstcluster, 0); |
2128 | rc = fat_open(IF_MV(file->volume,) newfile.firstcluster, &olddir_file, NULL); | ||
2129 | if (rc < 0) | 2273 | if (rc < 0) |
2130 | return rc * 10 - 6; | 2274 | FAT_ERROR(rc * 10 - 2); |
2131 | 2275 | ||
2132 | /* get the first sector of the dir */ | 2276 | file->firstcluster = 0; |
2133 | rc = fat_seek(&olddir_file, 0); | 2277 | fat_rewind(filestr); |
2134 | if (rc < 0) | 2278 | } |
2135 | return rc * 10 - 7; | ||
2136 | 2279 | ||
2137 | unsigned char* buf = fat_get_sector_buffer(); | 2280 | if (file->dircluster) |
2138 | rc = fat_readwrite(&olddir_file, 1, buf, false); | 2281 | { |
2282 | rc = update_short_entry(fat_bpb, file, size, fatentp); | ||
2139 | if (rc < 0) | 2283 | if (rc < 0) |
2140 | { | 2284 | FAT_ERROR(rc * 10 - 3); |
2141 | fat_release_sector_buffer(); | 2285 | } |
2142 | return rc * 10 - 8; | 2286 | else if (fatentp) |
2143 | } | 2287 | { |
2144 | 2288 | fat_empty_fat_direntry(fatentp); | |
2145 | /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */ | 2289 | } |
2146 | if(dir->file.firstcluster == fat_bpb->bpb_rootclus) | ||
2147 | parentcluster = 0; | ||
2148 | else | ||
2149 | parentcluster = dir->file.firstcluster; | ||
2150 | 2290 | ||
2151 | entry = buf + DIR_ENTRY_SIZE; | 2291 | #ifdef TEST_FAT |
2152 | if(strncmp(".. ", entry, 11)) | 2292 | if (file->firstcluster) |
2293 | { | ||
2294 | unsigned long count = 0; | ||
2295 | for (long next = file->firstcluster; next; | ||
2296 | next = get_next_cluster(fat_bpb, next)) | ||
2153 | { | 2297 | { |
2154 | fat_release_sector_buffer(); | 2298 | DEBUGF("cluster %ld: %lx\n", count, next); |
2155 | /* .. entry must be second entry according to FAT spec (p.29) */ | 2299 | count++; |
2156 | DEBUGF("Second dir entry is not double-dot!\n"); | ||
2157 | return rc * 10 - 9; | ||
2158 | } | 2300 | } |
2159 | clusptr = (short*)(entry + FATDIR_FSTCLUSHI); | ||
2160 | *clusptr = htole16(parentcluster >> 16); | ||
2161 | 2301 | ||
2162 | clusptr = (short*)(entry + FATDIR_FSTCLUSLO); | 2302 | uint32_t len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE; |
2163 | *clusptr = htole16(parentcluster & 0xffff); | 2303 | DEBUGF("File is %lu clusters (chainlen=%lu, size=%lu)\n", |
2304 | count, len, size ); | ||
2164 | 2305 | ||
2165 | /* write back this sector */ | 2306 | if (len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE) |
2166 | rc = fat_seek(&olddir_file, 0); | 2307 | panicf("Cluster chain is too long\n"); |
2167 | if (rc < 0) | ||
2168 | { | ||
2169 | fat_release_sector_buffer(); | ||
2170 | return rc * 10 - 7; | ||
2171 | } | ||
2172 | 2308 | ||
2173 | rc = fat_readwrite(&olddir_file, 1, buf, true); | 2309 | if (len < size) |
2174 | fat_release_sector_buffer(); | 2310 | panicf("Cluster chain is too short\n"); |
2175 | if (rc < 1) | ||
2176 | return rc * 10 - 8; | ||
2177 | } | 2311 | } |
2312 | #endif /* TEST_FAT */ | ||
2178 | 2313 | ||
2179 | return 0; | 2314 | rc = 0; |
2315 | fat_error: | ||
2316 | cache_commit(fat_bpb); | ||
2317 | return rc; | ||
2180 | } | 2318 | } |
2181 | 2319 | ||
2182 | static long next_write_cluster(struct fat_file* file, | 2320 | void fat_filestr_init(struct fat_filestr *fatstr, struct fat_file *file) |
2183 | long oldcluster, | ||
2184 | long* newsector) | ||
2185 | { | 2321 | { |
2186 | #ifdef HAVE_MULTIVOLUME | 2322 | fatstr->fatfilep = file; |
2187 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2323 | fat_rewind(fatstr); |
2188 | #else | 2324 | } |
2189 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
2190 | #endif | ||
2191 | long cluster = 0; | ||
2192 | long sector; | ||
2193 | |||
2194 | LDEBUGF("next_write_cluster(%lx,%lx)\n",file->firstcluster, oldcluster); | ||
2195 | |||
2196 | if (oldcluster) | ||
2197 | cluster = get_next_cluster(IF_MV(fat_bpb,) oldcluster); | ||
2198 | |||
2199 | if (!cluster) { | ||
2200 | if (oldcluster > 0) | ||
2201 | cluster = find_free_cluster(IF_MV(fat_bpb,) oldcluster+1); | ||
2202 | else if (oldcluster == 0) | ||
2203 | cluster = find_free_cluster(IF_MV(fat_bpb,) | ||
2204 | fat_bpb->fsinfo.nextfree); | ||
2205 | #ifdef HAVE_FAT16SUPPORT | ||
2206 | else /* negative, pseudo-cluster of the root dir */ | ||
2207 | return 0; /* impossible to append something to the root */ | ||
2208 | #endif | ||
2209 | 2325 | ||
2210 | if (cluster) { | 2326 | unsigned long fat_query_sectornum(const struct fat_filestr *filestr) |
2211 | if (oldcluster) | 2327 | { |
2212 | update_fat_entry(IF_MV(fat_bpb,) oldcluster, cluster); | 2328 | /* return next sector number to be transferred */ |
2213 | else | 2329 | struct bpb * const fat_bpb = FAT_BPB(filestr->fatfilep->volume); |
2214 | file->firstcluster = cluster; | 2330 | if (!fat_bpb) |
2215 | update_fat_entry(IF_MV(fat_bpb,) cluster, FAT_EOF_MARK); | 2331 | return INVALID_SECNUM; |
2216 | } | ||
2217 | else { | ||
2218 | #ifdef TEST_FAT | ||
2219 | if (fat_bpb->fsinfo.freecount>0) | ||
2220 | panicf("There is free space, but find_free_cluster() " | ||
2221 | "didn't find it!\n"); | ||
2222 | #endif | ||
2223 | DEBUGF("next_write_cluster(): Disk full!\n"); | ||
2224 | return 0; | ||
2225 | } | ||
2226 | } | ||
2227 | sector = cluster2sec(IF_MV(fat_bpb,) cluster); | ||
2228 | if (sector<0) | ||
2229 | return 0; | ||
2230 | 2332 | ||
2231 | *newsector = sector; | 2333 | return fat_bpb->bpb_secperclus*filestr->clusternum + filestr->sectornum + 1; |
2232 | return cluster; | ||
2233 | } | 2334 | } |
2234 | 2335 | ||
2235 | static int transfer(IF_MV(struct bpb* fat_bpb,) | 2336 | /* helper for fat_readwrite */ |
2236 | unsigned long start, long count, char* buf, bool write ) | 2337 | static long transfer(struct bpb *fat_bpb, unsigned long start, long count, |
2338 | char *buf, bool write) | ||
2237 | { | 2339 | { |
2238 | #ifndef HAVE_MULTIVOLUME | 2340 | long rc = 0; |
2239 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
2240 | #endif | ||
2241 | int rc; | ||
2242 | 2341 | ||
2243 | LDEBUGF("transfer(s=%lx, c=%lx, %s)\n", | 2342 | DEBUGF("%s(s=%lx, c=%lx, wr=%u)\n", __func__, |
2244 | start+ fat_bpb->startsector, count, write?"write":"read"); | 2343 | start + fat_bpb->startsector, count, write ? 1 : 0); |
2245 | if (write) { | 2344 | |
2345 | if (write) | ||
2346 | { | ||
2246 | unsigned long firstallowed; | 2347 | unsigned long firstallowed; |
2247 | #ifdef HAVE_FAT16SUPPORT | 2348 | #ifdef HAVE_FAT16SUPPORT |
2248 | if (fat_bpb->is_fat16) | 2349 | if (fat_bpb->is_fat16) |
2249 | firstallowed = fat_bpb->rootdirsector; | 2350 | firstallowed = fat_bpb->rootdirsector; |
2250 | else | 2351 | else |
2251 | #endif | 2352 | #endif /* HAVE_FAT16SUPPORT */ |
2252 | firstallowed = fat_bpb->firstdatasector; | 2353 | firstallowed = fat_bpb->firstdatasector; |
2253 | 2354 | ||
2254 | if (start < firstallowed) | 2355 | if (start < firstallowed) |
2255 | panicf("Write %ld before data\n", firstallowed - start); | 2356 | panicf("Write %ld before data\n", firstallowed - start); |
2357 | |||
2256 | if (start + count > fat_bpb->totalsectors) | 2358 | if (start + count > fat_bpb->totalsectors) |
2359 | { | ||
2257 | panicf("Write %ld after data\n", | 2360 | panicf("Write %ld after data\n", |
2258 | start + count - fat_bpb->totalsectors); | 2361 | start + count - fat_bpb->totalsectors); |
2259 | rc = storage_write_sectors(IF_MD(fat_bpb->drive,) | 2362 | } |
2260 | start + fat_bpb->startsector, count, buf); | 2363 | else |
2364 | { | ||
2365 | rc = storage_write_sectors(IF_MD(fat_bpb->drive,) | ||
2366 | start + fat_bpb->startsector, count, buf); | ||
2367 | } | ||
2261 | } | 2368 | } |
2262 | else | 2369 | else |
2370 | { | ||
2263 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) | 2371 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) |
2264 | start + fat_bpb->startsector, count, buf); | 2372 | start + fat_bpb->startsector, count, buf); |
2265 | if (rc < 0) { | 2373 | } |
2266 | DEBUGF( "transfer() - Couldn't %s sector %lx" | 2374 | |
2267 | " (error code %d)\n", | 2375 | if (rc < 0) |
2268 | write ? "write":"read", start, rc); | 2376 | { |
2377 | DEBUGF("Couldn't %s sector %lx (err %d)\n", | ||
2378 | write ? "write":"read", start, rc); | ||
2269 | return rc; | 2379 | return rc; |
2270 | } | 2380 | } |
2381 | |||
2271 | return 0; | 2382 | return 0; |
2272 | } | 2383 | } |
2273 | 2384 | ||
2274 | 2385 | long fat_readwrite(struct fat_filestr *filestr, unsigned long sectorcount, | |
2275 | long fat_readwrite( struct fat_file *file, long sectorcount, | 2386 | void *buf, bool write) |
2276 | void* buf, bool write ) | ||
2277 | { | 2387 | { |
2278 | #ifdef HAVE_MULTIVOLUME | 2388 | struct fat_file * const file = filestr->fatfilep; |
2279 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2389 | struct bpb * const fat_bpb = FAT_BPB(file->volume); |
2280 | #else | 2390 | if (!fat_bpb) |
2281 | struct bpb* fat_bpb = &fat_bpbs[0]; | 2391 | return -1; |
2282 | #endif | ||
2283 | long cluster = file->lastcluster; | ||
2284 | long sector = file->lastsector; | ||
2285 | long clusternum = file->clusternum; | ||
2286 | long numsec = file->sectornum; | ||
2287 | bool eof = file->eof; | ||
2288 | long first=0, last=0; | ||
2289 | long i; | ||
2290 | int rc; | ||
2291 | 2392 | ||
2292 | LDEBUGF( "fat_readwrite(file:%lx,count:0x%lx,buf:%lx,%s)\n", | 2393 | bool eof = filestr->eof; |
2293 | file->firstcluster,sectorcount,(long)buf,write?"write":"read"); | ||
2294 | LDEBUGF( "fat_readwrite: sec=%lx numsec=%ld eof=%d\n", | ||
2295 | sector,numsec, eof?1:0); | ||
2296 | 2394 | ||
2297 | if ( eof && !write) | 2395 | if ((eof && !write) || !sectorcount) |
2298 | return 0; | 2396 | return 0; |
2299 | 2397 | ||
2300 | /* find sequential sectors and write them all at once */ | 2398 | long rc; |
2301 | for (i=0; (i < sectorcount) && (sector > -1); i++ ) { | ||
2302 | numsec++; | ||
2303 | if ( numsec > (long)fat_bpb->bpb_secperclus || !cluster ) { | ||
2304 | long oldcluster = cluster; | ||
2305 | long oldsector = sector; | ||
2306 | long oldnumsec = numsec; | ||
2307 | if (write) | ||
2308 | cluster = next_write_cluster(file, cluster, §or); | ||
2309 | else { | ||
2310 | cluster = get_next_cluster(IF_MV(fat_bpb,) cluster); | ||
2311 | sector = cluster2sec(IF_MV(fat_bpb,) cluster); | ||
2312 | } | ||
2313 | 2399 | ||
2314 | clusternum++; | 2400 | long cluster = filestr->lastcluster; |
2315 | numsec=1; | 2401 | unsigned long sector = filestr->lastsector; |
2402 | long clusternum = filestr->clusternum; | ||
2403 | unsigned long sectornum = filestr->sectornum; | ||
2316 | 2404 | ||
2317 | if (!cluster) { | 2405 | DEBUGF("%s(file:%lx,count:0x%lx,buf:%lx,%s)\n", __func__, |
2318 | eof = true; | 2406 | file->firstcluster, sectorcount, (long)buf, |
2319 | if ( write ) { | 2407 | write ? "write":"read"); |
2320 | /* remember last cluster, in case | 2408 | DEBUGF("%s: sec:%lx numsec:%ld eof:%d\n", __func__, |
2321 | we want to append to the file */ | 2409 | sector, (long)sectornum, eof ? 1 : 0); |
2322 | sector = oldsector; | 2410 | |
2323 | cluster = oldcluster; | 2411 | eof = false; |
2324 | numsec = oldnumsec; | 2412 | |
2325 | clusternum--; | 2413 | if (!sector) |
2326 | i = -1; /* Error code */ | 2414 | { |
2327 | break; | 2415 | /* look up first sector of file */ |
2328 | } | 2416 | long newcluster = file->firstcluster; |
2329 | } | 2417 | |
2330 | else | 2418 | if (write && !newcluster) |
2331 | eof = false; | 2419 | { |
2420 | /* file is empty; try to allocate its first cluster */ | ||
2421 | newcluster = next_write_cluster(fat_bpb, 0); | ||
2422 | file->firstcluster = newcluster; | ||
2332 | } | 2423 | } |
2333 | else { | 2424 | |
2334 | if (sector) | 2425 | if (newcluster) |
2335 | sector++; | 2426 | { |
2336 | else { | 2427 | cluster = newcluster; |
2337 | /* look up first sector of file */ | 2428 | sector = cluster2sec(fat_bpb, cluster) - 1; |
2338 | sector = cluster2sec(IF_MV(fat_bpb,) file->firstcluster); | 2429 | |
2339 | numsec=1; | 2430 | #ifdef HAVE_FAT16SUPPORT |
2340 | #ifdef HAVE_FAT16SUPPORT | 2431 | if (fat_bpb->is_fat16 && file->firstcluster < 0) |
2341 | if (file->firstcluster < 0) | 2432 | { |
2342 | { /* FAT16 root dir */ | 2433 | sector += fat_bpb->rootdirsectornum; |
2343 | sector += fat_bpb->rootdiroffset; | 2434 | sectornum = fat_bpb->rootdirsectornum; |
2344 | numsec += fat_bpb->rootdiroffset; | ||
2345 | } | ||
2346 | #endif | ||
2347 | } | 2435 | } |
2436 | #endif /* HAVE_FAT16SUPPORT */ | ||
2348 | } | 2437 | } |
2438 | } | ||
2349 | 2439 | ||
2350 | if (!first) | 2440 | if (!sector) |
2351 | first = sector; | 2441 | { |
2442 | sectorcount = 0; | ||
2443 | eof = true; | ||
2444 | } | ||
2352 | 2445 | ||
2353 | if ( ((sector != first) && (sector != last+1)) || /* not sequential */ | 2446 | unsigned long transferred = 0; |
2354 | (last-first+1 == 256) ) { /* max 256 sectors per ata request */ | 2447 | unsigned long count = 0; |
2355 | long count = last - first + 1; | 2448 | unsigned long last = sector; |
2356 | rc = transfer(IF_MV(fat_bpb,) first, count, buf, write ); | ||
2357 | if (rc < 0) | ||
2358 | return rc * 10 - 1; | ||
2359 | 2449 | ||
2360 | buf = (char *)buf + count * SECTOR_SIZE; | 2450 | while (transferred + count < sectorcount) |
2361 | first = sector; | 2451 | { |
2452 | if (++sectornum >= fat_bpb->bpb_secperclus) | ||
2453 | { | ||
2454 | /* out of sectors in this cluster; get the next cluster */ | ||
2455 | long newcluster = write ? next_write_cluster(fat_bpb, cluster) : | ||
2456 | get_next_cluster(fat_bpb, cluster); | ||
2457 | if (newcluster) | ||
2458 | { | ||
2459 | cluster = newcluster; | ||
2460 | sector = cluster2sec(fat_bpb, cluster) - 1; | ||
2461 | clusternum++; | ||
2462 | sectornum = 0; | ||
2463 | |||
2464 | /* jumped clusters right at start? */ | ||
2465 | if (!count) | ||
2466 | last = sector; | ||
2467 | } | ||
2468 | else | ||
2469 | { | ||
2470 | sectornum--; /* remain in previous position */ | ||
2471 | eof = true; | ||
2472 | break; | ||
2473 | } | ||
2362 | } | 2474 | } |
2363 | 2475 | ||
2364 | if ((i == sectorcount-1) && /* last sector requested */ | 2476 | /* find sequential sectors and transfer them all at once */ |
2365 | (!eof)) | 2477 | if (sector != last || count >= FAT_MAX_TRANSFER_SIZE) |
2366 | { | 2478 | { |
2367 | long count = sector - first + 1; | 2479 | /* not sequential/over limit */ |
2368 | rc = transfer(IF_MV(fat_bpb,) first, count, buf, write ); | 2480 | rc = transfer(fat_bpb, last - count + 1, count, buf, write); |
2369 | if (rc < 0) | 2481 | if (rc < 0) |
2370 | return rc * 10 - 2; | 2482 | FAT_ERROR(rc * 10 - 2); |
2483 | |||
2484 | transferred += count; | ||
2485 | buf += count * SECTOR_SIZE; | ||
2486 | count = 0; | ||
2371 | } | 2487 | } |
2372 | 2488 | ||
2373 | last = sector; | 2489 | count++; |
2490 | last = ++sector; | ||
2374 | } | 2491 | } |
2375 | 2492 | ||
2376 | file->lastcluster = cluster; | 2493 | if (count) |
2377 | file->lastsector = sector; | 2494 | { |
2378 | file->clusternum = clusternum; | 2495 | /* transfer any remainder */ |
2379 | file->sectornum = numsec; | 2496 | rc = transfer(fat_bpb, last - count + 1, count, buf, write); |
2380 | file->eof = eof; | 2497 | if (rc < 0) |
2498 | FAT_ERROR(rc * 10 - 3); | ||
2499 | |||
2500 | transferred += count; | ||
2501 | } | ||
2502 | |||
2503 | rc = (eof && write) ? FAT_RC_ENOSPC : (long)transferred; | ||
2504 | fat_error: | ||
2505 | filestr->lastcluster = cluster; | ||
2506 | filestr->lastsector = sector; | ||
2507 | filestr->clusternum = clusternum; | ||
2508 | filestr->sectornum = sectornum; | ||
2509 | filestr->eof = eof; | ||
2510 | |||
2511 | if (rc >= 0) | ||
2512 | DEBUGF("Sectors transferred: %ld\n", rc); | ||
2381 | 2513 | ||
2382 | /* if eof, don't report last block as read/written */ | 2514 | return rc; |
2383 | if (eof) | 2515 | } |
2384 | i--; | ||
2385 | 2516 | ||
2386 | DEBUGF("Sectors written: %ld\n", i); | 2517 | void fat_rewind(struct fat_filestr *filestr) |
2387 | return i; | 2518 | { |
2519 | /* rewind the file position */ | ||
2520 | filestr->lastcluster = filestr->fatfilep->firstcluster; | ||
2521 | filestr->lastsector = 0; | ||
2522 | filestr->clusternum = 0; | ||
2523 | filestr->sectornum = FAT_RW_VAL; | ||
2524 | filestr->eof = false; | ||
2388 | } | 2525 | } |
2389 | 2526 | ||
2390 | int fat_seek(struct fat_file *file, unsigned long seeksector ) | 2527 | int fat_seek(struct fat_filestr *filestr, unsigned long seeksector) |
2391 | { | 2528 | { |
2392 | #ifdef HAVE_MULTIVOLUME | 2529 | const struct fat_file * const file = filestr->fatfilep; |
2393 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2530 | struct bpb * const fat_bpb = FAT_BPB(file->volume); |
2394 | #else | 2531 | if (!fat_bpb) |
2395 | struct bpb* fat_bpb = &fat_bpbs[0]; | 2532 | return -1; |
2396 | #endif | 2533 | |
2397 | long clusternum=0, numclusters=0, sectornum=0, sector=0; | 2534 | int rc; |
2398 | long cluster = file->firstcluster; | 2535 | long cluster = file->firstcluster; |
2399 | long i; | 2536 | unsigned long sector = 0; |
2537 | long clusternum = 0; | ||
2538 | unsigned long sectornum = FAT_RW_VAL; | ||
2400 | 2539 | ||
2401 | #ifdef HAVE_FAT16SUPPORT | 2540 | #ifdef HAVE_FAT16SUPPORT |
2402 | if (cluster < 0) /* FAT16 root dir */ | 2541 | if (fat_bpb->is_fat16 && cluster < 0) /* FAT16 root dir */ |
2403 | seeksector += fat_bpb->rootdiroffset; | 2542 | seeksector += fat_bpb->rootdirsectornum; |
2404 | #endif | 2543 | #endif /* HAVE_FAT16SUPPORT */ |
2544 | |||
2545 | filestr->eof = false; | ||
2546 | if (seeksector) | ||
2547 | { | ||
2548 | if (cluster == 0) | ||
2549 | { | ||
2550 | DEBUGF("Seeking beyond the end of empty file! " | ||
2551 | "(sector %lu, cluster %ld)\n", seeksector, | ||
2552 | seeksector / fat_bpb->bpb_secperclus); | ||
2553 | FAT_ERROR(FAT_SEEK_EOF); | ||
2554 | } | ||
2405 | 2555 | ||
2406 | file->eof = false; | ||
2407 | if (seeksector) { | ||
2408 | /* we need to find the sector BEFORE the requested, since | 2556 | /* we need to find the sector BEFORE the requested, since |
2409 | the file struct stores the last accessed sector */ | 2557 | the file struct stores the last accessed sector */ |
2410 | seeksector--; | 2558 | seeksector--; |
2411 | numclusters = clusternum = seeksector / fat_bpb->bpb_secperclus; | 2559 | clusternum = seeksector / fat_bpb->bpb_secperclus; |
2412 | sectornum = seeksector % fat_bpb->bpb_secperclus; | 2560 | sectornum = seeksector % fat_bpb->bpb_secperclus; |
2413 | 2561 | ||
2414 | if (file->clusternum && clusternum >= file->clusternum) | 2562 | long numclusters = clusternum; |
2563 | |||
2564 | if (filestr->clusternum && clusternum >= filestr->clusternum) | ||
2415 | { | 2565 | { |
2416 | cluster = file->lastcluster; | 2566 | /* seek forward from current position */ |
2417 | numclusters -= file->clusternum; | 2567 | cluster = filestr->lastcluster; |
2568 | numclusters -= filestr->clusternum; | ||
2418 | } | 2569 | } |
2419 | 2570 | ||
2420 | for (i=0; i<numclusters; i++) { | 2571 | for (long i = 0; i < numclusters; i++) |
2421 | cluster = get_next_cluster(IF_MV(fat_bpb,) cluster); | 2572 | { |
2422 | if (!cluster) { | 2573 | cluster = get_next_cluster(fat_bpb, cluster); |
2574 | |||
2575 | if (!cluster) | ||
2576 | { | ||
2423 | DEBUGF("Seeking beyond the end of the file! " | 2577 | DEBUGF("Seeking beyond the end of the file! " |
2424 | "(sector %ld, cluster %ld)\n", seeksector, i); | 2578 | "(sector %lu, cluster %ld)\n", seeksector, i); |
2425 | return -1; | 2579 | FAT_ERROR(FAT_SEEK_EOF); |
2426 | } | 2580 | } |
2427 | } | 2581 | } |
2428 | 2582 | ||
2429 | sector = cluster2sec(IF_MV(fat_bpb,) cluster) + sectornum; | 2583 | sector = cluster2sec(fat_bpb, cluster) + sectornum; |
2430 | } | ||
2431 | else { | ||
2432 | sectornum = -1; | ||
2433 | } | 2584 | } |
2434 | 2585 | ||
2435 | LDEBUGF("fat_seek(%lx, %lx) == %lx, %lx, %lx\n", | 2586 | DEBUGF("%s(%lx, %lx) == %lx, %lx, %lx\n", __func__, |
2436 | file->firstcluster, seeksector, cluster, sector, sectornum); | 2587 | file->firstcluster, seeksector, cluster, sector, sectornum); |
2437 | 2588 | ||
2438 | file->lastcluster = cluster; | 2589 | filestr->lastcluster = cluster; |
2439 | file->lastsector = sector; | 2590 | filestr->lastsector = sector; |
2440 | file->clusternum = clusternum; | 2591 | filestr->clusternum = clusternum; |
2441 | file->sectornum = sectornum + 1; | 2592 | filestr->sectornum = sectornum; |
2442 | return 0; | 2593 | |
2594 | rc = 0; | ||
2595 | fat_error: | ||
2596 | return rc; | ||
2443 | } | 2597 | } |
2444 | 2598 | ||
2445 | int fat_opendir(IF_MV(int volume,) | 2599 | int fat_truncate(const struct fat_filestr *filestr) |
2446 | struct fat_dir *dir, unsigned long startcluster, | ||
2447 | const struct fat_dir *parent_dir) | ||
2448 | { | 2600 | { |
2449 | #ifdef HAVE_MULTIVOLUME | 2601 | DEBUGF("%s(): %lX\n", __func__, filestr->lastcluster); |
2450 | struct bpb* fat_bpb = &fat_bpbs[volume]; | 2602 | |
2451 | /* fixme: remove error check when done */ | 2603 | struct bpb * const fat_bpb = FAT_BPB(filestr->fatfilep->volume); |
2452 | if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted) | 2604 | if (!fat_bpb) |
2453 | { | ||
2454 | LDEBUGF("fat_open() illegal volume %d\n", volume); | ||
2455 | return -1; | 2605 | return -1; |
2456 | } | ||
2457 | #else | ||
2458 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
2459 | #endif | ||
2460 | int rc; | ||
2461 | 2606 | ||
2462 | if (startcluster == 0) | 2607 | int rc = 1; |
2463 | startcluster = fat_bpb->bpb_rootclus; | ||
2464 | 2608 | ||
2465 | rc = fat_open(IF_MV(volume,) startcluster, &dir->file, parent_dir); | 2609 | long last = filestr->lastcluster; |
2466 | if(rc) | 2610 | long next = 0; |
2611 | |||
2612 | /* truncate trailing clusters after the current position */ | ||
2613 | if (last) | ||
2467 | { | 2614 | { |
2468 | DEBUGF( "fat_opendir() - Couldn't open dir" | 2615 | next = get_next_cluster(fat_bpb, last); |
2469 | " (error code %d)\n", rc); | 2616 | int rc2 = update_fat_entry(fat_bpb, last, FAT_EOF_MARK); |
2470 | return rc * 10 - 1; | 2617 | if (rc2 < 0) |
2618 | FAT_ERROR(rc2 * 10 - 2); | ||
2471 | } | 2619 | } |
2472 | |||
2473 | /* assign them after fat_open call so that fat_opendir can be called with the same | ||
2474 | * fat_dir as parent and result */ | ||
2475 | dir->entry = 0; | ||
2476 | dir->sector = 0; | ||
2477 | 2620 | ||
2478 | return 0; | 2621 | int rc2 = free_cluster_chain(fat_bpb, next); |
2622 | if (rc2 <= 0) | ||
2623 | { | ||
2624 | DEBUGF("Failed freeing cluster chain\n"); | ||
2625 | rc = 0; /* still partial success */ | ||
2626 | } | ||
2627 | |||
2628 | fat_error: | ||
2629 | return rc; | ||
2479 | } | 2630 | } |
2480 | 2631 | ||
2481 | int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) | 2632 | |
2633 | /** Directory stream functions **/ | ||
2634 | |||
2635 | int fat_readdir(struct fat_filestr *dirstr, struct fat_dirscan_info *scan, | ||
2636 | struct filestr_cache *cachep, struct fat_direntry *entry) | ||
2482 | { | 2637 | { |
2483 | bool done = false; | 2638 | int rc = 0; |
2484 | int i, j; | 2639 | |
2485 | int rc; | 2640 | /* long file names are stored in special entries; each entry holds up to |
2486 | int order; | 2641 | 13 UTF-16 characters' thus, UTF-8 converted names can be max 255 chars |
2487 | unsigned char firstbyte; | 2642 | (1020 bytes) long, not including the trailing '\0'. */ |
2488 | /* Long file names are stored in special entries. Each entry holds | 2643 | struct fatlong_parse_state lnparse; |
2489 | up to 13 characters. Names can be max 255 chars (not bytes!) long */ | 2644 | fatlong_parse_start(&lnparse); |
2490 | /* The number of long entries in the long name can be retrieve from the first | ||
2491 | * long entry because there are stored in reverse order and have an ordinal */ | ||
2492 | int nb_longs = 0; | ||
2493 | /* The long entries are expected to be in order, so remember the last ordinal */ | ||
2494 | int last_long_ord = 0; | ||
2495 | 2645 | ||
2496 | dir->entrycount = 0; | 2646 | scan->entries = 0; |
2497 | 2647 | ||
2498 | while(!done) | 2648 | while (1) |
2499 | { | 2649 | { |
2500 | if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector ) | 2650 | unsigned int direntry = ++scan->entry; |
2651 | if (direntry >= MAX_DIRENTRIES) | ||
2501 | { | 2652 | { |
2502 | rc = fat_readwrite(&dir->file, 1, dir->sectorcache, false); | 2653 | DEBUGF("%s() - Dir is too large (entry %u)\n", __func__, |
2503 | if (rc == 0) { | 2654 | direntry); |
2504 | /* eof */ | 2655 | FAT_ERROR(-1); |
2505 | entry->name[0] = 0; | 2656 | } |
2506 | break; | 2657 | |
2658 | unsigned long sector = direntry / DIR_ENTRIES_PER_SECTOR; | ||
2659 | if (cachep->sector != sector) | ||
2660 | { | ||
2661 | if (cachep->sector + 1 != sector) | ||
2662 | { | ||
2663 | /* Nothing cached or sector isn't contiguous */ | ||
2664 | int rc2 = fat_seek(dirstr, sector); | ||
2665 | if (rc2 < 0) | ||
2666 | FAT_ERROR(rc2 * 10 - 2); | ||
2507 | } | 2667 | } |
2508 | if (rc < 0) { | 2668 | |
2509 | DEBUGF( "fat_getnext() - Couldn't read dir" | 2669 | int rc2 = fat_readwrite(dirstr, 1, cachep->buffer, false); |
2510 | " (error code %d)\n", rc); | 2670 | if (rc2 <= 0) |
2511 | return rc * 10 - 1; | 2671 | { |
2672 | if (rc2 == 0) | ||
2673 | break; /* eof */ | ||
2674 | |||
2675 | DEBUGF("%s() - Couldn't read dir (err %d)\n", __func__, rc); | ||
2676 | FAT_ERROR(rc2 * 10 - 3); | ||
2512 | } | 2677 | } |
2513 | dir->sector = dir->file.lastsector; | 2678 | |
2679 | cachep->sector = sector; | ||
2514 | } | 2680 | } |
2515 | 2681 | ||
2516 | for (i = dir->entry % DIR_ENTRIES_PER_SECTOR; | 2682 | unsigned int index = direntry % DIR_ENTRIES_PER_SECTOR; |
2517 | i < DIR_ENTRIES_PER_SECTOR; i++) { | 2683 | union raw_dirent *ent = &((union raw_dirent *)cachep->buffer)[index]; |
2518 | unsigned int entrypos = i * DIR_ENTRY_SIZE; | ||
2519 | 2684 | ||
2520 | firstbyte = dir->sectorcache[entrypos]; | 2685 | if (ent->name[0] == 0) |
2521 | dir->entry++; | 2686 | break; /* last entry */ |
2522 | 2687 | ||
2523 | if (firstbyte == 0xe5) { | 2688 | if (ent->name[0] == 0xe5) |
2524 | /* free entry */ | 2689 | { |
2525 | dir->entrycount = 0; | 2690 | scan->entries = 0; |
2526 | continue; | 2691 | continue; /* free entry */ |
2527 | } | 2692 | } |
2528 | 2693 | ||
2529 | if (firstbyte == 0) { | 2694 | ++scan->entries; |
2530 | /* last entry */ | ||
2531 | entry->name[0] = 0; | ||
2532 | dir->entrycount = 0; | ||
2533 | return 0; | ||
2534 | } | ||
2535 | 2695 | ||
2536 | dir->entrycount++; | 2696 | if (IS_LDIR_ATTR(ent->ldir_attr)) |
2537 | 2697 | { | |
2538 | /* LFN entry? */ | 2698 | /* LFN entry */ |
2539 | if ( ( dir->sectorcache[entrypos + FATDIR_ATTR] & | 2699 | if (UNLIKELY(!fatlong_parse_entry(&lnparse, ent, entry))) |
2540 | FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) { | 2700 | { |
2541 | /* extract ordinal */ | 2701 | /* resync so we don't return just the short name if what we |
2542 | order = dir->sectorcache[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY; | 2702 | landed in the middle of is valid (directory changes |
2543 | /* is this entry the first long entry ? (first in order but containing last part) */ | 2703 | between calls likely created the situation; ignoring this |
2544 | if (dir->sectorcache[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) { | 2704 | case can be harmful elsewhere and is destructive to the |
2545 | /* check that order is not too big ! (and non-zero) */ | 2705 | entry series itself) */ |
2546 | if(order <= 0 || order > FATLONG_MAX_ORDER) | 2706 | struct bpb *fat_bpb = FAT_BPB(dirstr->fatfilep->volume); |
2547 | continue; /* ignore the whole LFN, will trigger lots of warnings */ | 2707 | if (!fat_bpb) |
2548 | nb_longs = order; | 2708 | FAT_ERROR(-4); |
2549 | last_long_ord = order; | 2709 | |
2550 | } | 2710 | dc_lock_cache(); |
2551 | else { | 2711 | |
2552 | /* check orphan entry */ | 2712 | while (--scan->entry != FAT_RW_VAL) /* at beginning? */ |
2553 | if (nb_longs == 0) { | 2713 | { |
2554 | logf("fat warning: orphan LFN entry"); | 2714 | ent = cache_direntry(fat_bpb, dirstr, scan->entry); |
2555 | /* ignore */ | ||
2556 | continue; | ||
2557 | } | ||
2558 | |||
2559 | /* check order */ | ||
2560 | if (order != (last_long_ord - 1)) { | ||
2561 | logf("fat warning: wrong LFN ordinal"); | ||
2562 | /* ignore the whole LFN, will trigger lots of warnings */ | ||
2563 | nb_longs = 0; | ||
2564 | } | ||
2565 | |||
2566 | last_long_ord = order; | ||
2567 | } | ||
2568 | 2715 | ||
2569 | /* copy part, reuse [order] for another purpose :) */ | 2716 | /* name[0] == 0 shouldn't happen here but... */ |
2570 | order = (order - 1) * FATLONG_NAME_BYTES_PER_ENTRY; | 2717 | if (!ent || ent->name[0] == 0 || ent->name[0] == 0xe5 || |
2571 | for(j = 0; j < FATLONG_NAME_CHUNKS; j++) { | 2718 | !IS_LDIR_ATTR(ent->ldir_attr)) |
2572 | memcpy(dir->longname + order, | 2719 | break; |
2573 | dir->sectorcache + entrypos + FATLONG_NAME_POS[j], | ||
2574 | FATLONG_NAME_SIZE[j]); | ||
2575 | order += FATLONG_NAME_SIZE[j]; | ||
2576 | } | 2720 | } |
2721 | |||
2722 | dc_unlock_cache(); | ||
2723 | |||
2724 | /* retry it once from the new position */ | ||
2725 | scan->entries = 0; | ||
2726 | continue; | ||
2577 | } | 2727 | } |
2578 | else { | 2728 | } |
2579 | if ( parse_direntry(entry, dir->sectorcache + entrypos) ) { | 2729 | else if (!IS_VOL_ID_ATTR(ent->attr)) /* ignore volume id entry */ |
2580 | 2730 | { | |
2581 | /* don't return volume id entry */ | 2731 | rc = 1; |
2582 | if ( (entry->attr & | 2732 | |
2583 | (FAT_ATTR_VOLUME_ID|FAT_ATTR_DIRECTORY)) | 2733 | if (!fatlong_parse_finish(&lnparse, ent, entry)) |
2584 | == FAT_ATTR_VOLUME_ID) | 2734 | { |
2585 | continue; | 2735 | /* the long entries failed to pass all checks or there is |
2586 | 2736 | just a short entry. */ | |
2587 | /* replace shortname with longname? */ | 2737 | DEBUGF("SN-DOS:'%s'", entry->shortname); |
2588 | /* check that the long name is complete */ | 2738 | strcpy(entry->name, entry->shortname); |
2589 | if (nb_longs != 0 && last_long_ord == 1) { | 2739 | scan->entries = 1; |
2590 | /* hold a copy of the shortname in case the long one is too long */ | 2740 | rc = 2; /* name is OEM */ |
2591 | unsigned char shortname[13]; /* 8+3+dot+\0 */ | ||
2592 | int longname_utf8len = 0; | ||
2593 | /* One character at a time, add 1 for trailing \0, 4 is the maximum size | ||
2594 | * of a UTF8 encoded character in rockbox */ | ||
2595 | unsigned char longname_utf8segm[4 + 1]; | ||
2596 | unsigned short ucs; | ||
2597 | int segm_utf8len; | ||
2598 | /* Temporarily store short name */ | ||
2599 | strcpy(shortname, entry->name); | ||
2600 | entry->name[0] = 0; | ||
2601 | |||
2602 | /* Convert the FAT name to a utf8-encoded one. | ||
2603 | * The name is not necessary NUL-terminated ! */ | ||
2604 | for (j = 0; j < nb_longs * FATLONG_NAME_BYTES_PER_ENTRY; j += 2) { | ||
2605 | ucs = dir->longname[j] | (dir->longname[j + 1] << 8); | ||
2606 | if(ucs == 0 || ucs == FAT_LONGNAME_PAD_UCS) | ||
2607 | break; | ||
2608 | /* utf8encode will return a pointer after the converted | ||
2609 | * string, subtract the pointer to the start to get the length of it */ | ||
2610 | segm_utf8len = utf8encode(ucs, longname_utf8segm) - longname_utf8segm; | ||
2611 | |||
2612 | /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */ | ||
2613 | if (longname_utf8len + segm_utf8len >= FAT_FILENAME_BYTES) { | ||
2614 | /* force use of short name */ | ||
2615 | longname_utf8len = FAT_FILENAME_BYTES + 1; | ||
2616 | break; /* fallback later */ | ||
2617 | } | ||
2618 | else { | ||
2619 | longname_utf8segm[segm_utf8len] = 0; | ||
2620 | strcat(entry->name + longname_utf8len, longname_utf8segm); | ||
2621 | longname_utf8len += segm_utf8len; | ||
2622 | } | ||
2623 | } | ||
2624 | |||
2625 | /* Does the utf8-encoded name fit into the entry? */ | ||
2626 | /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */ | ||
2627 | if (longname_utf8len >= FAT_FILENAME_BYTES) { | ||
2628 | /* Take the short DOS name. Need to utf8-encode it | ||
2629 | since it may contain chars from the upper half of | ||
2630 | the OEM code page which wouldn't be a valid utf8. | ||
2631 | Beware: this file will be shown with strange | ||
2632 | glyphs in file browser since unicode 0x80 to 0x9F | ||
2633 | are control characters. */ | ||
2634 | logf("SN-DOS: %s", shortname); | ||
2635 | unsigned char *utf8; | ||
2636 | utf8 = iso_decode(shortname, entry->name, -1, | ||
2637 | strlen(shortname)); | ||
2638 | *utf8 = 0; | ||
2639 | logf("SN: %s", entry->name); | ||
2640 | } else { | ||
2641 | logf("LN: %s", entry->name); | ||
2642 | logf("LNLen: %d", longname_utf8len); | ||
2643 | } | ||
2644 | } | ||
2645 | done = true; | ||
2646 | i++; | ||
2647 | break; | ||
2648 | } | ||
2649 | } | 2741 | } |
2742 | |||
2743 | DEBUGF("LN:\"%s\"", entry->name); | ||
2744 | break; | ||
2650 | } | 2745 | } |
2746 | } /* end while */ | ||
2747 | |||
2748 | fat_error: | ||
2749 | if (rc <= 0) | ||
2750 | { | ||
2751 | /* error or eod; stay on last good position */ | ||
2752 | fat_empty_fat_direntry(entry); | ||
2753 | scan->entry--; | ||
2754 | scan->entries = 0; | ||
2651 | } | 2755 | } |
2756 | |||
2757 | return rc; | ||
2758 | } | ||
2759 | |||
2760 | void fat_rewinddir(struct fat_dirscan_info *scan) | ||
2761 | { | ||
2762 | /* rewind the directory scan counter to the beginning */ | ||
2763 | scan->entry = FAT_RW_VAL; | ||
2764 | scan->entries = 0; | ||
2765 | } | ||
2766 | |||
2767 | |||
2768 | /** Mounting and unmounting functions **/ | ||
2769 | |||
2770 | bool fat_ismounted(IF_MV_NONVOID(int volume)) | ||
2771 | { | ||
2772 | return !!FAT_BPB(volume); | ||
2773 | } | ||
2774 | |||
2775 | int fat_mount(IF_MV(int volume,) IF_MD(int drive,) unsigned long startsector) | ||
2776 | { | ||
2777 | int rc; | ||
2778 | |||
2779 | struct bpb * const fat_bpb = &fat_bpbs[IF_MV_VOL(volume)]; | ||
2780 | if (fat_bpb->mounted) | ||
2781 | FAT_ERROR(-1); /* double mount */ | ||
2782 | |||
2783 | /* fill-in basic info first */ | ||
2784 | fat_bpb->startsector = startsector; | ||
2785 | #ifdef HAVE_MULTIVOLUME | ||
2786 | fat_bpb->volume = volume; | ||
2787 | #endif | ||
2788 | #ifdef HAVE_MULTIDRIVE | ||
2789 | fat_bpb->drive = drive; | ||
2790 | #endif | ||
2791 | |||
2792 | rc = fat_mount_internal(fat_bpb); | ||
2793 | if (rc < 0) | ||
2794 | FAT_ERROR(rc * 10 - 2); | ||
2795 | |||
2796 | /* it worked */ | ||
2797 | fat_bpb->mounted = true; | ||
2798 | |||
2799 | /* calculate freecount if unset */ | ||
2800 | if (fat_bpb->fsinfo.freecount == 0xffffffff) | ||
2801 | fat_recalc_free(IF_MV(fat_bpb->volume)); | ||
2802 | |||
2803 | DEBUGF("Freecount: %ld\n", (unsigned long)fat_bpb->fsinfo.freecount); | ||
2804 | DEBUGF("Nextfree: 0x%lx\n", (unsigned long)fat_bpb->fsinfo.nextfree); | ||
2805 | DEBUGF("Cluster count: 0x%lx\n", fat_bpb->dataclusters); | ||
2806 | DEBUGF("Sectors per cluster: %d\n", fat_bpb->bpb_secperclus); | ||
2807 | DEBUGF("FAT sectors: 0x%lx\n", fat_bpb->fatsize); | ||
2808 | |||
2809 | rc = 0; | ||
2810 | fat_error: | ||
2811 | return rc; | ||
2812 | } | ||
2813 | |||
2814 | int fat_unmount(IF_MV_NONVOID(int volume)) | ||
2815 | { | ||
2816 | struct bpb * const fat_bpb = FAT_BPB(volume); | ||
2817 | if (!fat_bpb) | ||
2818 | return -1; /* not mounted */ | ||
2819 | |||
2820 | /* free the entries for this volume */ | ||
2821 | cache_discard(IF_MV(fat_bpb)); | ||
2822 | fat_bpb->mounted = false; | ||
2823 | |||
2652 | return 0; | 2824 | return 0; |
2653 | } | 2825 | } |
2654 | 2826 | ||
2827 | |||
2828 | /** Debug screen stuff **/ | ||
2829 | |||
2830 | #ifdef MAX_LOG_SECTOR_SIZE | ||
2831 | int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume)) | ||
2832 | { | ||
2833 | int bytes = 0; | ||
2834 | |||
2835 | struct bpb * const fat_bpb = FAT_BPB(volume); | ||
2836 | if (fat_bpb) | ||
2837 | bytes = fat_bpb->bpb_bytspersec; | ||
2838 | |||
2839 | return bytes; | ||
2840 | } | ||
2841 | #endif /* MAX_LOG_SECTOR_SIZE */ | ||
2842 | |||
2655 | unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)) | 2843 | unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)) |
2656 | { | 2844 | { |
2657 | #ifndef HAVE_MULTIVOLUME | 2845 | unsigned int size = 0; |
2658 | const int volume = 0; | 2846 | |
2659 | #endif | 2847 | struct bpb * const fat_bpb = FAT_BPB(volume); |
2660 | struct bpb* fat_bpb = &fat_bpbs[volume]; | 2848 | if (fat_bpb) |
2661 | return fat_bpb->bpb_secperclus * SECTOR_SIZE; | 2849 | size = fat_bpb->bpb_secperclus * SECTOR_SIZE; |
2850 | |||
2851 | return size; | ||
2662 | } | 2852 | } |
2663 | 2853 | ||
2664 | #ifdef HAVE_MULTIVOLUME | 2854 | void fat_recalc_free(IF_MV_NONVOID(int volume)) |
2665 | bool fat_ismounted(int volume) | ||
2666 | { | 2855 | { |
2667 | return (volume<NUM_VOLUMES && fat_bpbs[volume].mounted); | 2856 | struct bpb * const fat_bpb = FAT_BPB(volume); |
2857 | if (!fat_bpb) | ||
2858 | return; | ||
2859 | |||
2860 | dc_lock_cache(); | ||
2861 | fat_recalc_free_internal(fat_bpb); | ||
2862 | dc_unlock_cache(); | ||
2863 | } | ||
2864 | |||
2865 | bool fat_size(IF_MV(int volume,) unsigned long *size, unsigned long *free) | ||
2866 | { | ||
2867 | struct bpb * const fat_bpb = FAT_BPB(volume); | ||
2868 | if (!fat_bpb) | ||
2869 | return false; | ||
2870 | |||
2871 | unsigned long factor = fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024; | ||
2872 | |||
2873 | if (size) *size = fat_bpb->dataclusters * factor; | ||
2874 | if (free) *free = fat_bpb->fsinfo.freecount * factor; | ||
2875 | |||
2876 | return true; | ||
2877 | } | ||
2878 | |||
2879 | |||
2880 | /** Misc. **/ | ||
2881 | |||
2882 | void fat_empty_fat_direntry(struct fat_direntry *entry) | ||
2883 | { | ||
2884 | entry->name[0] = 0; | ||
2885 | entry->shortname[0] = 0; | ||
2886 | entry->attr = 0; | ||
2887 | entry->crttimetenth = 0; | ||
2888 | entry->crttime = 0; | ||
2889 | entry->crtdate = 0; | ||
2890 | entry->lstaccdate = 0; | ||
2891 | entry->wrttime = 0; | ||
2892 | entry->wrtdate = 0; | ||
2893 | entry->filesize = 0; | ||
2894 | entry->firstcluster = 0; | ||
2895 | } | ||
2896 | |||
2897 | time_t fattime_mktime(uint16_t fatdate, uint16_t fattime) | ||
2898 | { | ||
2899 | /* this knows our mktime() only uses these struct tm fields */ | ||
2900 | struct tm tm; | ||
2901 | tm.tm_sec = ((fattime ) & 0x1f) * 2; | ||
2902 | tm.tm_min = ((fattime >> 5) & 0x3f); | ||
2903 | tm.tm_hour = ((fattime >> 11) ); | ||
2904 | tm.tm_mday = ((fatdate ) & 0x1f); | ||
2905 | tm.tm_mon = ((fatdate >> 5) & 0x0f) - 1; | ||
2906 | tm.tm_year = ((fatdate >> 9) ) + 80; | ||
2907 | |||
2908 | return mktime(&tm); | ||
2909 | } | ||
2910 | |||
2911 | void fat_init(void) | ||
2912 | { | ||
2913 | dc_lock_cache(); | ||
2914 | |||
2915 | /* mark the possible volumes as not mounted */ | ||
2916 | for (unsigned int i = 0; i < NUM_VOLUMES; i++) | ||
2917 | { | ||
2918 | dc_discard_all(IF_MV(i)); | ||
2919 | fat_bpbs[i].mounted = false; | ||
2920 | } | ||
2921 | |||
2922 | dc_unlock_cache(); | ||
2668 | } | 2923 | } |
2669 | #endif | ||