Make bin2c ensure that the generated C arrays are 32bit aligned. Building nrv2e_d8...
[kugel-rb.git] / firmware / drivers / fat.c
blob169672f08eddf3cb5b71b6670cfdc2e8c37ad439
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <stdbool.h>
26 #include "fat.h"
27 #include "storage.h"
28 #include "debug.h"
29 #include "panic.h"
30 #include "system.h"
31 #include "timefuncs.h"
32 #include "kernel.h"
33 #include "rbunicode.h"
34 #include "logf.h"
36 #define BYTES2INT16(array,pos) \
37 (array[pos] | (array[pos+1] << 8 ))
38 #define BYTES2INT32(array,pos) \
39 ((long)array[pos] | ((long)array[pos+1] << 8 ) | \
40 ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
42 #define FATTYPE_FAT12 0
43 #define FATTYPE_FAT16 1
44 #define FATTYPE_FAT32 2
46 /* BPB offsets; generic */
47 #define BS_JMPBOOT 0
48 #define BS_OEMNAME 3
49 #define BPB_BYTSPERSEC 11
50 #define BPB_SECPERCLUS 13
51 #define BPB_RSVDSECCNT 14
52 #define BPB_NUMFATS 16
53 #define BPB_ROOTENTCNT 17
54 #define BPB_TOTSEC16 19
55 #define BPB_MEDIA 21
56 #define BPB_FATSZ16 22
57 #define BPB_SECPERTRK 24
58 #define BPB_NUMHEADS 26
59 #define BPB_HIDDSEC 28
60 #define BPB_TOTSEC32 32
62 /* fat12/16 */
63 #define BS_DRVNUM 36
64 #define BS_RESERVED1 37
65 #define BS_BOOTSIG 38
66 #define BS_VOLID 39
67 #define BS_VOLLAB 43
68 #define BS_FILSYSTYPE 54
70 /* fat32 */
71 #define BPB_FATSZ32 36
72 #define BPB_EXTFLAGS 40
73 #define BPB_FSVER 42
74 #define BPB_ROOTCLUS 44
75 #define BPB_FSINFO 48
76 #define BPB_BKBOOTSEC 50
77 #define BS_32_DRVNUM 64
78 #define BS_32_BOOTSIG 66
79 #define BS_32_VOLID 67
80 #define BS_32_VOLLAB 71
81 #define BS_32_FILSYSTYPE 82
83 #define BPB_LAST_WORD 510
86 /* attributes */
87 #define FAT_ATTR_LONG_NAME (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
88 FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID)
89 #define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
90 FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \
91 FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE )
93 /* NTRES flags */
94 #define FAT_NTRES_LC_NAME 0x08
95 #define FAT_NTRES_LC_EXT 0x10
97 #define FATDIR_NAME 0
98 #define FATDIR_ATTR 11
99 #define FATDIR_NTRES 12
100 #define FATDIR_CRTTIMETENTH 13
101 #define FATDIR_CRTTIME 14
102 #define FATDIR_CRTDATE 16
103 #define FATDIR_LSTACCDATE 18
104 #define FATDIR_FSTCLUSHI 20
105 #define FATDIR_WRTTIME 22
106 #define FATDIR_WRTDATE 24
107 #define FATDIR_FSTCLUSLO 26
108 #define FATDIR_FILESIZE 28
110 #define FATLONG_ORDER 0
111 #define FATLONG_TYPE 12
112 #define FATLONG_CHKSUM 13
114 #define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4)
115 #define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2)
116 #define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE)
117 #define DIR_ENTRY_SIZE 32
118 #define NAME_BYTES_PER_ENTRY 13
119 #define FAT_BAD_MARK 0x0ffffff7
120 #define FAT_EOF_MARK 0x0ffffff8
121 #define FAT_LONGNAME_PAD_BYTE 0xff
122 #define FAT_LONGNAME_PAD_UCS 0xffff
124 struct fsinfo {
125 unsigned long freecount; /* last known free cluster count */
126 unsigned long nextfree; /* first cluster to start looking for free
127 clusters, or 0xffffffff for no hint */
129 /* fsinfo offsets */
130 #define FSINFO_FREECOUNT 488
131 #define FSINFO_NEXTFREE 492
133 /* Note: This struct doesn't hold the raw values after mounting if
134 * bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte
135 * physical sectors. */
136 struct bpb
138 int bpb_bytspersec; /* Bytes per sector, typically 512 */
139 unsigned int bpb_secperclus; /* Sectors per cluster */
140 int bpb_rsvdseccnt; /* Number of reserved sectors */
141 int bpb_numfats; /* Number of FAT structures, typically 2 */
142 int bpb_totsec16; /* Number of sectors on the volume (old 16-bit) */
143 int bpb_media; /* Media type (typically 0xf0 or 0xf8) */
144 int bpb_fatsz16; /* Number of used sectors per FAT structure */
145 unsigned long bpb_totsec32; /* Number of sectors on the volume
146 (new 32-bit) */
147 unsigned int last_word; /* 0xAA55 */
149 /**** FAT32 specific *****/
150 long bpb_fatsz32;
151 long bpb_rootclus;
152 long bpb_fsinfo;
154 /* variables for internal use */
155 unsigned long fatsize;
156 unsigned long totalsectors;
157 unsigned long rootdirsector;
158 unsigned long firstdatasector;
159 unsigned long startsector;
160 unsigned long dataclusters;
161 struct fsinfo fsinfo;
162 #ifdef HAVE_FAT16SUPPORT
163 int bpb_rootentcnt; /* Number of dir entries in the root */
164 /* internals for FAT16 support */
165 bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */
166 unsigned int rootdiroffset; /* sector offset of root dir relative to start
167 * of first pseudo cluster */
168 #endif /* #ifdef HAVE_FAT16SUPPORT */
169 #ifdef HAVE_MULTIVOLUME
170 #ifdef HAVE_MULTIDRIVE
171 int drive; /* on which physical device is this located */
172 #endif
173 bool mounted; /* flag if this volume is mounted */
174 #endif
177 static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */
178 static bool initialized = false;
180 static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb));
181 static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb));
182 static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb));
183 static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,)
184 long secnum, bool dirty);
185 static void create_dos_name(const unsigned char *name, unsigned char *newname);
186 static void randomize_dos_name(unsigned char *name);
187 static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,)
188 unsigned long start);
189 static int transfer(IF_MV2(struct bpb* fat_bpb,) unsigned long start,
190 long count, char* buf, bool write );
192 #define FAT_CACHE_SIZE 0x20
193 #define FAT_CACHE_MASK (FAT_CACHE_SIZE-1)
195 struct fat_cache_entry
197 long secnum;
198 bool inuse;
199 bool dirty;
200 #ifdef HAVE_MULTIVOLUME
201 struct bpb* fat_vol ; /* shared cache for all volumes */
202 #endif
205 static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE];
206 static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE];
207 static struct mutex cache_mutex SHAREDBSS_ATTR;
209 #if defined(HAVE_HOTSWAP) && !(CONFIG_STORAGE & STORAGE_MMC) /* A better condition ?? */
210 void fat_lock(void)
212 mutex_lock(&cache_mutex);
215 void fat_unlock(void)
217 mutex_unlock(&cache_mutex);
219 #endif
221 static long cluster2sec(IF_MV2(struct bpb* fat_bpb,) long cluster)
223 #ifndef HAVE_MULTIVOLUME
224 struct bpb* fat_bpb = &fat_bpbs[0];
225 #endif
226 #ifdef HAVE_FAT16SUPPORT
227 /* negative clusters (FAT16 root dir) don't get the 2 offset */
228 int zerocluster = cluster < 0 ? 0 : 2;
229 #else
230 const long zerocluster = 2;
231 #endif
233 if (cluster > (long)(fat_bpb->dataclusters + 1))
235 DEBUGF( "cluster2sec() - Bad cluster number (%ld)\n", cluster);
236 return -1;
239 return (cluster - zerocluster) * fat_bpb->bpb_secperclus
240 + fat_bpb->firstdatasector;
243 void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
245 #ifndef HAVE_MULTIVOLUME
246 const int volume = 0;
247 #endif
248 struct bpb* fat_bpb = &fat_bpbs[volume];
249 if (size)
250 *size = fat_bpb->dataclusters * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024);
251 if (free)
252 *free = fat_bpb->fsinfo.freecount * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024);
255 void fat_init(void)
257 unsigned int i;
259 if (!initialized)
261 initialized = true;
262 mutex_init(&cache_mutex);
265 #ifdef HAVE_PRIORITY_SCHEDULING
266 /* Disable this because it is dangerous due to the assumption that
267 * mutex_unlock won't yield */
268 mutex_set_preempt(&cache_mutex, false);
269 #endif
271 /* mark the FAT cache as unused */
272 for(i = 0;i < FAT_CACHE_SIZE;i++)
274 fat_cache[i].secnum = 8; /* We use a "safe" sector just in case */
275 fat_cache[i].inuse = false;
276 fat_cache[i].dirty = false;
277 #ifdef HAVE_MULTIVOLUME
278 fat_cache[i].fat_vol = NULL;
279 #endif
281 #ifdef HAVE_MULTIVOLUME
282 /* mark the possible volumes as not mounted */
283 for (i=0; i<NUM_VOLUMES;i++)
285 fat_bpbs[i].mounted = false;
287 #endif
290 int fat_mount(IF_MV2(int volume,) IF_MD2(int drive,) long startsector)
292 #ifndef HAVE_MULTIVOLUME
293 const int volume = 0;
294 #endif
295 struct bpb* fat_bpb = &fat_bpbs[volume];
296 unsigned char buf[SECTOR_SIZE];
297 int rc;
298 int secmult;
299 long datasec;
300 #ifdef HAVE_FAT16SUPPORT
301 int rootdirsectors;
302 #endif
304 /* Read the sector */
305 rc = storage_read_sectors(drive, startsector,1,buf);
306 if(rc)
308 DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)\n", rc);
309 return rc * 10 - 1;
312 memset(fat_bpb, 0, sizeof(struct bpb));
313 fat_bpb->startsector = startsector;
314 #ifdef HAVE_MULTIDRIVE
315 fat_bpb->drive = drive;
316 #endif
318 fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC);
319 secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE;
320 /* Sanity check is performed later */
322 fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS];
323 fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf,BPB_RSVDSECCNT);
324 fat_bpb->bpb_numfats = buf[BPB_NUMFATS];
325 fat_bpb->bpb_media = buf[BPB_MEDIA];
326 fat_bpb->bpb_fatsz16 = secmult * BYTES2INT16(buf,BPB_FATSZ16);
327 fat_bpb->bpb_fatsz32 = secmult * BYTES2INT32(buf,BPB_FATSZ32);
328 fat_bpb->bpb_totsec16 = secmult * BYTES2INT16(buf,BPB_TOTSEC16);
329 fat_bpb->bpb_totsec32 = secmult * BYTES2INT32(buf,BPB_TOTSEC32);
330 fat_bpb->last_word = BYTES2INT16(buf,BPB_LAST_WORD);
332 /* calculate a few commonly used values */
333 if (fat_bpb->bpb_fatsz16 != 0)
334 fat_bpb->fatsize = fat_bpb->bpb_fatsz16;
335 else
336 fat_bpb->fatsize = fat_bpb->bpb_fatsz32;
338 if (fat_bpb->bpb_totsec16 != 0)
339 fat_bpb->totalsectors = fat_bpb->bpb_totsec16;
340 else
341 fat_bpb->totalsectors = fat_bpb->bpb_totsec32;
343 #ifdef HAVE_FAT16SUPPORT
344 fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT);
345 if (!fat_bpb->bpb_bytspersec)
346 return -2;
347 rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE
348 + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec);
349 #endif /* #ifdef HAVE_FAT16SUPPORT */
351 fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt
352 #ifdef HAVE_FAT16SUPPORT
353 + rootdirsectors
354 #endif
355 + fat_bpb->bpb_numfats * fat_bpb->fatsize;
357 /* Determine FAT type */
358 datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector;
359 if (fat_bpb->bpb_secperclus)
360 fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus;
361 else
362 return -2;
364 #ifdef TEST_FAT
366 we are sometimes testing with "illegally small" fat32 images,
367 so we don't use the proper fat32 test case for test code
369 if ( fat_bpb->bpb_fatsz16 )
370 #else
371 if ( fat_bpb->dataclusters < 65525 )
372 #endif
373 { /* FAT16 */
374 #ifdef HAVE_FAT16SUPPORT
375 fat_bpb->is_fat16 = true;
376 if (fat_bpb->dataclusters < 4085)
377 { /* FAT12 */
378 DEBUGF("This is FAT12. Go away!\n");
379 return -2;
381 #else /* #ifdef HAVE_FAT16SUPPORT */
382 DEBUGF("This is not FAT32. Go away!\n");
383 return -2;
384 #endif /* #ifndef HAVE_FAT16SUPPORT */
387 #ifdef HAVE_FAT16SUPPORT
388 if (fat_bpb->is_fat16)
389 { /* FAT16 specific part of BPB */
390 int dirclusters;
391 fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt
392 + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16;
393 dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1)
394 / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */
395 /* I assign negative pseudo cluster numbers for the root directory,
396 their range is counted upward until -1. */
397 fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data*/
398 fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus
399 - rootdirsectors;
401 else
402 #endif /* #ifdef HAVE_FAT16SUPPORT */
403 { /* FAT32 specific part of BPB */
404 fat_bpb->bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS);
405 fat_bpb->bpb_fsinfo = secmult * BYTES2INT16(buf,BPB_FSINFO);
406 fat_bpb->rootdirsector = cluster2sec(IF_MV2(fat_bpb,)
407 fat_bpb->bpb_rootclus);
410 rc = bpb_is_sane(IF_MV(fat_bpb));
411 if (rc < 0)
413 DEBUGF( "fat_mount() - BPB is not sane\n");
414 return rc * 10 - 3;
417 #ifdef HAVE_FAT16SUPPORT
418 if (fat_bpb->is_fat16)
420 fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */
421 fat_bpb->fsinfo.nextfree = 0xffffffff;
423 else
424 #endif /* #ifdef HAVE_FAT16SUPPORT */
426 /* Read the fsinfo sector */
427 rc = storage_read_sectors(drive,
428 startsector + fat_bpb->bpb_fsinfo, 1, buf);
429 if (rc < 0)
431 DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)\n", rc);
432 return rc * 10 - 4;
434 fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT);
435 fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE);
438 /* calculate freecount if unset */
439 if ( fat_bpb->fsinfo.freecount == 0xffffffff )
441 fat_recalc_free(IF_MV(volume));
444 LDEBUGF("Freecount: %ld\n",fat_bpb->fsinfo.freecount);
445 LDEBUGF("Nextfree: 0x%lx\n",fat_bpb->fsinfo.nextfree);
446 LDEBUGF("Cluster count: 0x%lx\n",fat_bpb->dataclusters);
447 LDEBUGF("Sectors per cluster: %d\n",fat_bpb->bpb_secperclus);
448 LDEBUGF("FAT sectors: 0x%lx\n",fat_bpb->fatsize);
450 #ifdef HAVE_MULTIVOLUME
451 fat_bpb->mounted = true;
452 #endif
454 return 0;
457 #ifdef HAVE_HOTSWAP
458 int fat_unmount(int volume, bool flush)
460 int rc;
461 #ifdef HAVE_MULTIVOLUME
462 struct bpb* fat_bpb = &fat_bpbs[volume];
463 #else
464 (void)volume;
465 #endif
467 if(flush)
469 rc = flush_fat(IF_MV(fat_bpb)); /* the clean way, while still alive */
471 else
472 { /* volume is not accessible any more, e.g. MMC removed */
473 int i;
474 mutex_lock(&cache_mutex);
475 for(i = 0;i < FAT_CACHE_SIZE;i++)
477 struct fat_cache_entry *fce = &fat_cache[i];
478 if(fce->inuse
479 #ifdef HAVE_MULTIVOLUME
480 && fce->fat_vol == fat_bpb
481 #endif
484 fce->inuse = false; /* discard all from that volume */
485 fce->dirty = false;
488 mutex_unlock(&cache_mutex);
489 rc = 0;
491 #ifdef HAVE_MULTIVOLUME
492 fat_bpb->mounted = false;
493 #endif
494 return rc;
496 #endif /* #ifdef HAVE_HOTSWAP */
498 void fat_recalc_free(IF_MV_NONVOID(int volume))
500 #ifndef HAVE_MULTIVOLUME
501 const int volume = 0;
502 #endif
503 struct bpb* fat_bpb = &fat_bpbs[volume];
504 long free = 0;
505 unsigned long i;
506 #ifdef HAVE_FAT16SUPPORT
507 if (fat_bpb->is_fat16)
509 for (i = 0; i<fat_bpb->fatsize; i++) {
510 unsigned int j;
511 unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
512 for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) {
513 unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j;
514 if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
515 break;
517 if (letoh16(fat[j]) == 0x0000) {
518 free++;
519 if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
520 fat_bpb->fsinfo.nextfree = c;
525 else
526 #endif /* #ifdef HAVE_FAT16SUPPORT */
528 for (i = 0; i<fat_bpb->fatsize; i++) {
529 unsigned int j;
530 unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
531 for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) {
532 unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j;
533 if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
534 break;
536 if (!(letoh32(fat[j]) & 0x0fffffff)) {
537 free++;
538 if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
539 fat_bpb->fsinfo.nextfree = c;
544 fat_bpb->fsinfo.freecount = free;
545 update_fsinfo(IF_MV(fat_bpb));
548 static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb))
550 #ifndef HAVE_MULTIVOLUME
551 struct bpb* fat_bpb = &fat_bpbs[0];
552 #endif
553 if(fat_bpb->bpb_bytspersec % SECTOR_SIZE)
555 DEBUGF( "bpb_is_sane() - Error: sector size is not sane (%d)\n",
556 fat_bpb->bpb_bytspersec);
557 return -1;
559 if((long)fat_bpb->bpb_secperclus * (long)fat_bpb->bpb_bytspersec
560 > 128L*1024L)
562 DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K "
563 "(%d * %d = %d)\n",
564 fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus,
565 fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus);
566 return -2;
568 if(fat_bpb->bpb_numfats != 2)
570 DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n",
571 fat_bpb->bpb_numfats);
573 if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8)
575 DEBUGF( "bpb_is_sane() - Warning: Non-standard "
576 "media type (0x%02x)\n",
577 fat_bpb->bpb_media);
579 if(fat_bpb->last_word != 0xaa55)
581 DEBUGF( "bpb_is_sane() - Error: Last word is not "
582 "0xaa55 (0x%04x)\n", fat_bpb->last_word);
583 return -3;
586 if (fat_bpb->fsinfo.freecount >
587 (fat_bpb->totalsectors - fat_bpb->firstdatasector)/
588 fat_bpb->bpb_secperclus)
590 DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size "
591 "(0x%04lx)\n", fat_bpb->fsinfo.freecount);
592 return -4;
595 return 0;
598 static void flush_fat_sector(struct fat_cache_entry *fce,
599 unsigned char *sectorbuf)
601 int rc;
602 long secnum;
604 /* With multivolume, use only the FAT info from the cached sector! */
605 #ifdef HAVE_MULTIVOLUME
606 secnum = fce->secnum + fce->fat_vol->startsector;
607 #else
608 secnum = fce->secnum + fat_bpbs[0].startsector;
609 #endif
611 /* Write to the first FAT */
612 rc = storage_write_sectors(fce->fat_vol->drive,
613 secnum, 1,
614 sectorbuf);
615 if(rc < 0)
617 panicf("flush_fat_sector() - Could not write sector %ld"
618 " (error %d)\n",
619 secnum, rc);
621 #ifdef HAVE_MULTIVOLUME
622 if(fce->fat_vol->bpb_numfats > 1)
623 #else
624 if(fat_bpbs[0].bpb_numfats > 1)
625 #endif
627 /* Write to the second FAT */
628 #ifdef HAVE_MULTIVOLUME
629 secnum += fce->fat_vol->fatsize;
630 #else
631 secnum += fat_bpbs[0].fatsize;
632 #endif
633 rc = storage_write_sectors(fce->fat_vol->drive,
634 secnum, 1, sectorbuf);
635 if(rc < 0)
637 panicf("flush_fat_sector() - Could not write sector %ld"
638 " (error %d)\n",
639 secnum, rc);
642 fce->dirty = false;
645 /* Note: The returned pointer is only safely valid until the next
646 task switch! (Any subsequent ata read/write may yield.) */
647 static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,)
648 long fatsector, bool dirty)
650 #ifndef HAVE_MULTIVOLUME
651 struct bpb* fat_bpb = &fat_bpbs[0];
652 #endif
653 long secnum = fatsector + fat_bpb->bpb_rsvdseccnt;
654 int cache_index = secnum & FAT_CACHE_MASK;
655 struct fat_cache_entry *fce = &fat_cache[cache_index];
656 unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0];
657 int rc;
659 mutex_lock(&cache_mutex); /* make changes atomic */
661 /* Delete the cache entry if it isn't the sector we want */
662 if(fce->inuse && (fce->secnum != secnum
663 #ifdef HAVE_MULTIVOLUME
664 || fce->fat_vol != fat_bpb
665 #endif
668 /* Write back if it is dirty */
669 if(fce->dirty)
671 flush_fat_sector(fce, sectorbuf);
673 fce->inuse = false;
676 /* Load the sector if it is not cached */
677 if(!fce->inuse)
679 rc = storage_read_sectors(fat_bpb->drive,
680 secnum + fat_bpb->startsector,1,
681 sectorbuf);
682 if(rc < 0)
684 DEBUGF( "cache_fat_sector() - Could not read sector %ld"
685 " (error %d)\n", secnum, rc);
686 mutex_unlock(&cache_mutex);
687 return NULL;
689 fce->inuse = true;
690 fce->secnum = secnum;
691 #ifdef HAVE_MULTIVOLUME
692 fce->fat_vol = fat_bpb;
693 #endif
695 if (dirty)
696 fce->dirty = true; /* dirt remains, sticky until flushed */
697 mutex_unlock(&cache_mutex);
698 return sectorbuf;
701 static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,)
702 unsigned long startcluster)
704 #ifndef HAVE_MULTIVOLUME
705 struct bpb* fat_bpb = &fat_bpbs[0];
706 #endif
707 unsigned long sector;
708 unsigned long offset;
709 unsigned long i;
711 #ifdef HAVE_FAT16SUPPORT
712 if (fat_bpb->is_fat16)
714 sector = startcluster / CLUSTERS_PER_FAT16_SECTOR;
715 offset = startcluster % CLUSTERS_PER_FAT16_SECTOR;
717 for (i = 0; i<fat_bpb->fatsize; i++) {
718 unsigned int j;
719 unsigned int nr = (i + sector) % fat_bpb->fatsize;
720 unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false);
721 if ( !fat )
722 break;
723 for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) {
724 int k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR;
725 if (letoh16(fat[k]) == 0x0000) {
726 unsigned int c = nr * CLUSTERS_PER_FAT16_SECTOR + k;
727 /* Ignore the reserved clusters 0 & 1, and also
728 cluster numbers out of bounds */
729 if ( c < 2 || c > fat_bpb->dataclusters+1 )
730 continue;
731 LDEBUGF("find_free_cluster(%x) == %x\n",startcluster,c);
732 fat_bpb->fsinfo.nextfree = c;
733 return c;
736 offset = 0;
739 else
740 #endif /* #ifdef HAVE_FAT16SUPPORT */
742 sector = startcluster / CLUSTERS_PER_FAT_SECTOR;
743 offset = startcluster % CLUSTERS_PER_FAT_SECTOR;
745 for (i = 0; i<fat_bpb->fatsize; i++) {
746 unsigned int j;
747 unsigned long nr = (i + sector) % fat_bpb->fatsize;
748 unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false);
749 if ( !fat )
750 break;
751 for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) {
752 int k = (j + offset) % CLUSTERS_PER_FAT_SECTOR;
753 if (!(letoh32(fat[k]) & 0x0fffffff)) {
754 unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k;
755 /* Ignore the reserved clusters 0 & 1, and also
756 cluster numbers out of bounds */
757 if ( c < 2 || c > fat_bpb->dataclusters+1 )
758 continue;
759 LDEBUGF("find_free_cluster(%lx) == %lx\n",startcluster,c);
760 fat_bpb->fsinfo.nextfree = c;
761 return c;
764 offset = 0;
768 LDEBUGF("find_free_cluster(%lx) == 0\n",startcluster);
769 return 0; /* 0 is an illegal cluster number */
772 static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry,
773 unsigned long val)
775 #ifndef HAVE_MULTIVOLUME
776 struct bpb* fat_bpb = &fat_bpbs[0];
777 #endif
778 #ifdef HAVE_FAT16SUPPORT
779 if (fat_bpb->is_fat16)
781 int sector = entry / CLUSTERS_PER_FAT16_SECTOR;
782 int offset = entry % CLUSTERS_PER_FAT16_SECTOR;
783 unsigned short* sec;
785 val &= 0xFFFF;
787 LDEBUGF("update_fat_entry(%x,%x)\n",entry,val);
789 if (entry==val)
790 panicf("Creating FAT loop: %lx,%lx\n",entry,val);
792 if ( entry < 2 )
793 panicf("Updating reserved FAT entry %ld.\n",entry);
795 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true);
796 if (!sec)
798 DEBUGF( "update_fat_entry() - Could not cache sector %d\n", sector);
799 return -1;
802 if ( val ) {
803 if (letoh16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0)
804 fat_bpb->fsinfo.freecount--;
806 else {
807 if (letoh16(sec[offset]))
808 fat_bpb->fsinfo.freecount++;
811 LDEBUGF("update_fat_entry: %d free clusters\n",
812 fat_bpb->fsinfo.freecount);
814 sec[offset] = htole16(val);
816 else
817 #endif /* #ifdef HAVE_FAT16SUPPORT */
819 long sector = entry / CLUSTERS_PER_FAT_SECTOR;
820 int offset = entry % CLUSTERS_PER_FAT_SECTOR;
821 unsigned long* sec;
823 LDEBUGF("update_fat_entry(%lx,%lx)\n",entry,val);
825 if (entry==val)
826 panicf("Creating FAT loop: %lx,%lx\n",entry,val);
828 if ( entry < 2 )
829 panicf("Updating reserved FAT entry %ld.\n",entry);
831 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true);
832 if (!sec)
834 DEBUGF("update_fat_entry() - Could not cache sector %ld\n", sector);
835 return -1;
838 if ( val ) {
839 if (!(letoh32(sec[offset]) & 0x0fffffff) &&
840 fat_bpb->fsinfo.freecount > 0)
841 fat_bpb->fsinfo.freecount--;
843 else {
844 if (letoh32(sec[offset]) & 0x0fffffff)
845 fat_bpb->fsinfo.freecount++;
848 LDEBUGF("update_fat_entry: %ld free clusters\n",
849 fat_bpb->fsinfo.freecount);
851 /* don't change top 4 bits */
852 sec[offset] &= htole32(0xf0000000);
853 sec[offset] |= htole32(val & 0x0fffffff);
856 return 0;
859 static long read_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry)
861 #ifdef HAVE_FAT16SUPPORT
862 #ifndef HAVE_MULTIVOLUME
863 struct bpb* fat_bpb = &fat_bpbs[0];
864 #endif
865 if (fat_bpb->is_fat16)
867 int sector = entry / CLUSTERS_PER_FAT16_SECTOR;
868 int offset = entry % CLUSTERS_PER_FAT16_SECTOR;
869 unsigned short* sec;
871 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false);
872 if (!sec)
874 DEBUGF( "read_fat_entry() - Could not cache sector %d\n", sector);
875 return -1;
878 return letoh16(sec[offset]);
880 else
881 #endif /* #ifdef HAVE_FAT16SUPPORT */
883 long sector = entry / CLUSTERS_PER_FAT_SECTOR;
884 int offset = entry % CLUSTERS_PER_FAT_SECTOR;
885 unsigned long* sec;
887 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false);
888 if (!sec)
890 DEBUGF( "read_fat_entry() - Could not cache sector %ld\n", sector);
891 return -1;
894 return letoh32(sec[offset]) & 0x0fffffff;
898 static long get_next_cluster(IF_MV2(struct bpb* fat_bpb,) long cluster)
900 long next_cluster;
901 long eof_mark = FAT_EOF_MARK;
903 #ifdef HAVE_FAT16SUPPORT
904 #ifndef HAVE_MULTIVOLUME
905 struct bpb* fat_bpb = &fat_bpbs[0];
906 #endif
907 if (fat_bpb->is_fat16)
909 eof_mark &= 0xFFFF; /* only 16 bit */
910 if (cluster < 0) /* FAT16 root dir */
911 return cluster + 1; /* don't use the FAT */
913 #endif
914 next_cluster = read_fat_entry(IF_MV2(fat_bpb,) cluster);
916 /* is this last cluster in chain? */
917 if ( next_cluster >= eof_mark )
918 return 0;
919 else
920 return next_cluster;
923 static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb))
925 #ifndef HAVE_MULTIVOLUME
926 struct bpb* fat_bpb = &fat_bpbs[0];
927 #endif
928 unsigned char fsinfo[SECTOR_SIZE];
929 unsigned long* intptr;
930 int rc;
932 #ifdef HAVE_FAT16SUPPORT
933 if (fat_bpb->is_fat16)
934 return 0; /* FAT16 has no FsInfo */
935 #endif /* #ifdef HAVE_FAT16SUPPORT */
937 /* update fsinfo */
938 rc = storage_read_sectors(fat_bpb->drive,
939 fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo);
940 if (rc < 0)
942 DEBUGF( "flush_fat() - Couldn't read FSInfo (error code %d)\n", rc);
943 return rc * 10 - 1;
945 intptr = (long*)&(fsinfo[FSINFO_FREECOUNT]);
946 *intptr = htole32(fat_bpb->fsinfo.freecount);
948 intptr = (long*)&(fsinfo[FSINFO_NEXTFREE]);
949 *intptr = htole32(fat_bpb->fsinfo.nextfree);
951 rc = storage_write_sectors(fat_bpb->drive,
952 fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo);
953 if (rc < 0)
955 DEBUGF( "flush_fat() - Couldn't write FSInfo (error code %d)\n", rc);
956 return rc * 10 - 2;
959 return 0;
962 static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb))
964 int i;
965 int rc;
966 unsigned char *sec;
967 LDEBUGF("flush_fat()\n");
969 mutex_lock(&cache_mutex);
970 for(i = 0;i < FAT_CACHE_SIZE;i++)
972 struct fat_cache_entry *fce = &fat_cache[i];
973 if(fce->inuse
974 #ifdef HAVE_MULTIVOLUME
975 && fce->fat_vol == fat_bpb
976 #endif
977 && fce->dirty)
979 sec = fat_cache_sectors[i];
980 flush_fat_sector(fce, sec);
983 mutex_unlock(&cache_mutex);
985 rc = update_fsinfo(IF_MV(fat_bpb));
986 if (rc < 0)
987 return rc * 10 - 3;
989 return 0;
992 static void fat_time(unsigned short* date,
993 unsigned short* time,
994 unsigned short* tenth )
996 #if CONFIG_RTC
997 struct tm* tm = get_time();
999 if (date)
1000 *date = ((tm->tm_year - 80) << 9) |
1001 ((tm->tm_mon + 1) << 5) |
1002 tm->tm_mday;
1004 if (time)
1005 *time = (tm->tm_hour << 11) |
1006 (tm->tm_min << 5) |
1007 (tm->tm_sec >> 1);
1009 if (tenth)
1010 *tenth = (tm->tm_sec & 1) * 100;
1011 #else
1012 /* non-RTC version returns an increment from the supplied time, or a
1013 * fixed standard time/date if no time given as input */
1015 /* Macros to convert a 2-digit string to a decimal constant.
1016 (YEAR), MONTH and DAY are set by the date command, which outputs
1017 DAY as 00..31 and MONTH as 01..12. The leading zero would lead to
1018 misinterpretation as an octal constant. */
1019 #define S100(x) 1 ## x
1020 #define C2DIG2DEC(x) (S100(x)-100)
1021 /* The actual build date, as FAT date constant */
1022 #define BUILD_DATE_FAT (((YEAR - 1980) << 9) \
1023 | (C2DIG2DEC(MONTH) << 5) \
1024 | C2DIG2DEC(DAY))
1026 bool date_forced = false;
1027 bool next_day = false;
1028 unsigned time2 = 0; /* double time, for CRTTIME with 1s precision */
1030 if (date && *date < BUILD_DATE_FAT)
1032 *date = BUILD_DATE_FAT;
1033 date_forced = true;
1036 if (time)
1038 time2 = *time << 1;
1039 if (time2 == 0 || date_forced)
1041 time2 = (11 < 6) | 11; /* set to 00:11:11 */
1043 else
1045 unsigned mins = (time2 >> 6) & 0x3f;
1046 unsigned hours = (time2 >> 12) & 0x1f;
1048 mins = 11 * ((mins/11) + 1); /* advance to next multiple of 11 */
1049 if (mins > 59)
1051 mins = 11; /* 00 would be a bad marker */
1052 if (++hours > 23)
1054 hours = 0;
1055 next_day = true;
1058 time2 = (hours << 12) | (mins << 6) | mins; /* secs = mins */
1060 *time = time2 >> 1;
1063 if (tenth)
1064 *tenth = (time2 & 1) * 100;
1066 if (date && next_day)
1068 static const unsigned char daysinmonth[] =
1069 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1070 unsigned day = *date & 0x1f;
1071 unsigned month = (*date >> 5) & 0x0f;
1072 unsigned year = (*date >> 9) & 0x7f;
1074 /* simplification: ignore leap years */
1075 if (++day > daysinmonth[month-1])
1077 day = 1;
1078 if (++month > 12)
1080 month = 1;
1081 year++;
1084 *date = (year << 9) | (month << 5) | day;
1087 #endif /* CONFIG_RTC */
1090 static int write_long_name(struct fat_file* file,
1091 unsigned int firstentry,
1092 unsigned int numentries,
1093 const unsigned char* name,
1094 const unsigned char* shortname,
1095 bool is_directory)
1097 unsigned char buf[SECTOR_SIZE];
1098 unsigned char* entry;
1099 unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR;
1100 unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR;
1101 unsigned char chksum = 0;
1102 unsigned int i, j=0;
1103 unsigned int nameidx=0, namelen = utf8length(name);
1104 int rc;
1105 unsigned short name_utf16[namelen + 1];
1107 LDEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)\n",
1108 file->firstcluster, firstentry, numentries, name);
1110 rc = fat_seek(file, sector);
1111 if (rc<0)
1112 return rc * 10 - 1;
1114 rc = fat_readwrite(file, 1, buf, false);
1115 if (rc<1)
1116 return rc * 10 - 2;
1118 /* calculate shortname checksum */
1119 for (i=11; i>0; i--)
1120 chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++];
1122 /* calc position of last name segment */
1123 if ( namelen > NAME_BYTES_PER_ENTRY )
1124 for (nameidx=0;
1125 nameidx < (namelen - NAME_BYTES_PER_ENTRY);
1126 nameidx += NAME_BYTES_PER_ENTRY);
1128 /* we need to convert the name first */
1129 /* since it is written in reverse order */
1130 for (i = 0; i <= namelen; i++)
1131 name = utf8decode(name, &name_utf16[i]);
1133 for (i=0; i < numentries; i++) {
1134 /* new sector? */
1135 if ( idx >= DIR_ENTRIES_PER_SECTOR ) {
1136 /* update current sector */
1137 rc = fat_seek(file, sector);
1138 if (rc<0)
1139 return rc * 10 - 3;
1141 rc = fat_readwrite(file, 1, buf, true);
1142 if (rc<1)
1143 return rc * 10 - 4;
1145 /* read next sector */
1146 rc = fat_readwrite(file, 1, buf, false);
1147 if (rc<0) {
1148 LDEBUGF("Failed writing new sector: %d\n",rc);
1149 return rc * 10 - 5;
1151 if (rc==0)
1152 /* end of dir */
1153 memset(buf, 0, sizeof buf);
1155 sector++;
1156 idx = 0;
1159 entry = buf + idx * DIR_ENTRY_SIZE;
1161 /* verify this entry is free */
1162 if (entry[0] && entry[0] != 0xe5 )
1163 panicf("Dir entry %d in sector %x is not free! "
1164 "%02x %02x %02x %02x",
1165 idx, sector,
1166 entry[0], entry[1], entry[2], entry[3]);
1168 memset(entry, 0, DIR_ENTRY_SIZE);
1169 if ( i+1 < numentries ) {
1170 /* longname entry */
1171 unsigned int k, l = nameidx;
1173 entry[FATLONG_ORDER] = numentries-i-1;
1174 if (i==0) {
1175 /* mark this as last long entry */
1176 entry[FATLONG_ORDER] |= 0x40;
1178 /* pad name with 0xffff */
1179 for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
1180 for (k=14; k<26; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
1181 for (k=28; k<32; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
1183 /* set name */
1184 for (k=0; k<5 && l <= namelen; k++) {
1185 entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff);
1186 entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8);
1188 for (k=0; k<6 && l <= namelen; k++) {
1189 entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff);
1190 entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8);
1192 for (k=0; k<2 && l <= namelen; k++) {
1193 entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff);
1194 entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8);
1197 entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME;
1198 entry[FATDIR_FSTCLUSLO] = 0;
1199 entry[FATLONG_TYPE] = 0;
1200 entry[FATLONG_CHKSUM] = chksum;
1201 LDEBUGF("Longname entry %d: %s\n", idx, name+nameidx);
1203 else {
1204 /* shortname entry */
1205 unsigned short date=0, time=0, tenth=0;
1206 LDEBUGF("Shortname entry: %s\n", shortname);
1207 memcpy(entry + FATDIR_NAME, shortname, 11);
1208 entry[FATDIR_ATTR] = is_directory?FAT_ATTR_DIRECTORY:0;
1209 entry[FATDIR_NTRES] = 0;
1211 fat_time(&date, &time, &tenth);
1212 entry[FATDIR_CRTTIMETENTH] = tenth;
1213 *(unsigned short*)(entry + FATDIR_CRTTIME) = htole16(time);
1214 *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time);
1215 *(unsigned short*)(entry + FATDIR_CRTDATE) = htole16(date);
1216 *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date);
1217 *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date);
1219 idx++;
1220 nameidx -= NAME_BYTES_PER_ENTRY;
1223 /* update last sector */
1224 rc = fat_seek(file, sector);
1225 if (rc<0)
1226 return rc * 10 - 6;
1228 rc = fat_readwrite(file, 1, buf, true);
1229 if (rc<1)
1230 return rc * 10 - 7;
1232 return 0;
1235 static int fat_checkname(const unsigned char* newname)
1237 static const char invalid_chars[] = "\"*/:<>?\\|";
1238 int len = strlen(newname);
1239 /* More sanity checks are probably needed */
1240 if (len > 255 || newname[len - 1] == '.')
1242 return -1;
1244 while (*newname)
1246 if (*newname < ' ' || strchr(invalid_chars, *newname) != NULL)
1247 return -1;
1248 newname++;
1250 /* check trailing space(s) */
1251 if(*(--newname) == ' ')
1252 return -1;
1254 return 0;
1257 static int add_dir_entry(struct fat_dir* dir,
1258 struct fat_file* file,
1259 const char* name,
1260 bool is_directory,
1261 bool dotdir)
1263 #ifdef HAVE_MULTIVOLUME
1264 struct bpb* fat_bpb = &fat_bpbs[dir->file.volume];
1265 #else
1266 struct bpb* fat_bpb = &fat_bpbs[0];
1267 #endif
1268 unsigned char buf[SECTOR_SIZE];
1269 unsigned char shortname[12];
1270 int rc;
1271 unsigned int sector;
1272 bool done = false;
1273 int entries_needed, entries_found = 0;
1274 int firstentry;
1276 LDEBUGF( "add_dir_entry(%s,%lx)\n",
1277 name, file->firstcluster);
1279 /* Don't check dotdirs name for validity */
1280 if (dotdir == false) {
1281 rc = fat_checkname(name);
1282 if (rc < 0) {
1283 /* filename is invalid */
1284 return rc * 10 - 1;
1288 #ifdef HAVE_MULTIVOLUME
1289 file->volume = dir->file.volume; /* inherit the volume, to make sure */
1290 #endif
1292 /* The "." and ".." directory entries must not be long names */
1293 if(dotdir) {
1294 int i;
1295 strlcpy(shortname, name, 12);
1296 for(i = strlen(shortname); i < 12; i++)
1297 shortname[i] = ' ';
1299 entries_needed = 1;
1300 } else {
1301 create_dos_name(name, shortname);
1303 /* one dir entry needed for every 13 bytes of filename,
1304 plus one entry for the short name */
1305 entries_needed = (utf8length(name) + (NAME_BYTES_PER_ENTRY-1))
1306 / NAME_BYTES_PER_ENTRY + 1;
1309 restart:
1310 firstentry = -1;
1312 rc = fat_seek(&dir->file, 0);
1313 if (rc < 0)
1314 return rc * 10 - 2;
1316 /* step 1: search for free entries and check for duplicate shortname */
1317 for (sector = 0; !done; sector++)
1319 unsigned int i;
1321 rc = fat_readwrite(&dir->file, 1, buf, false);
1322 if (rc < 0) {
1323 DEBUGF( "add_dir_entry() - Couldn't read dir"
1324 " (error code %d)\n", rc);
1325 return rc * 10 - 3;
1328 if (rc == 0) { /* current end of dir reached */
1329 LDEBUGF("End of dir on cluster boundary\n");
1330 break;
1333 /* look for free slots */
1334 for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++)
1336 switch (buf[i * DIR_ENTRY_SIZE]) {
1337 case 0:
1338 entries_found += DIR_ENTRIES_PER_SECTOR - i;
1339 LDEBUGF("Found end of dir %d\n",
1340 sector * DIR_ENTRIES_PER_SECTOR + i);
1341 i = DIR_ENTRIES_PER_SECTOR - 1;
1342 done = true;
1343 break;
1345 case 0xe5:
1346 entries_found++;
1347 LDEBUGF("Found free entry %d (%d/%d)\n",
1348 sector * DIR_ENTRIES_PER_SECTOR + i,
1349 entries_found, entries_needed);
1350 break;
1352 default:
1353 entries_found = 0;
1355 /* check that our intended shortname doesn't already exist */
1356 if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 11)) {
1357 /* shortname exists already, make a new one */
1358 randomize_dos_name(shortname);
1359 LDEBUGF("Duplicate shortname, changing to %s\n",
1360 shortname);
1362 /* name has changed, we need to restart search */
1363 goto restart;
1365 break;
1367 if (firstentry < 0 && (entries_found >= entries_needed))
1368 firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1
1369 - entries_found;
1373 /* step 2: extend the dir if necessary */
1374 if (firstentry < 0)
1376 LDEBUGF("Adding new sector(s) to dir\n");
1377 rc = fat_seek(&dir->file, sector);
1378 if (rc < 0)
1379 return rc * 10 - 4;
1380 memset(buf, 0, sizeof buf);
1382 /* we must clear whole clusters */
1383 for (; (entries_found < entries_needed) ||
1384 (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); sector++)
1386 if (sector >= (65536/DIR_ENTRIES_PER_SECTOR))
1387 return -5; /* dir too large -- FAT specification */
1389 rc = fat_readwrite(&dir->file, 1, buf, true);
1390 if (rc < 1) /* No more room or something went wrong */
1391 return rc * 10 - 6;
1393 entries_found += DIR_ENTRIES_PER_SECTOR;
1396 firstentry = sector * DIR_ENTRIES_PER_SECTOR - entries_found;
1399 /* step 3: add entry */
1400 sector = firstentry / DIR_ENTRIES_PER_SECTOR;
1401 LDEBUGF("Adding longname to entry %d in sector %d\n",
1402 firstentry, sector);
1404 rc = write_long_name(&dir->file, firstentry,
1405 entries_needed, name, shortname, is_directory);
1406 if (rc < 0)
1407 return rc * 10 - 7;
1409 /* remember where the shortname dir entry is located */
1410 file->direntry = firstentry + entries_needed - 1;
1411 file->direntries = entries_needed;
1412 file->dircluster = dir->file.firstcluster;
1413 LDEBUGF("Added new dir entry %d, using %d slots.\n",
1414 file->direntry, file->direntries);
1416 return 0;
1419 static unsigned char char2dos(unsigned char c, int* randomize)
1421 static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|";
1423 if (c <= 0x20)
1424 c = 0; /* Illegal char, remove */
1425 else if (strchr(invalid_chars, c) != NULL)
1427 /* Illegal char, replace */
1428 c = '_';
1429 *randomize = 1; /* as per FAT spec */
1431 else
1432 c = toupper(c);
1434 return c;
1437 static void create_dos_name(const unsigned char *name, unsigned char *newname)
1439 int i;
1440 unsigned char *ext;
1441 int randomize = 0;
1443 /* Find extension part */
1444 ext = strrchr(name, '.');
1445 if (ext == name) /* handle .dotnames */
1446 ext = NULL;
1448 /* needs to randomize? */
1449 if((ext && (strlen(ext) > 4)) ||
1450 ((ext ? (unsigned int)(ext-name) : strlen(name)) > 8) )
1451 randomize = 1;
1453 /* Name part */
1454 for (i = 0; *name && (!ext || name < ext) && (i < 8); name++)
1456 unsigned char c = char2dos(*name, &randomize);
1457 if (c)
1458 newname[i++] = c;
1461 /* Pad both name and extension */
1462 while (i < 11)
1463 newname[i++] = ' ';
1465 if (newname[0] == 0xe5) /* Special kanji character */
1466 newname[0] = 0x05;
1468 if (ext)
1469 { /* Extension part */
1470 ext++;
1471 for (i = 8; *ext && (i < 11); ext++)
1473 unsigned char c = char2dos(*ext, &randomize);
1474 if (c)
1475 newname[i++] = c;
1479 if(randomize)
1480 randomize_dos_name(newname);
1483 static void randomize_dos_name(unsigned char *name)
1485 unsigned char* tilde = NULL; /* ~ location */
1486 unsigned char* lastpt = NULL; /* last point of filename */
1487 unsigned char* nameptr = name; /* working copy of name pointer */
1488 unsigned char num[9]; /* holds number as string */
1489 int i = 0;
1490 int cnt = 1;
1491 int numlen;
1492 int offset;
1494 while(i++ < 8)
1496 /* hunt for ~ and where to put it */
1497 if((!tilde) && (*nameptr == '~'))
1498 tilde = nameptr;
1499 if((!lastpt) && ((*nameptr == ' ' || *nameptr == '~')))
1500 lastpt = nameptr;
1501 nameptr++;
1503 if(tilde)
1505 /* extract current count and increment */
1506 memcpy(num,tilde+1,7-(unsigned int)(tilde-name));
1507 num[7-(unsigned int)(tilde-name)] = 0;
1508 cnt = atoi(num) + 1;
1510 cnt %= 10000000; /* protection */
1511 snprintf(num, 9, "~%d", cnt); /* allow room for trailing zero */
1512 numlen = strlen(num); /* required space */
1513 offset = (unsigned int)(lastpt ? lastpt - name : 8); /* prev startpoint */
1514 if(offset > (8-numlen)) offset = 8-numlen; /* correct for new numlen */
1516 memcpy(&name[offset], num, numlen);
1518 /* in special case of counter overflow: pad with spaces */
1519 for(offset = offset+numlen; offset < 8; offset++)
1520 name[offset] = ' ';
1523 static int update_short_entry( struct fat_file* file, long size, int attr )
1525 unsigned char buf[SECTOR_SIZE];
1526 int sector = file->direntry / DIR_ENTRIES_PER_SECTOR;
1527 unsigned char* entry =
1528 buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR);
1529 unsigned long* sizeptr;
1530 unsigned short* clusptr;
1531 struct fat_file dir;
1532 int rc;
1534 LDEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)\n",
1535 file->firstcluster, file->direntry, size);
1537 /* create a temporary file handle for the dir holding this file */
1538 rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL);
1539 if (rc < 0)
1540 return rc * 10 - 1;
1542 rc = fat_seek( &dir, sector );
1543 if (rc<0)
1544 return rc * 10 - 2;
1546 rc = fat_readwrite(&dir, 1, buf, false);
1547 if (rc < 1)
1548 return rc * 10 - 3;
1550 if (!entry[0] || entry[0] == 0xe5)
1551 panicf("Updating size on empty dir entry %d\n", file->direntry);
1553 entry[FATDIR_ATTR] = attr & 0xFF;
1555 clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
1556 *clusptr = htole16(file->firstcluster >> 16);
1558 clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
1559 *clusptr = htole16(file->firstcluster & 0xffff);
1561 sizeptr = (long*)(entry + FATDIR_FILESIZE);
1562 *sizeptr = htole32(size);
1565 #if CONFIG_RTC
1566 unsigned short time = 0;
1567 unsigned short date = 0;
1568 #else
1569 /* get old time to increment from */
1570 unsigned short time = htole16(*(unsigned short*)(entry+FATDIR_WRTTIME));
1571 unsigned short date = htole16(*(unsigned short*)(entry+FATDIR_WRTDATE));
1572 #endif
1573 fat_time(&date, &time, NULL);
1574 *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time);
1575 *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date);
1576 *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date);
1579 rc = fat_seek( &dir, sector );
1580 if (rc < 0)
1581 return rc * 10 - 4;
1583 rc = fat_readwrite(&dir, 1, buf, true);
1584 if (rc < 1)
1585 return rc * 10 - 5;
1587 return 0;
1590 static int parse_direntry(struct fat_direntry *de, const unsigned char *buf)
1592 int i=0,j=0;
1593 unsigned char c;
1594 bool lowercase;
1596 memset(de, 0, sizeof(struct fat_direntry));
1597 de->attr = buf[FATDIR_ATTR];
1598 de->crttimetenth = buf[FATDIR_CRTTIMETENTH];
1599 de->crtdate = BYTES2INT16(buf,FATDIR_CRTDATE);
1600 de->crttime = BYTES2INT16(buf,FATDIR_CRTTIME);
1601 de->wrtdate = BYTES2INT16(buf,FATDIR_WRTDATE);
1602 de->wrttime = BYTES2INT16(buf,FATDIR_WRTTIME);
1603 de->filesize = BYTES2INT32(buf,FATDIR_FILESIZE);
1604 de->firstcluster = ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSLO)) |
1605 ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16);
1606 /* The double cast is to prevent a sign-extension to be done on CalmRISC16.
1607 (the result of the shift is always considered signed) */
1609 /* fix the name */
1610 lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME);
1611 c = buf[FATDIR_NAME];
1612 if (c == 0x05) /* special kanji char */
1613 c = 0xe5;
1614 i = 0;
1615 while (c != ' ') {
1616 de->name[j++] = lowercase ? tolower(c) : c;
1617 if (++i >= 8)
1618 break;
1619 c = buf[FATDIR_NAME+i];
1621 if (buf[FATDIR_NAME+8] != ' ') {
1622 lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT);
1623 de->name[j++] = '.';
1624 for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++)
1625 de->name[j++] = lowercase ? tolower(c) : c;
1627 return 1;
1630 int fat_open(IF_MV2(int volume,)
1631 long startcluster,
1632 struct fat_file *file,
1633 const struct fat_dir* dir)
1635 file->firstcluster = startcluster;
1636 file->lastcluster = startcluster;
1637 file->lastsector = 0;
1638 file->clusternum = 0;
1639 file->sectornum = 0;
1640 file->eof = false;
1641 #ifdef HAVE_MULTIVOLUME
1642 file->volume = volume;
1643 /* fixme: remove error check when done */
1644 if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted)
1646 LDEBUGF("fat_open() illegal volume %d\n", volume);
1647 return -1;
1649 #endif
1651 /* remember where the file's dir entry is located */
1652 if ( dir ) {
1653 file->direntry = dir->entry - 1;
1654 file->direntries = dir->entrycount;
1655 file->dircluster = dir->file.firstcluster;
1657 LDEBUGF("fat_open(%lx), entry %d\n",startcluster,file->direntry);
1658 return 0;
1661 int fat_create_file(const char* name,
1662 struct fat_file* file,
1663 struct fat_dir* dir)
1665 int rc;
1667 LDEBUGF("fat_create_file(\"%s\",%lx,%lx)\n",name,(long)file,(long)dir);
1668 rc = add_dir_entry(dir, file, name, false, false);
1669 if (!rc) {
1670 file->firstcluster = 0;
1671 file->lastcluster = 0;
1672 file->lastsector = 0;
1673 file->clusternum = 0;
1674 file->sectornum = 0;
1675 file->eof = false;
1678 return rc;
1681 int fat_create_dir(const char* name,
1682 struct fat_dir* newdir,
1683 struct fat_dir* dir)
1685 #ifdef HAVE_MULTIVOLUME
1686 struct bpb* fat_bpb = &fat_bpbs[dir->file.volume];
1687 #else
1688 struct bpb* fat_bpb = &fat_bpbs[0];
1689 #endif
1690 unsigned char buf[SECTOR_SIZE];
1691 int i;
1692 long sector;
1693 int rc;
1694 struct fat_file dummyfile;
1696 LDEBUGF("fat_create_dir(\"%s\",%lx,%lx)\n",name,(long)newdir,(long)dir);
1698 memset(newdir, 0, sizeof(struct fat_dir));
1699 memset(&dummyfile, 0, sizeof(struct fat_file));
1701 /* First, add the entry in the parent directory */
1702 rc = add_dir_entry(dir, &newdir->file, name, true, false);
1703 if (rc < 0)
1704 return rc * 10 - 1;
1706 /* Allocate a new cluster for the directory */
1707 newdir->file.firstcluster = find_free_cluster(IF_MV2(fat_bpb,)
1708 fat_bpb->fsinfo.nextfree);
1709 if(newdir->file.firstcluster == 0)
1710 return -1;
1712 update_fat_entry(IF_MV2(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK);
1714 /* Clear the entire cluster */
1715 memset(buf, 0, sizeof buf);
1716 sector = cluster2sec(IF_MV2(fat_bpb,) newdir->file.firstcluster);
1717 for(i = 0;i < (int)fat_bpb->bpb_secperclus;i++) {
1718 rc = transfer(IF_MV2(fat_bpb,) sector + i, 1, buf, true );
1719 if (rc < 0)
1720 return rc * 10 - 2;
1723 /* Then add the "." entry */
1724 rc = add_dir_entry(newdir, &dummyfile, ".", true, true);
1725 if (rc < 0)
1726 return rc * 10 - 3;
1727 dummyfile.firstcluster = newdir->file.firstcluster;
1728 update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY);
1730 /* and the ".." entry */
1731 rc = add_dir_entry(newdir, &dummyfile, "..", true, true);
1732 if (rc < 0)
1733 return rc * 10 - 4;
1735 /* The root cluster is cluster 0 in the ".." entry */
1736 if(dir->file.firstcluster == fat_bpb->bpb_rootclus)
1737 dummyfile.firstcluster = 0;
1738 else
1739 dummyfile.firstcluster = dir->file.firstcluster;
1740 update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY);
1742 /* Set the firstcluster field in the direntry */
1743 update_short_entry(&newdir->file, 0, FAT_ATTR_DIRECTORY);
1745 rc = flush_fat(IF_MV(fat_bpb));
1746 if (rc < 0)
1747 return rc * 10 - 5;
1749 return rc;
1752 int fat_truncate(const struct fat_file *file)
1754 /* truncate trailing clusters */
1755 long next;
1756 long last = file->lastcluster;
1757 #ifdef HAVE_MULTIVOLUME
1758 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1759 #endif
1761 LDEBUGF("fat_truncate(%lx, %lx)\n", file->firstcluster, last);
1763 for ( last = get_next_cluster(IF_MV2(fat_bpb,) last); last; last = next ) {
1764 next = get_next_cluster(IF_MV2(fat_bpb,) last);
1765 update_fat_entry(IF_MV2(fat_bpb,) last,0);
1767 if (file->lastcluster)
1768 update_fat_entry(IF_MV2(fat_bpb,) file->lastcluster,FAT_EOF_MARK);
1770 return 0;
1773 int fat_closewrite(struct fat_file *file, long size, int attr)
1775 int rc;
1776 #ifdef HAVE_MULTIVOLUME
1777 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1778 #endif
1779 LDEBUGF("fat_closewrite(size=%ld)\n",size);
1781 if (!size) {
1782 /* empty file */
1783 if ( file->firstcluster ) {
1784 update_fat_entry(IF_MV2(fat_bpb,) file->firstcluster, 0);
1785 file->firstcluster = 0;
1789 if (file->dircluster) {
1790 rc = update_short_entry(file, size, attr);
1791 if (rc < 0)
1792 return rc * 10 - 1;
1795 flush_fat(IF_MV(fat_bpb));
1797 #ifdef TEST_FAT
1798 if ( file->firstcluster ) {
1799 /* debug */
1800 #ifdef HAVE_MULTIVOLUME
1801 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1802 #else
1803 struct bpb* fat_bpb = &fat_bpbs[0];
1804 #endif
1805 long count = 0;
1806 long len;
1807 long next;
1808 for ( next = file->firstcluster; next;
1809 next = get_next_cluster(IF_MV2(fat_bpb,) next) ) {
1810 LDEBUGF("cluster %ld: %lx\n", count, next);
1811 count++;
1813 len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE;
1814 LDEBUGF("File is %ld clusters (chainlen=%ld, size=%ld)\n",
1815 count, len, size );
1816 if ( len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE)
1817 panicf("Cluster chain is too long\n");
1818 if ( len < size )
1819 panicf("Cluster chain is too short\n");
1821 #endif
1823 return 0;
1826 static int free_direntries(struct fat_file* file)
1828 unsigned char buf[SECTOR_SIZE];
1829 struct fat_file dir;
1830 int numentries = file->direntries;
1831 unsigned int entry = file->direntry - numentries + 1;
1832 unsigned int sector = entry / DIR_ENTRIES_PER_SECTOR;
1833 int i;
1834 int rc;
1836 /* create a temporary file handle for the dir holding this file */
1837 rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL);
1838 if (rc < 0)
1839 return rc * 10 - 1;
1841 rc = fat_seek( &dir, sector );
1842 if (rc < 0)
1843 return rc * 10 - 2;
1845 rc = fat_readwrite(&dir, 1, buf, false);
1846 if (rc < 1)
1847 return rc * 10 - 3;
1849 for (i=0; i < numentries; i++) {
1850 LDEBUGF("Clearing dir entry %d (%d/%d)\n",
1851 entry, i+1, numentries);
1852 buf[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5;
1853 entry++;
1855 if ( (entry % DIR_ENTRIES_PER_SECTOR) == 0 ) {
1856 /* flush this sector */
1857 rc = fat_seek(&dir, sector);
1858 if (rc < 0)
1859 return rc * 10 - 4;
1861 rc = fat_readwrite(&dir, 1, buf, true);
1862 if (rc < 1)
1863 return rc * 10 - 5;
1865 if ( i+1 < numentries ) {
1866 /* read next sector */
1867 rc = fat_readwrite(&dir, 1, buf, false);
1868 if (rc < 1)
1869 return rc * 10 - 6;
1871 sector++;
1875 if ( entry % DIR_ENTRIES_PER_SECTOR ) {
1876 /* flush this sector */
1877 rc = fat_seek(&dir, sector);
1878 if (rc < 0)
1879 return rc * 10 - 7;
1881 rc = fat_readwrite(&dir, 1, buf, true);
1882 if (rc < 1)
1883 return rc * 10 - 8;
1886 return 0;
1889 int fat_remove(struct fat_file* file)
1891 long next, last = file->firstcluster;
1892 int rc;
1893 #ifdef HAVE_MULTIVOLUME
1894 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1895 #endif
1897 LDEBUGF("fat_remove(%lx)\n",last);
1899 while ( last ) {
1900 next = get_next_cluster(IF_MV2(fat_bpb,) last);
1901 update_fat_entry(IF_MV2(fat_bpb,) last,0);
1902 last = next;
1905 if ( file->dircluster ) {
1906 rc = free_direntries(file);
1907 if (rc < 0)
1908 return rc * 10 - 1;
1911 file->firstcluster = 0;
1912 file->dircluster = 0;
1914 rc = flush_fat(IF_MV(fat_bpb));
1915 if (rc < 0)
1916 return rc * 10 - 2;
1918 return 0;
1921 int fat_rename(struct fat_file* file,
1922 struct fat_dir* dir,
1923 const unsigned char* newname,
1924 long size,
1925 int attr)
1927 int rc;
1928 struct fat_dir olddir;
1929 struct fat_file newfile = *file;
1930 unsigned char buf[SECTOR_SIZE];
1931 unsigned char* entry = NULL;
1932 unsigned short* clusptr = NULL;
1933 unsigned int parentcluster;
1934 #ifdef HAVE_MULTIVOLUME
1935 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1937 if (file->volume != dir->file.volume) {
1938 DEBUGF("No rename across volumes!\n");
1939 return -1;
1941 #else
1942 struct bpb* fat_bpb = &fat_bpbs[0];
1943 #endif
1945 if ( !file->dircluster ) {
1946 DEBUGF("File has no dir cluster!\n");
1947 return -2;
1950 /* create a temporary file handle */
1951 rc = fat_opendir(IF_MV2(file->volume,) &olddir, file->dircluster, NULL);
1952 if (rc < 0)
1953 return rc * 10 - 1;
1955 /* create new name */
1956 rc = add_dir_entry(dir, &newfile, newname, false, false);
1957 if (rc < 0)
1958 return rc * 10 - 2;
1960 /* write size and cluster link */
1961 rc = update_short_entry(&newfile, size, attr);
1962 if (rc < 0)
1963 return rc * 10 - 3;
1965 /* remove old name */
1966 rc = free_direntries(file);
1967 if (rc < 0)
1968 return rc * 10 - 4;
1970 rc = flush_fat(IF_MV(fat_bpb));
1971 if (rc < 0)
1972 return rc * 10 - 5;
1974 /* if renaming a directory, update the .. entry to make sure
1975 it points to its parent directory (we don't check if it was a move) */
1976 if(FAT_ATTR_DIRECTORY == attr) {
1977 /* open the dir that was renamed, we re-use the olddir struct */
1978 rc = fat_opendir(IF_MV2(file->volume,) &olddir, newfile.firstcluster,
1979 NULL);
1980 if (rc < 0)
1981 return rc * 10 - 6;
1983 /* get the first sector of the dir */
1984 rc = fat_seek(&olddir.file, 0);
1985 if (rc < 0)
1986 return rc * 10 - 7;
1988 rc = fat_readwrite(&olddir.file, 1, buf, false);
1989 if (rc < 0)
1990 return rc * 10 - 8;
1992 /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */
1993 if(dir->file.firstcluster == fat_bpb->bpb_rootclus)
1994 parentcluster = 0;
1995 else
1996 parentcluster = dir->file.firstcluster;
1998 entry = buf + DIR_ENTRY_SIZE;
1999 if(strncmp(".. ", entry, 11))
2001 /* .. entry must be second entry according to FAT spec (p.29) */
2002 DEBUGF("Second dir entry is not double-dot!\n");
2003 return rc * 10 - 9;
2005 clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
2006 *clusptr = htole16(parentcluster >> 16);
2008 clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
2009 *clusptr = htole16(parentcluster & 0xffff);
2011 /* write back this sector */
2012 rc = fat_seek(&olddir.file, 0);
2013 if (rc < 0)
2014 return rc * 10 - 7;
2016 rc = fat_readwrite(&olddir.file, 1, buf, true);
2017 if (rc < 1)
2018 return rc * 10 - 8;
2021 return 0;
2024 static long next_write_cluster(struct fat_file* file,
2025 long oldcluster,
2026 long* newsector)
2028 #ifdef HAVE_MULTIVOLUME
2029 struct bpb* fat_bpb = &fat_bpbs[file->volume];
2030 #else
2031 struct bpb* fat_bpb = &fat_bpbs[0];
2032 #endif
2033 long cluster = 0;
2034 long sector;
2036 LDEBUGF("next_write_cluster(%lx,%lx)\n",file->firstcluster, oldcluster);
2038 if (oldcluster)
2039 cluster = get_next_cluster(IF_MV2(fat_bpb,) oldcluster);
2041 if (!cluster) {
2042 if (oldcluster > 0)
2043 cluster = find_free_cluster(IF_MV2(fat_bpb,) oldcluster+1);
2044 else if (oldcluster == 0)
2045 cluster = find_free_cluster(IF_MV2(fat_bpb,)
2046 fat_bpb->fsinfo.nextfree);
2047 #ifdef HAVE_FAT16SUPPORT
2048 else /* negative, pseudo-cluster of the root dir */
2049 return 0; /* impossible to append something to the root */
2050 #endif
2052 if (cluster) {
2053 if (oldcluster)
2054 update_fat_entry(IF_MV2(fat_bpb,) oldcluster, cluster);
2055 else
2056 file->firstcluster = cluster;
2057 update_fat_entry(IF_MV2(fat_bpb,) cluster, FAT_EOF_MARK);
2059 else {
2060 #ifdef TEST_FAT
2061 if (fat_bpb->fsinfo.freecount>0)
2062 panicf("There is free space, but find_free_cluster() "
2063 "didn't find it!\n");
2064 #endif
2065 DEBUGF("next_write_cluster(): Disk full!\n");
2066 return 0;
2069 sector = cluster2sec(IF_MV2(fat_bpb,) cluster);
2070 if (sector<0)
2071 return 0;
2073 *newsector = sector;
2074 return cluster;
2077 static int transfer(IF_MV2(struct bpb* fat_bpb,)
2078 unsigned long start, long count, char* buf, bool write )
2080 #ifndef HAVE_MULTIVOLUME
2081 struct bpb* fat_bpb = &fat_bpbs[0];
2082 #endif
2083 int rc;
2085 LDEBUGF("transfer(s=%lx, c=%lx, %s)\n",
2086 start+ fat_bpb->startsector, count, write?"write":"read");
2087 if (write) {
2088 unsigned long firstallowed;
2089 #ifdef HAVE_FAT16SUPPORT
2090 if (fat_bpb->is_fat16)
2091 firstallowed = fat_bpb->rootdirsector;
2092 else
2093 #endif
2094 firstallowed = fat_bpb->firstdatasector;
2096 if (start < firstallowed)
2097 panicf("Write %ld before data\n", firstallowed - start);
2098 if (start + count > fat_bpb->totalsectors)
2099 panicf("Write %ld after data\n",
2100 start + count - fat_bpb->totalsectors);
2101 rc = storage_write_sectors(fat_bpb->drive,
2102 start + fat_bpb->startsector, count, buf);
2104 else
2105 rc = storage_read_sectors(fat_bpb->drive,
2106 start + fat_bpb->startsector, count, buf);
2107 if (rc < 0) {
2108 DEBUGF( "transfer() - Couldn't %s sector %lx"
2109 " (error code %d)\n",
2110 write ? "write":"read", start, rc);
2111 return rc;
2113 return 0;
2117 long fat_readwrite( struct fat_file *file, long sectorcount,
2118 void* buf, bool write )
2120 #ifdef HAVE_MULTIVOLUME
2121 struct bpb* fat_bpb = &fat_bpbs[file->volume];
2122 #else
2123 struct bpb* fat_bpb = &fat_bpbs[0];
2124 #endif
2125 long cluster = file->lastcluster;
2126 long sector = file->lastsector;
2127 long clusternum = file->clusternum;
2128 long numsec = file->sectornum;
2129 bool eof = file->eof;
2130 long first=0, last=0;
2131 long i;
2132 int rc;
2134 LDEBUGF( "fat_readwrite(file:%lx,count:0x%lx,buf:%lx,%s)\n",
2135 file->firstcluster,sectorcount,(long)buf,write?"write":"read");
2136 LDEBUGF( "fat_readwrite: sec=%lx numsec=%ld eof=%d\n",
2137 sector,numsec, eof?1:0);
2139 if ( eof && !write)
2140 return 0;
2142 /* find sequential sectors and write them all at once */
2143 for (i=0; (i < sectorcount) && (sector > -1); i++ ) {
2144 numsec++;
2145 if ( numsec > (long)fat_bpb->bpb_secperclus || !cluster ) {
2146 long oldcluster = cluster;
2147 long oldsector = sector;
2148 long oldnumsec = numsec;
2149 if (write)
2150 cluster = next_write_cluster(file, cluster, &sector);
2151 else {
2152 cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster);
2153 sector = cluster2sec(IF_MV2(fat_bpb,) cluster);
2156 clusternum++;
2157 numsec=1;
2159 if (!cluster) {
2160 eof = true;
2161 if ( write ) {
2162 /* remember last cluster, in case
2163 we want to append to the file */
2164 sector = oldsector;
2165 cluster = oldcluster;
2166 numsec = oldnumsec;
2167 clusternum--;
2168 i = -1; /* Error code */
2169 break;
2172 else
2173 eof = false;
2175 else {
2176 if (sector)
2177 sector++;
2178 else {
2179 /* look up first sector of file */
2180 sector = cluster2sec(IF_MV2(fat_bpb,) file->firstcluster);
2181 numsec=1;
2182 #ifdef HAVE_FAT16SUPPORT
2183 if (file->firstcluster < 0)
2184 { /* FAT16 root dir */
2185 sector += fat_bpb->rootdiroffset;
2186 numsec += fat_bpb->rootdiroffset;
2188 #endif
2192 if (!first)
2193 first = sector;
2195 if ( ((sector != first) && (sector != last+1)) || /* not sequential */
2196 (last-first+1 == 256) ) { /* max 256 sectors per ata request */
2197 long count = last - first + 1;
2198 rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write );
2199 if (rc < 0)
2200 return rc * 10 - 1;
2202 buf = (char *)buf + count * SECTOR_SIZE;
2203 first = sector;
2206 if ((i == sectorcount-1) && /* last sector requested */
2207 (!eof))
2209 long count = sector - first + 1;
2210 rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write );
2211 if (rc < 0)
2212 return rc * 10 - 2;
2215 last = sector;
2218 file->lastcluster = cluster;
2219 file->lastsector = sector;
2220 file->clusternum = clusternum;
2221 file->sectornum = numsec;
2222 file->eof = eof;
2224 /* if eof, don't report last block as read/written */
2225 if (eof)
2226 i--;
2228 DEBUGF("Sectors written: %ld\n", i);
2229 return i;
2232 int fat_seek(struct fat_file *file, unsigned long seeksector )
2234 #ifdef HAVE_MULTIVOLUME
2235 struct bpb* fat_bpb = &fat_bpbs[file->volume];
2236 #else
2237 struct bpb* fat_bpb = &fat_bpbs[0];
2238 #endif
2239 long clusternum=0, numclusters=0, sectornum=0, sector=0;
2240 long cluster = file->firstcluster;
2241 long i;
2243 #ifdef HAVE_FAT16SUPPORT
2244 if (cluster < 0) /* FAT16 root dir */
2245 seeksector += fat_bpb->rootdiroffset;
2246 #endif
2248 file->eof = false;
2249 if (seeksector) {
2250 /* we need to find the sector BEFORE the requested, since
2251 the file struct stores the last accessed sector */
2252 seeksector--;
2253 numclusters = clusternum = seeksector / fat_bpb->bpb_secperclus;
2254 sectornum = seeksector % fat_bpb->bpb_secperclus;
2256 if (file->clusternum && clusternum >= file->clusternum)
2258 cluster = file->lastcluster;
2259 numclusters -= file->clusternum;
2262 for (i=0; i<numclusters; i++) {
2263 cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster);
2264 if (!cluster) {
2265 DEBUGF("Seeking beyond the end of the file! "
2266 "(sector %ld, cluster %ld)\n", seeksector, i);
2267 return -1;
2271 sector = cluster2sec(IF_MV2(fat_bpb,) cluster) + sectornum;
2273 else {
2274 sectornum = -1;
2277 LDEBUGF("fat_seek(%lx, %lx) == %lx, %lx, %lx\n",
2278 file->firstcluster, seeksector, cluster, sector, sectornum);
2280 file->lastcluster = cluster;
2281 file->lastsector = sector;
2282 file->clusternum = clusternum;
2283 file->sectornum = sectornum + 1;
2284 return 0;
2287 int fat_opendir(IF_MV2(int volume,)
2288 struct fat_dir *dir, unsigned long startcluster,
2289 const struct fat_dir *parent_dir)
2291 #ifdef HAVE_MULTIVOLUME
2292 struct bpb* fat_bpb = &fat_bpbs[volume];
2293 /* fixme: remove error check when done */
2294 if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted)
2296 LDEBUGF("fat_open() illegal volume %d\n", volume);
2297 return -1;
2299 #else
2300 struct bpb* fat_bpb = &fat_bpbs[0];
2301 #endif
2302 int rc;
2304 dir->entry = 0;
2305 dir->sector = 0;
2307 if (startcluster == 0)
2308 startcluster = fat_bpb->bpb_rootclus;
2310 rc = fat_open(IF_MV2(volume,) startcluster, &dir->file, parent_dir);
2311 if(rc)
2313 DEBUGF( "fat_opendir() - Couldn't open dir"
2314 " (error code %d)\n", rc);
2315 return rc * 10 - 1;
2318 return 0;
2321 /* Copies a segment of long file name (UTF-16 LE encoded) to the
2322 * destination buffer (UTF-8 encoded). Copying is stopped when
2323 * either 0x0000 or 0xffff (FAT pad char) is encountered.
2324 * Trailing \0 is also appended at the end of the UTF8-encoded
2325 * string.
2327 * utf16src utf16 (little endian) segment to copy
2328 * utf16count max number of the utf16-characters to copy
2329 * utf8dst where to write UTF8-encoded string to
2331 * returns the number of UTF-16 characters actually copied
2333 static int fat_copy_long_name_segment(unsigned char *utf16src,
2334 int utf16count, unsigned char *utf8dst) {
2335 int cnt = 0;
2336 while ((utf16count--) > 0) {
2337 unsigned short ucs = utf16src[0] | (utf16src[1] << 8);
2338 if ((ucs == 0) || (ucs == FAT_LONGNAME_PAD_UCS)) {
2339 break;
2341 utf8dst = utf8encode(ucs, utf8dst);
2342 utf16src += 2;
2343 cnt++;
2345 *utf8dst = 0;
2346 return cnt;
2349 int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
2351 bool done = false;
2352 int i;
2353 int rc;
2354 unsigned char firstbyte;
2355 /* Long file names are stored in special entries. Each entry holds
2356 up to 13 characters. Names can be max 255 chars (not bytes!) long
2357 hence max 20 entries are required. */
2358 int longarray[20];
2359 int longs=0;
2360 int sectoridx=0;
2361 unsigned char* cached_buf = dir->sectorcache[0];
2363 dir->entrycount = 0;
2365 while(!done)
2367 if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector )
2369 rc = fat_readwrite(&dir->file, 1, cached_buf, false);
2370 if (rc == 0) {
2371 /* eof */
2372 entry->name[0] = 0;
2373 break;
2375 if (rc < 0) {
2376 DEBUGF( "fat_getnext() - Couldn't read dir"
2377 " (error code %d)\n", rc);
2378 return rc * 10 - 1;
2380 dir->sector = dir->file.lastsector;
2383 for (i = dir->entry % DIR_ENTRIES_PER_SECTOR;
2384 i < DIR_ENTRIES_PER_SECTOR; i++)
2386 unsigned int entrypos = i * DIR_ENTRY_SIZE;
2388 firstbyte = cached_buf[entrypos];
2389 dir->entry++;
2391 if (firstbyte == 0xe5) {
2392 /* free entry */
2393 sectoridx = 0;
2394 dir->entrycount = 0;
2395 continue;
2398 if (firstbyte == 0) {
2399 /* last entry */
2400 entry->name[0] = 0;
2401 dir->entrycount = 0;
2402 return 0;
2405 dir->entrycount++;
2407 /* longname entry? */
2408 if ( ( cached_buf[entrypos + FATDIR_ATTR] &
2409 FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) {
2410 longarray[longs++] = entrypos + sectoridx;
2412 else {
2413 if ( parse_direntry(entry,
2414 &cached_buf[entrypos]) ) {
2416 /* don't return volume id entry */
2417 if ( (entry->attr &
2418 (FAT_ATTR_VOLUME_ID|FAT_ATTR_DIRECTORY))
2419 == FAT_ATTR_VOLUME_ID)
2420 continue;
2422 /* replace shortname with longname? */
2423 if ( longs ) {
2424 int j;
2425 /* This should be enough to hold any name segment
2426 utf8-encoded */
2427 unsigned char shortname[13]; /* 8+3+dot+\0 */
2428 /* Add 1 for trailing \0 */
2429 unsigned char longname_utf8segm[6*4 + 1];
2430 int longname_utf8len = 0;
2431 /* Temporarily store it */
2432 strcpy(shortname, entry->name);
2433 entry->name[0] = 0;
2435 /* iterate backwards through the dir entries */
2436 for (j=longs-1; j>=0; j--) {
2437 unsigned char* ptr = cached_buf;
2438 int index = longarray[j];
2439 /* current or cached sector? */
2440 if ( sectoridx >= SECTOR_SIZE ) {
2441 if ( sectoridx >= SECTOR_SIZE*2 ) {
2442 if ( ( index >= SECTOR_SIZE ) &&
2443 ( index < SECTOR_SIZE*2 ))
2444 ptr = dir->sectorcache[1];
2445 else
2446 ptr = dir->sectorcache[2];
2448 else {
2449 if ( index < SECTOR_SIZE )
2450 ptr = dir->sectorcache[1];
2453 index &= SECTOR_SIZE-1;
2456 /* Try to append each segment of the long name.
2457 Check if we'd exceed the buffer.
2458 Also check for FAT padding characters 0xFFFF. */
2459 if (fat_copy_long_name_segment(ptr + index + 1, 5,
2460 longname_utf8segm) == 0) break;
2461 /* logf("SG: %s, EN: %s", longname_utf8segm,
2462 entry->name); */
2463 longname_utf8len += strlen(longname_utf8segm);
2464 if (longname_utf8len < FAT_FILENAME_BYTES)
2465 strcat(entry->name, longname_utf8segm);
2466 else
2467 break;
2469 if (fat_copy_long_name_segment(ptr + index + 14, 6,
2470 longname_utf8segm) == 0) break;
2471 /* logf("SG: %s, EN: %s", longname_utf8segm,
2472 entry->name); */
2473 longname_utf8len += strlen(longname_utf8segm);
2474 if (longname_utf8len < FAT_FILENAME_BYTES)
2475 strcat(entry->name, longname_utf8segm);
2476 else
2477 break;
2479 if (fat_copy_long_name_segment(ptr + index + 28, 2,
2480 longname_utf8segm) == 0) break;
2481 /* logf("SG: %s, EN: %s", longname_utf8segm,
2482 entry->name); */
2483 longname_utf8len += strlen(longname_utf8segm);
2484 if (longname_utf8len < FAT_FILENAME_BYTES)
2485 strcat(entry->name, longname_utf8segm);
2486 else
2487 break;
2490 /* Does the utf8-encoded name fit into the entry? */
2491 if (longname_utf8len >= FAT_FILENAME_BYTES) {
2492 /* Take the short DOS name. Need to utf8-encode it
2493 since it may contain chars from the upper half of
2494 the OEM code page which wouldn't be a valid utf8.
2495 Beware: this file will be shown with strange
2496 glyphs in file browser since unicode 0x80 to 0x9F
2497 are control characters. */
2498 logf("SN-DOS: %s", shortname);
2499 unsigned char *utf8;
2500 utf8 = iso_decode(shortname, entry->name, -1,
2501 strlen(shortname));
2502 *utf8 = 0;
2503 logf("SN: %s", entry->name);
2504 } else {
2505 /* logf("LN: %s", entry->name);
2506 logf("LNLen: %d (%c)", longname_utf8len,
2507 entry->name[0]); */
2510 done = true;
2511 sectoridx = 0;
2512 i++;
2513 break;
2518 /* save this sector, for longname use */
2519 if ( sectoridx )
2520 memcpy( dir->sectorcache[2], dir->sectorcache[0], SECTOR_SIZE );
2521 else
2522 memcpy( dir->sectorcache[1], dir->sectorcache[0], SECTOR_SIZE );
2523 sectoridx += SECTOR_SIZE;
2526 return 0;
2529 unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume))
2531 #ifndef HAVE_MULTIVOLUME
2532 const int volume = 0;
2533 #endif
2534 struct bpb* fat_bpb = &fat_bpbs[volume];
2535 return fat_bpb->bpb_secperclus * SECTOR_SIZE;
2538 #ifdef HAVE_MULTIVOLUME
2539 bool fat_ismounted(int volume)
2541 return (volume<NUM_VOLUMES && fat_bpbs[volume].mounted);
2543 #endif