Hotswap code shuffling: Fix yellow. Simplify some target function access. Keep fat...
[kugel-rb.git] / firmware / drivers / fat.c
blob8ae3b70cd3e32adc679a086b59784a8ce1d0792d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23 #include <stdbool.h>
24 #include "fat.h"
25 #include "ata.h"
26 #include "debug.h"
27 #include "panic.h"
28 #include "system.h"
29 #include "timefuncs.h"
30 #include "kernel.h"
31 #include "rbunicode.h"
32 #include "logf.h"
33 #include "atoi.h"
35 #define BYTES2INT16(array,pos) \
36 (array[pos] | (array[pos+1] << 8 ))
37 #define BYTES2INT32(array,pos) \
38 ((long)array[pos] | ((long)array[pos+1] << 8 ) | \
39 ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
41 #define FATTYPE_FAT12 0
42 #define FATTYPE_FAT16 1
43 #define FATTYPE_FAT32 2
45 /* BPB offsets; generic */
46 #define BS_JMPBOOT 0
47 #define BS_OEMNAME 3
48 #define BPB_BYTSPERSEC 11
49 #define BPB_SECPERCLUS 13
50 #define BPB_RSVDSECCNT 14
51 #define BPB_NUMFATS 16
52 #define BPB_ROOTENTCNT 17
53 #define BPB_TOTSEC16 19
54 #define BPB_MEDIA 21
55 #define BPB_FATSZ16 22
56 #define BPB_SECPERTRK 24
57 #define BPB_NUMHEADS 26
58 #define BPB_HIDDSEC 28
59 #define BPB_TOTSEC32 32
61 /* fat12/16 */
62 #define BS_DRVNUM 36
63 #define BS_RESERVED1 37
64 #define BS_BOOTSIG 38
65 #define BS_VOLID 39
66 #define BS_VOLLAB 43
67 #define BS_FILSYSTYPE 54
69 /* fat32 */
70 #define BPB_FATSZ32 36
71 #define BPB_EXTFLAGS 40
72 #define BPB_FSVER 42
73 #define BPB_ROOTCLUS 44
74 #define BPB_FSINFO 48
75 #define BPB_BKBOOTSEC 50
76 #define BS_32_DRVNUM 64
77 #define BS_32_BOOTSIG 66
78 #define BS_32_VOLID 67
79 #define BS_32_VOLLAB 71
80 #define BS_32_FILSYSTYPE 82
82 #define BPB_LAST_WORD 510
85 /* attributes */
86 #define FAT_ATTR_LONG_NAME (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
87 FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID)
88 #define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
89 FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \
90 FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE )
92 /* NTRES flags */
93 #define FAT_NTRES_LC_NAME 0x08
94 #define FAT_NTRES_LC_EXT 0x10
96 #define FATDIR_NAME 0
97 #define FATDIR_ATTR 11
98 #define FATDIR_NTRES 12
99 #define FATDIR_CRTTIMETENTH 13
100 #define FATDIR_CRTTIME 14
101 #define FATDIR_CRTDATE 16
102 #define FATDIR_LSTACCDATE 18
103 #define FATDIR_FSTCLUSHI 20
104 #define FATDIR_WRTTIME 22
105 #define FATDIR_WRTDATE 24
106 #define FATDIR_FSTCLUSLO 26
107 #define FATDIR_FILESIZE 28
109 #define FATLONG_ORDER 0
110 #define FATLONG_TYPE 12
111 #define FATLONG_CHKSUM 13
113 #define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4)
114 #define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2)
115 #define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE)
116 #define DIR_ENTRY_SIZE 32
117 #define NAME_BYTES_PER_ENTRY 13
118 #define FAT_BAD_MARK 0x0ffffff7
119 #define FAT_EOF_MARK 0x0ffffff8
120 #define FAT_LONGNAME_PAD_BYTE 0xff
121 #define FAT_LONGNAME_PAD_UCS 0xffff
123 struct fsinfo {
124 unsigned long freecount; /* last known free cluster count */
125 unsigned long nextfree; /* first cluster to start looking for free
126 clusters, or 0xffffffff for no hint */
128 /* fsinfo offsets */
129 #define FSINFO_FREECOUNT 488
130 #define FSINFO_NEXTFREE 492
132 /* Note: This struct doesn't hold the raw values after mounting if
133 * bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte
134 * physical sectors. */
135 struct bpb
137 int bpb_bytspersec; /* Bytes per sector, typically 512 */
138 unsigned int bpb_secperclus; /* Sectors per cluster */
139 int bpb_rsvdseccnt; /* Number of reserved sectors */
140 int bpb_numfats; /* Number of FAT structures, typically 2 */
141 int bpb_totsec16; /* Number of sectors on the volume (old 16-bit) */
142 int bpb_media; /* Media type (typically 0xf0 or 0xf8) */
143 int bpb_fatsz16; /* Number of used sectors per FAT structure */
144 unsigned long bpb_totsec32; /* Number of sectors on the volume
145 (new 32-bit) */
146 unsigned int last_word; /* 0xAA55 */
148 /**** FAT32 specific *****/
149 long bpb_fatsz32;
150 long bpb_rootclus;
151 long bpb_fsinfo;
153 /* variables for internal use */
154 unsigned long fatsize;
155 unsigned long totalsectors;
156 unsigned long rootdirsector;
157 unsigned long firstdatasector;
158 unsigned long startsector;
159 unsigned long dataclusters;
160 struct fsinfo fsinfo;
161 #ifdef HAVE_FAT16SUPPORT
162 int bpb_rootentcnt; /* Number of dir entries in the root */
163 /* internals for FAT16 support */
164 bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */
165 unsigned int rootdiroffset; /* sector offset of root dir relative to start
166 * of first pseudo cluster */
167 #endif /* #ifdef HAVE_FAT16SUPPORT */
168 #ifdef HAVE_MULTIVOLUME
169 int drive; /* on which physical device is this located */
170 bool mounted; /* flag if this volume is mounted */
171 #endif
174 static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */
175 static bool initialized = false;
177 static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb));
178 static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb));
179 static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb));
180 static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,)
181 long secnum, bool dirty);
182 static void create_dos_name(const unsigned char *name, unsigned char *newname);
183 static void randomize_dos_name(unsigned char *name);
184 static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,)
185 unsigned long start);
186 static int transfer(IF_MV2(struct bpb* fat_bpb,) unsigned long start,
187 long count, char* buf, bool write );
189 #define FAT_CACHE_SIZE 0x20
190 #define FAT_CACHE_MASK (FAT_CACHE_SIZE-1)
192 struct fat_cache_entry
194 long secnum;
195 bool inuse;
196 bool dirty;
197 #ifdef HAVE_MULTIVOLUME
198 struct bpb* fat_vol ; /* shared cache for all volumes */
199 #endif
202 static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE];
203 static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE];
204 static struct mutex cache_mutex NOCACHEBSS_ATTR;
206 #if defined(HAVE_HOTSWAP) && !defined(HAVE_MMC) /* A better condition ?? */
207 void fat_lock(void)
209 mutex_lock(&cache_mutex);
212 void fat_unlock(void)
214 mutex_unlock(&cache_mutex);
216 #endif
218 static long cluster2sec(IF_MV2(struct bpb* fat_bpb,) long cluster)
220 #ifndef HAVE_MULTIVOLUME
221 struct bpb* fat_bpb = &fat_bpbs[0];
222 #endif
223 #ifdef HAVE_FAT16SUPPORT
224 /* negative clusters (FAT16 root dir) don't get the 2 offset */
225 int zerocluster = cluster < 0 ? 0 : 2;
226 #else
227 const long zerocluster = 2;
228 #endif
230 if (cluster > (long)(fat_bpb->dataclusters + 1))
232 DEBUGF( "cluster2sec() - Bad cluster number (%ld)\n", cluster);
233 return -1;
236 return (cluster - zerocluster) * fat_bpb->bpb_secperclus
237 + fat_bpb->firstdatasector;
240 void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
242 #ifndef HAVE_MULTIVOLUME
243 const int volume = 0;
244 #endif
245 struct bpb* fat_bpb = &fat_bpbs[volume];
246 if (size)
247 *size = fat_bpb->dataclusters * fat_bpb->bpb_secperclus / 2;
248 if (free)
249 *free = fat_bpb->fsinfo.freecount * fat_bpb->bpb_secperclus / 2;
252 void fat_init(void)
254 unsigned int i;
256 if (!initialized)
258 initialized = true;
259 mutex_init(&cache_mutex);
262 /* mark the FAT cache as unused */
263 for(i = 0;i < FAT_CACHE_SIZE;i++)
265 fat_cache[i].secnum = 8; /* We use a "safe" sector just in case */
266 fat_cache[i].inuse = false;
267 fat_cache[i].dirty = false;
268 #ifdef HAVE_MULTIVOLUME
269 fat_cache[i].fat_vol = NULL;
270 #endif
272 #ifdef HAVE_MULTIVOLUME
273 /* mark the possible volumes as not mounted */
274 for (i=0; i<NUM_VOLUMES;i++)
276 fat_bpbs[i].mounted = false;
278 #endif
281 int fat_mount(IF_MV2(int volume,) IF_MV2(int drive,) long startsector)
283 #ifndef HAVE_MULTIVOLUME
284 const int volume = 0;
285 #endif
286 struct bpb* fat_bpb = &fat_bpbs[volume];
287 unsigned char buf[SECTOR_SIZE];
288 int rc;
289 int secmult;
290 long datasec;
291 #ifdef HAVE_FAT16SUPPORT
292 int rootdirsectors;
293 #endif
295 /* Read the sector */
296 rc = ata_read_sectors(IF_MV2(drive,) startsector,1,buf);
297 if(rc)
299 DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)\n", rc);
300 return rc * 10 - 1;
303 memset(fat_bpb, 0, sizeof(struct bpb));
304 fat_bpb->startsector = startsector;
305 #ifdef HAVE_MULTIVOLUME
306 fat_bpb->drive = drive;
307 #endif
309 fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC);
310 secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE;
311 /* Sanity check is performed later */
313 fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS];
314 fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf,BPB_RSVDSECCNT);
315 fat_bpb->bpb_numfats = buf[BPB_NUMFATS];
316 fat_bpb->bpb_media = buf[BPB_MEDIA];
317 fat_bpb->bpb_fatsz16 = secmult * BYTES2INT16(buf,BPB_FATSZ16);
318 fat_bpb->bpb_fatsz32 = secmult * BYTES2INT32(buf,BPB_FATSZ32);
319 fat_bpb->bpb_totsec16 = secmult * BYTES2INT16(buf,BPB_TOTSEC16);
320 fat_bpb->bpb_totsec32 = secmult * BYTES2INT32(buf,BPB_TOTSEC32);
321 fat_bpb->last_word = BYTES2INT16(buf,BPB_LAST_WORD);
323 /* calculate a few commonly used values */
324 if (fat_bpb->bpb_fatsz16 != 0)
325 fat_bpb->fatsize = fat_bpb->bpb_fatsz16;
326 else
327 fat_bpb->fatsize = fat_bpb->bpb_fatsz32;
329 if (fat_bpb->bpb_totsec16 != 0)
330 fat_bpb->totalsectors = fat_bpb->bpb_totsec16;
331 else
332 fat_bpb->totalsectors = fat_bpb->bpb_totsec32;
334 #ifdef HAVE_FAT16SUPPORT
335 fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT);
336 if (!fat_bpb->bpb_bytspersec)
337 return -2;
338 rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE
339 + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec);
340 #endif /* #ifdef HAVE_FAT16SUPPORT */
342 fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt
343 #ifdef HAVE_FAT16SUPPORT
344 + rootdirsectors
345 #endif
346 + fat_bpb->bpb_numfats * fat_bpb->fatsize;
348 /* Determine FAT type */
349 datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector;
350 if (fat_bpb->bpb_secperclus)
351 fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus;
352 else
353 return -2;
355 #ifdef TEST_FAT
357 we are sometimes testing with "illegally small" fat32 images,
358 so we don't use the proper fat32 test case for test code
360 if ( fat_bpb->bpb_fatsz16 )
361 #else
362 if ( fat_bpb->dataclusters < 65525 )
363 #endif
364 { /* FAT16 */
365 #ifdef HAVE_FAT16SUPPORT
366 fat_bpb->is_fat16 = true;
367 if (fat_bpb->dataclusters < 4085)
368 { /* FAT12 */
369 DEBUGF("This is FAT12. Go away!\n");
370 return -2;
372 #else /* #ifdef HAVE_FAT16SUPPORT */
373 DEBUGF("This is not FAT32. Go away!\n");
374 return -2;
375 #endif /* #ifndef HAVE_FAT16SUPPORT */
378 #ifdef HAVE_FAT16SUPPORT
379 if (fat_bpb->is_fat16)
380 { /* FAT16 specific part of BPB */
381 int dirclusters;
382 fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt
383 + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16;
384 dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1)
385 / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */
386 /* I assign negative pseudo cluster numbers for the root directory,
387 their range is counted upward until -1. */
388 fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data*/
389 fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus
390 - rootdirsectors;
392 else
393 #endif /* #ifdef HAVE_FAT16SUPPORT */
394 { /* FAT32 specific part of BPB */
395 fat_bpb->bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS);
396 fat_bpb->bpb_fsinfo = secmult * BYTES2INT16(buf,BPB_FSINFO);
397 fat_bpb->rootdirsector = cluster2sec(IF_MV2(fat_bpb,)
398 fat_bpb->bpb_rootclus);
401 rc = bpb_is_sane(IF_MV(fat_bpb));
402 if (rc < 0)
404 DEBUGF( "fat_mount() - BPB is not sane\n");
405 return rc * 10 - 3;
408 #ifdef HAVE_FAT16SUPPORT
409 if (fat_bpb->is_fat16)
411 fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */
412 fat_bpb->fsinfo.nextfree = 0xffffffff;
414 else
415 #endif /* #ifdef HAVE_FAT16SUPPORT */
417 /* Read the fsinfo sector */
418 rc = ata_read_sectors(IF_MV2(drive,)
419 startsector + fat_bpb->bpb_fsinfo, 1, buf);
420 if (rc < 0)
422 DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)\n", rc);
423 return rc * 10 - 4;
425 fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT);
426 fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE);
429 /* calculate freecount if unset */
430 if ( fat_bpb->fsinfo.freecount == 0xffffffff )
432 fat_recalc_free(IF_MV(volume));
435 LDEBUGF("Freecount: %ld\n",fat_bpb->fsinfo.freecount);
436 LDEBUGF("Nextfree: 0x%lx\n",fat_bpb->fsinfo.nextfree);
437 LDEBUGF("Cluster count: 0x%lx\n",fat_bpb->dataclusters);
438 LDEBUGF("Sectors per cluster: %d\n",fat_bpb->bpb_secperclus);
439 LDEBUGF("FAT sectors: 0x%lx\n",fat_bpb->fatsize);
441 #ifdef HAVE_MULTIVOLUME
442 fat_bpb->mounted = true;
443 #endif
445 return 0;
448 #ifdef HAVE_HOTSWAP
449 int fat_unmount(int volume, bool flush)
451 int rc;
452 struct bpb* fat_bpb = &fat_bpbs[volume];
454 if(flush)
456 rc = flush_fat(fat_bpb); /* the clean way, while still alive */
458 else
459 { /* volume is not accessible any more, e.g. MMC removed */
460 int i;
461 mutex_lock(&cache_mutex);
462 for(i = 0;i < FAT_CACHE_SIZE;i++)
464 struct fat_cache_entry *fce = &fat_cache[i];
465 if(fce->inuse && fce->fat_vol == fat_bpb)
467 fce->inuse = false; /* discard all from that volume */
468 fce->dirty = false;
471 mutex_unlock(&cache_mutex);
472 rc = 0;
474 fat_bpb->mounted = false;
475 return rc;
477 #endif /* #ifdef HAVE_HOTSWAP */
479 void fat_recalc_free(IF_MV_NONVOID(int volume))
481 #ifndef HAVE_MULTIVOLUME
482 const int volume = 0;
483 #endif
484 struct bpb* fat_bpb = &fat_bpbs[volume];
485 long free = 0;
486 unsigned long i;
487 #ifdef HAVE_FAT16SUPPORT
488 if (fat_bpb->is_fat16)
490 for (i = 0; i<fat_bpb->fatsize; i++) {
491 unsigned int j;
492 unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
493 for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) {
494 unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j;
495 if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
496 break;
498 if (letoh16(fat[j]) == 0x0000) {
499 free++;
500 if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
501 fat_bpb->fsinfo.nextfree = c;
506 else
507 #endif /* #ifdef HAVE_FAT16SUPPORT */
509 for (i = 0; i<fat_bpb->fatsize; i++) {
510 unsigned int j;
511 unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
512 for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) {
513 unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j;
514 if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
515 break;
517 if (!(letoh32(fat[j]) & 0x0fffffff)) {
518 free++;
519 if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
520 fat_bpb->fsinfo.nextfree = c;
525 fat_bpb->fsinfo.freecount = free;
526 update_fsinfo(IF_MV(fat_bpb));
529 static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb))
531 #ifndef HAVE_MULTIVOLUME
532 struct bpb* fat_bpb = &fat_bpbs[0];
533 #endif
534 if(fat_bpb->bpb_bytspersec % SECTOR_SIZE)
536 DEBUGF( "bpb_is_sane() - Error: sector size is not sane (%d)\n",
537 fat_bpb->bpb_bytspersec);
538 return -1;
540 if((long)fat_bpb->bpb_secperclus * (long)fat_bpb->bpb_bytspersec
541 > 128L*1024L)
543 DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K "
544 "(%d * %d = %d)\n",
545 fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus,
546 fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus);
547 return -2;
549 if(fat_bpb->bpb_numfats != 2)
551 DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n",
552 fat_bpb->bpb_numfats);
554 if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8)
556 DEBUGF( "bpb_is_sane() - Warning: Non-standard "
557 "media type (0x%02x)\n",
558 fat_bpb->bpb_media);
560 if(fat_bpb->last_word != 0xaa55)
562 DEBUGF( "bpb_is_sane() - Error: Last word is not "
563 "0xaa55 (0x%04x)\n", fat_bpb->last_word);
564 return -3;
567 if (fat_bpb->fsinfo.freecount >
568 (fat_bpb->totalsectors - fat_bpb->firstdatasector)/
569 fat_bpb->bpb_secperclus)
571 DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size "
572 "(0x%04lx)\n", fat_bpb->fsinfo.freecount);
573 return -4;
576 return 0;
579 static void flush_fat_sector(struct fat_cache_entry *fce,
580 unsigned char *sectorbuf)
582 int rc;
583 long secnum;
585 /* With multivolume, use only the FAT info from the cached sector! */
586 #ifdef HAVE_MULTIVOLUME
587 secnum = fce->secnum + fce->fat_vol->startsector;
588 #else
589 secnum = fce->secnum + fat_bpbs[0].startsector;
590 #endif
592 /* Write to the first FAT */
593 rc = ata_write_sectors(IF_MV2(fce->fat_vol->drive,)
594 secnum, 1,
595 sectorbuf);
596 if(rc < 0)
598 panicf("flush_fat_sector() - Could not write sector %ld"
599 " (error %d)\n",
600 secnum, rc);
602 #ifdef HAVE_MULTIVOLUME
603 if(fce->fat_vol->bpb_numfats > 1)
604 #else
605 if(fat_bpbs[0].bpb_numfats > 1)
606 #endif
608 /* Write to the second FAT */
609 #ifdef HAVE_MULTIVOLUME
610 secnum += fce->fat_vol->fatsize;
611 #else
612 secnum += fat_bpbs[0].fatsize;
613 #endif
614 rc = ata_write_sectors(IF_MV2(fce->fat_vol->drive,)
615 secnum, 1, sectorbuf);
616 if(rc < 0)
618 panicf("flush_fat_sector() - Could not write sector %ld"
619 " (error %d)\n",
620 secnum, rc);
623 fce->dirty = false;
626 /* Note: The returned pointer is only safely valid until the next
627 task switch! (Any subsequent ata read/write may yield.) */
628 static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,)
629 long fatsector, bool dirty)
631 #ifndef HAVE_MULTIVOLUME
632 struct bpb* fat_bpb = &fat_bpbs[0];
633 #endif
634 long secnum = fatsector + fat_bpb->bpb_rsvdseccnt;
635 int cache_index = secnum & FAT_CACHE_MASK;
636 struct fat_cache_entry *fce = &fat_cache[cache_index];
637 unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0];
638 int rc;
640 mutex_lock(&cache_mutex); /* make changes atomic */
642 /* Delete the cache entry if it isn't the sector we want */
643 if(fce->inuse && (fce->secnum != secnum
644 #ifdef HAVE_MULTIVOLUME
645 || fce->fat_vol != fat_bpb
646 #endif
649 /* Write back if it is dirty */
650 if(fce->dirty)
652 flush_fat_sector(fce, sectorbuf);
654 fce->inuse = false;
657 /* Load the sector if it is not cached */
658 if(!fce->inuse)
660 rc = ata_read_sectors(IF_MV2(fat_bpb->drive,)
661 secnum + fat_bpb->startsector,1,
662 sectorbuf);
663 if(rc < 0)
665 DEBUGF( "cache_fat_sector() - Could not read sector %ld"
666 " (error %d)\n", secnum, rc);
667 mutex_unlock(&cache_mutex);
668 return NULL;
670 fce->inuse = true;
671 fce->secnum = secnum;
672 #ifdef HAVE_MULTIVOLUME
673 fce->fat_vol = fat_bpb;
674 #endif
676 if (dirty)
677 fce->dirty = true; /* dirt remains, sticky until flushed */
678 mutex_unlock(&cache_mutex);
679 return sectorbuf;
682 static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,)
683 unsigned long startcluster)
685 #ifndef HAVE_MULTIVOLUME
686 struct bpb* fat_bpb = &fat_bpbs[0];
687 #endif
688 unsigned long sector;
689 unsigned long offset;
690 unsigned long i;
692 #ifdef HAVE_FAT16SUPPORT
693 if (fat_bpb->is_fat16)
695 sector = startcluster / CLUSTERS_PER_FAT16_SECTOR;
696 offset = startcluster % CLUSTERS_PER_FAT16_SECTOR;
698 for (i = 0; i<fat_bpb->fatsize; i++) {
699 unsigned int j;
700 unsigned int nr = (i + sector) % fat_bpb->fatsize;
701 unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false);
702 if ( !fat )
703 break;
704 for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) {
705 int k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR;
706 if (letoh16(fat[k]) == 0x0000) {
707 unsigned int c = nr * CLUSTERS_PER_FAT16_SECTOR + k;
708 /* Ignore the reserved clusters 0 & 1, and also
709 cluster numbers out of bounds */
710 if ( c < 2 || c > fat_bpb->dataclusters+1 )
711 continue;
712 LDEBUGF("find_free_cluster(%x) == %x\n",startcluster,c);
713 fat_bpb->fsinfo.nextfree = c;
714 return c;
717 offset = 0;
720 else
721 #endif /* #ifdef HAVE_FAT16SUPPORT */
723 sector = startcluster / CLUSTERS_PER_FAT_SECTOR;
724 offset = startcluster % CLUSTERS_PER_FAT_SECTOR;
726 for (i = 0; i<fat_bpb->fatsize; i++) {
727 unsigned int j;
728 unsigned long nr = (i + sector) % fat_bpb->fatsize;
729 unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false);
730 if ( !fat )
731 break;
732 for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) {
733 int k = (j + offset) % CLUSTERS_PER_FAT_SECTOR;
734 if (!(letoh32(fat[k]) & 0x0fffffff)) {
735 unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k;
736 /* Ignore the reserved clusters 0 & 1, and also
737 cluster numbers out of bounds */
738 if ( c < 2 || c > fat_bpb->dataclusters+1 )
739 continue;
740 LDEBUGF("find_free_cluster(%lx) == %lx\n",startcluster,c);
741 fat_bpb->fsinfo.nextfree = c;
742 return c;
745 offset = 0;
749 LDEBUGF("find_free_cluster(%lx) == 0\n",startcluster);
750 return 0; /* 0 is an illegal cluster number */
753 static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry,
754 unsigned long val)
756 #ifndef HAVE_MULTIVOLUME
757 struct bpb* fat_bpb = &fat_bpbs[0];
758 #endif
759 #ifdef HAVE_FAT16SUPPORT
760 if (fat_bpb->is_fat16)
762 int sector = entry / CLUSTERS_PER_FAT16_SECTOR;
763 int offset = entry % CLUSTERS_PER_FAT16_SECTOR;
764 unsigned short* sec;
766 val &= 0xFFFF;
768 LDEBUGF("update_fat_entry(%x,%x)\n",entry,val);
770 if (entry==val)
771 panicf("Creating FAT loop: %lx,%lx\n",entry,val);
773 if ( entry < 2 )
774 panicf("Updating reserved FAT entry %ld.\n",entry);
776 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true);
777 if (!sec)
779 DEBUGF( "update_fat_entry() - Could not cache sector %d\n", sector);
780 return -1;
783 if ( val ) {
784 if (letoh16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0)
785 fat_bpb->fsinfo.freecount--;
787 else {
788 if (letoh16(sec[offset]))
789 fat_bpb->fsinfo.freecount++;
792 LDEBUGF("update_fat_entry: %d free clusters\n",
793 fat_bpb->fsinfo.freecount);
795 sec[offset] = htole16(val);
797 else
798 #endif /* #ifdef HAVE_FAT16SUPPORT */
800 long sector = entry / CLUSTERS_PER_FAT_SECTOR;
801 int offset = entry % CLUSTERS_PER_FAT_SECTOR;
802 unsigned long* sec;
804 LDEBUGF("update_fat_entry(%lx,%lx)\n",entry,val);
806 if (entry==val)
807 panicf("Creating FAT loop: %lx,%lx\n",entry,val);
809 if ( entry < 2 )
810 panicf("Updating reserved FAT entry %ld.\n",entry);
812 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true);
813 if (!sec)
815 DEBUGF("update_fat_entry() - Could not cache sector %ld\n", sector);
816 return -1;
819 if ( val ) {
820 if (!(letoh32(sec[offset]) & 0x0fffffff) &&
821 fat_bpb->fsinfo.freecount > 0)
822 fat_bpb->fsinfo.freecount--;
824 else {
825 if (letoh32(sec[offset]) & 0x0fffffff)
826 fat_bpb->fsinfo.freecount++;
829 LDEBUGF("update_fat_entry: %ld free clusters\n",
830 fat_bpb->fsinfo.freecount);
832 /* don't change top 4 bits */
833 sec[offset] &= htole32(0xf0000000);
834 sec[offset] |= htole32(val & 0x0fffffff);
837 return 0;
840 static long read_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry)
842 #ifdef HAVE_FAT16SUPPORT
843 #ifndef HAVE_MULTIVOLUME
844 struct bpb* fat_bpb = &fat_bpbs[0];
845 #endif
846 if (fat_bpb->is_fat16)
848 int sector = entry / CLUSTERS_PER_FAT16_SECTOR;
849 int offset = entry % CLUSTERS_PER_FAT16_SECTOR;
850 unsigned short* sec;
852 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false);
853 if (!sec)
855 DEBUGF( "read_fat_entry() - Could not cache sector %d\n", sector);
856 return -1;
859 return letoh16(sec[offset]);
861 else
862 #endif /* #ifdef HAVE_FAT16SUPPORT */
864 long sector = entry / CLUSTERS_PER_FAT_SECTOR;
865 int offset = entry % CLUSTERS_PER_FAT_SECTOR;
866 unsigned long* sec;
868 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false);
869 if (!sec)
871 DEBUGF( "read_fat_entry() - Could not cache sector %ld\n", sector);
872 return -1;
875 return letoh32(sec[offset]) & 0x0fffffff;
879 static long get_next_cluster(IF_MV2(struct bpb* fat_bpb,) long cluster)
881 long next_cluster;
882 long eof_mark = FAT_EOF_MARK;
884 #ifdef HAVE_FAT16SUPPORT
885 #ifndef HAVE_MULTIVOLUME
886 struct bpb* fat_bpb = &fat_bpbs[0];
887 #endif
888 if (fat_bpb->is_fat16)
890 eof_mark &= 0xFFFF; /* only 16 bit */
891 if (cluster < 0) /* FAT16 root dir */
892 return cluster + 1; /* don't use the FAT */
894 #endif
895 next_cluster = read_fat_entry(IF_MV2(fat_bpb,) cluster);
897 /* is this last cluster in chain? */
898 if ( next_cluster >= eof_mark )
899 return 0;
900 else
901 return next_cluster;
904 static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb))
906 #ifndef HAVE_MULTIVOLUME
907 struct bpb* fat_bpb = &fat_bpbs[0];
908 #endif
909 unsigned char fsinfo[SECTOR_SIZE];
910 unsigned long* intptr;
911 int rc;
913 #ifdef HAVE_FAT16SUPPORT
914 if (fat_bpb->is_fat16)
915 return 0; /* FAT16 has no FsInfo */
916 #endif /* #ifdef HAVE_FAT16SUPPORT */
918 /* update fsinfo */
919 rc = ata_read_sectors(IF_MV2(fat_bpb->drive,)
920 fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo);
921 if (rc < 0)
923 DEBUGF( "flush_fat() - Couldn't read FSInfo (error code %d)\n", rc);
924 return rc * 10 - 1;
926 intptr = (long*)&(fsinfo[FSINFO_FREECOUNT]);
927 *intptr = htole32(fat_bpb->fsinfo.freecount);
929 intptr = (long*)&(fsinfo[FSINFO_NEXTFREE]);
930 *intptr = htole32(fat_bpb->fsinfo.nextfree);
932 rc = ata_write_sectors(IF_MV2(fat_bpb->drive,)
933 fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo);
934 if (rc < 0)
936 DEBUGF( "flush_fat() - Couldn't write FSInfo (error code %d)\n", rc);
937 return rc * 10 - 2;
940 return 0;
943 static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb))
945 int i;
946 int rc;
947 unsigned char *sec;
948 LDEBUGF("flush_fat()\n");
950 mutex_lock(&cache_mutex);
951 for(i = 0;i < FAT_CACHE_SIZE;i++)
953 struct fat_cache_entry *fce = &fat_cache[i];
954 if(fce->inuse
955 #ifdef HAVE_MULTIVOLUME
956 && fce->fat_vol == fat_bpb
957 #endif
958 && fce->dirty)
960 sec = fat_cache_sectors[i];
961 flush_fat_sector(fce, sec);
964 mutex_unlock(&cache_mutex);
966 rc = update_fsinfo(IF_MV(fat_bpb));
967 if (rc < 0)
968 return rc * 10 - 3;
970 return 0;
973 static void fat_time(unsigned short* date,
974 unsigned short* time,
975 unsigned short* tenth )
977 #if CONFIG_RTC
978 struct tm* tm = get_time();
980 if (date)
981 *date = ((tm->tm_year - 80) << 9) |
982 ((tm->tm_mon + 1) << 5) |
983 tm->tm_mday;
985 if (time)
986 *time = (tm->tm_hour << 11) |
987 (tm->tm_min << 5) |
988 (tm->tm_sec >> 1);
990 if (tenth)
991 *tenth = (tm->tm_sec & 1) * 100;
992 #else
993 /* non-RTC version returns an increment from the supplied time, or a
994 * fixed standard time/date if no time given as input */
995 bool next_day = false;
997 if (time)
999 if (0 == *time)
1001 /* set to 00:15:00 */
1002 *time = (15 << 5);
1004 else
1006 unsigned short mins = (*time >> 5) & 0x003F;
1007 unsigned short hours = (*time >> 11) & 0x001F;
1008 if ((mins += 10) >= 60)
1010 mins = 0;
1011 hours++;
1013 if ((++hours) >= 24)
1015 hours = hours - 24;
1016 next_day = true;
1018 *time = (hours << 11) | (mins << 5);
1022 if (date)
1024 if (0 == *date)
1026 /* Macros to convert a 2-digit string to a decimal constant.
1027 (YEAR), MONTH and DAY are set by the date command, which outputs
1028 DAY as 00..31 and MONTH as 01..12. The leading zero would lead to
1029 misinterpretation as an octal constant. */
1030 #define S100(x) 1 ## x
1031 #define C2DIG2DEC(x) (S100(x)-100)
1032 /* set to build date */
1033 *date = ((YEAR - 1980) << 9) | (C2DIG2DEC(MONTH) << 5)
1034 | C2DIG2DEC(DAY);
1036 else
1038 unsigned short day = *date & 0x001F;
1039 unsigned short month = (*date >> 5) & 0x000F;
1040 unsigned short year = (*date >> 9) & 0x007F;
1041 if (next_day)
1043 /* do a very simple day increment - never go above 28 days */
1044 if (++day > 28)
1046 day = 1;
1047 if (++month > 12)
1049 month = 1;
1050 year++;
1053 *date = (year << 9) | (month << 5) | day;
1057 if (tenth)
1058 *tenth = 0;
1059 #endif /* CONFIG_RTC */
1062 static int write_long_name(struct fat_file* file,
1063 unsigned int firstentry,
1064 unsigned int numentries,
1065 const unsigned char* name,
1066 const unsigned char* shortname,
1067 bool is_directory)
1069 unsigned char buf[SECTOR_SIZE];
1070 unsigned char* entry;
1071 unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR;
1072 unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR;
1073 unsigned char chksum = 0;
1074 unsigned int i, j=0;
1075 unsigned int nameidx=0, namelen = utf8length(name);
1076 int rc;
1077 unsigned short name_utf16[namelen + 1];
1079 LDEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)\n",
1080 file->firstcluster, firstentry, numentries, name);
1082 rc = fat_seek(file, sector);
1083 if (rc<0)
1084 return rc * 10 - 1;
1086 rc = fat_readwrite(file, 1, buf, false);
1087 if (rc<1)
1088 return rc * 10 - 2;
1090 /* calculate shortname checksum */
1091 for (i=11; i>0; i--)
1092 chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++];
1094 /* calc position of last name segment */
1095 if ( namelen > NAME_BYTES_PER_ENTRY )
1096 for (nameidx=0;
1097 nameidx < (namelen - NAME_BYTES_PER_ENTRY);
1098 nameidx += NAME_BYTES_PER_ENTRY);
1100 /* we need to convert the name first */
1101 /* since it is written in reverse order */
1102 for (i = 0; i <= namelen; i++)
1103 name = utf8decode(name, &name_utf16[i]);
1105 for (i=0; i < numentries; i++) {
1106 /* new sector? */
1107 if ( idx >= DIR_ENTRIES_PER_SECTOR ) {
1108 /* update current sector */
1109 rc = fat_seek(file, sector);
1110 if (rc<0)
1111 return rc * 10 - 3;
1113 rc = fat_readwrite(file, 1, buf, true);
1114 if (rc<1)
1115 return rc * 10 - 4;
1117 /* read next sector */
1118 rc = fat_readwrite(file, 1, buf, false);
1119 if (rc<0) {
1120 LDEBUGF("Failed writing new sector: %d\n",rc);
1121 return rc * 10 - 5;
1123 if (rc==0)
1124 /* end of dir */
1125 memset(buf, 0, sizeof buf);
1127 sector++;
1128 idx = 0;
1131 entry = buf + idx * DIR_ENTRY_SIZE;
1133 /* verify this entry is free */
1134 if (entry[0] && entry[0] != 0xe5 )
1135 panicf("Dir entry %d in sector %x is not free! "
1136 "%02x %02x %02x %02x",
1137 idx, sector,
1138 entry[0], entry[1], entry[2], entry[3]);
1140 memset(entry, 0, DIR_ENTRY_SIZE);
1141 if ( i+1 < numentries ) {
1142 /* longname entry */
1143 unsigned int k, l = nameidx;
1145 entry[FATLONG_ORDER] = numentries-i-1;
1146 if (i==0) {
1147 /* mark this as last long entry */
1148 entry[FATLONG_ORDER] |= 0x40;
1150 /* pad name with 0xffff */
1151 for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
1152 for (k=14; k<26; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
1153 for (k=28; k<32; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
1155 /* set name */
1156 for (k=0; k<5 && l <= namelen; k++) {
1157 entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff);
1158 entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8);
1160 for (k=0; k<6 && l <= namelen; k++) {
1161 entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff);
1162 entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8);
1164 for (k=0; k<2 && l <= namelen; k++) {
1165 entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff);
1166 entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8);
1169 entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME;
1170 entry[FATDIR_FSTCLUSLO] = 0;
1171 entry[FATLONG_TYPE] = 0;
1172 entry[FATLONG_CHKSUM] = chksum;
1173 LDEBUGF("Longname entry %d: %s\n", idx, name+nameidx);
1175 else {
1176 /* shortname entry */
1177 unsigned short date=0, time=0, tenth=0;
1178 LDEBUGF("Shortname entry: %s\n", shortname);
1179 strncpy(entry + FATDIR_NAME, shortname, 11);
1180 entry[FATDIR_ATTR] = is_directory?FAT_ATTR_DIRECTORY:0;
1181 entry[FATDIR_NTRES] = 0;
1183 fat_time(&date, &time, &tenth);
1184 entry[FATDIR_CRTTIMETENTH] = tenth;
1185 *(unsigned short*)(entry + FATDIR_CRTTIME) = htole16(time);
1186 *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time);
1187 *(unsigned short*)(entry + FATDIR_CRTDATE) = htole16(date);
1188 *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date);
1189 *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date);
1191 idx++;
1192 nameidx -= NAME_BYTES_PER_ENTRY;
1195 /* update last sector */
1196 rc = fat_seek(file, sector);
1197 if (rc<0)
1198 return rc * 10 - 6;
1200 rc = fat_readwrite(file, 1, buf, true);
1201 if (rc<1)
1202 return rc * 10 - 7;
1204 return 0;
1207 static int fat_checkname(const unsigned char* newname)
1209 static const char invalid_chars[] = "\"*/:<>?\\|";
1210 int len = strlen(newname);
1211 /* More sanity checks are probably needed */
1212 if (len > 255 || newname[len - 1] == '.')
1214 return -1;
1216 while (*newname)
1218 if (*newname < ' ' || strchr(invalid_chars, *newname) != NULL)
1219 return -1;
1220 newname++;
1222 /* check trailing space(s) */
1223 if(*(--newname) == ' ')
1224 return -1;
1226 return 0;
1229 static int add_dir_entry(struct fat_dir* dir,
1230 struct fat_file* file,
1231 const char* name,
1232 bool is_directory,
1233 bool dotdir)
1235 #ifdef HAVE_MULTIVOLUME
1236 struct bpb* fat_bpb = &fat_bpbs[dir->file.volume];
1237 #else
1238 struct bpb* fat_bpb = &fat_bpbs[0];
1239 #endif
1240 unsigned char buf[SECTOR_SIZE];
1241 unsigned char shortname[12];
1242 int rc;
1243 unsigned int sector;
1244 bool done = false;
1245 int entries_needed, entries_found = 0;
1246 int firstentry;
1248 LDEBUGF( "add_dir_entry(%s,%lx)\n",
1249 name, file->firstcluster);
1251 /* Don't check dotdirs name for validity */
1252 if (dotdir == false) {
1253 rc = fat_checkname(name);
1254 if (rc < 0) {
1255 /* filename is invalid */
1256 return rc * 10 - 1;
1260 #ifdef HAVE_MULTIVOLUME
1261 file->volume = dir->file.volume; /* inherit the volume, to make sure */
1262 #endif
1264 /* The "." and ".." directory entries must not be long names */
1265 if(dotdir) {
1266 int i;
1267 strncpy(shortname, name, 12);
1268 for(i = strlen(shortname); i < 12; i++)
1269 shortname[i] = ' ';
1271 entries_needed = 1;
1272 } else {
1273 create_dos_name(name, shortname);
1275 /* one dir entry needed for every 13 bytes of filename,
1276 plus one entry for the short name */
1277 entries_needed = (utf8length(name) + (NAME_BYTES_PER_ENTRY-1))
1278 / NAME_BYTES_PER_ENTRY + 1;
1281 restart:
1282 firstentry = -1;
1284 rc = fat_seek(&dir->file, 0);
1285 if (rc < 0)
1286 return rc * 10 - 2;
1288 /* step 1: search for free entries and check for duplicate shortname */
1289 for (sector = 0; !done; sector++)
1291 unsigned int i;
1293 rc = fat_readwrite(&dir->file, 1, buf, false);
1294 if (rc < 0) {
1295 DEBUGF( "add_dir_entry() - Couldn't read dir"
1296 " (error code %d)\n", rc);
1297 return rc * 10 - 3;
1300 if (rc == 0) { /* current end of dir reached */
1301 LDEBUGF("End of dir on cluster boundary\n");
1302 break;
1305 /* look for free slots */
1306 for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++)
1308 switch (buf[i * DIR_ENTRY_SIZE]) {
1309 case 0:
1310 entries_found += DIR_ENTRIES_PER_SECTOR - i;
1311 LDEBUGF("Found end of dir %d\n",
1312 sector * DIR_ENTRIES_PER_SECTOR + i);
1313 i = DIR_ENTRIES_PER_SECTOR - 1;
1314 done = true;
1315 break;
1317 case 0xe5:
1318 entries_found++;
1319 LDEBUGF("Found free entry %d (%d/%d)\n",
1320 sector * DIR_ENTRIES_PER_SECTOR + i,
1321 entries_found, entries_needed);
1322 break;
1324 default:
1325 entries_found = 0;
1327 /* check that our intended shortname doesn't already exist */
1328 if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 11)) {
1329 /* shortname exists already, make a new one */
1330 randomize_dos_name(shortname);
1331 LDEBUGF("Duplicate shortname, changing to %s\n",
1332 shortname);
1334 /* name has changed, we need to restart search */
1335 goto restart;
1337 break;
1339 if (firstentry < 0 && (entries_found >= entries_needed))
1340 firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1
1341 - entries_found;
1345 /* step 2: extend the dir if necessary */
1346 if (firstentry < 0)
1348 LDEBUGF("Adding new sector(s) to dir\n");
1349 rc = fat_seek(&dir->file, sector);
1350 if (rc < 0)
1351 return rc * 10 - 4;
1352 memset(buf, 0, sizeof buf);
1354 /* we must clear whole clusters */
1355 for (; (entries_found < entries_needed) ||
1356 (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); sector++)
1358 if (sector >= (65536/DIR_ENTRIES_PER_SECTOR))
1359 return -5; /* dir too large -- FAT specification */
1361 rc = fat_readwrite(&dir->file, 1, buf, true);
1362 if (rc < 1) /* No more room or something went wrong */
1363 return rc * 10 - 6;
1365 entries_found += DIR_ENTRIES_PER_SECTOR;
1368 firstentry = sector * DIR_ENTRIES_PER_SECTOR - entries_found;
1371 /* step 3: add entry */
1372 sector = firstentry / DIR_ENTRIES_PER_SECTOR;
1373 LDEBUGF("Adding longname to entry %d in sector %d\n",
1374 firstentry, sector);
1376 rc = write_long_name(&dir->file, firstentry,
1377 entries_needed, name, shortname, is_directory);
1378 if (rc < 0)
1379 return rc * 10 - 7;
1381 /* remember where the shortname dir entry is located */
1382 file->direntry = firstentry + entries_needed - 1;
1383 file->direntries = entries_needed;
1384 file->dircluster = dir->file.firstcluster;
1385 LDEBUGF("Added new dir entry %d, using %d slots.\n",
1386 file->direntry, file->direntries);
1388 return 0;
1391 static unsigned char char2dos(unsigned char c, int* randomize)
1393 static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|";
1395 if (c <= 0x20)
1396 c = 0; /* Illegal char, remove */
1397 else if (strchr(invalid_chars, c) != NULL)
1399 /* Illegal char, replace */
1400 c = '_';
1401 *randomize = 1; /* as per FAT spec */
1403 else
1404 c = toupper(c);
1406 return c;
1409 static void create_dos_name(const unsigned char *name, unsigned char *newname)
1411 int i;
1412 unsigned char *ext;
1413 int randomize = 0;
1415 /* Find extension part */
1416 ext = strrchr(name, '.');
1417 if (ext == name) /* handle .dotnames */
1418 ext = NULL;
1420 /* needs to randomize? */
1421 if((ext && (strlen(ext) > 4)) ||
1422 ((ext ? (unsigned int)(ext-name) : strlen(name)) > 8) )
1423 randomize = 1;
1425 /* Name part */
1426 for (i = 0; *name && (!ext || name < ext) && (i < 8); name++)
1428 unsigned char c = char2dos(*name, &randomize);
1429 if (c)
1430 newname[i++] = c;
1433 /* Pad both name and extension */
1434 while (i < 11)
1435 newname[i++] = ' ';
1437 if (newname[0] == 0xe5) /* Special kanji character */
1438 newname[0] = 0x05;
1440 if (ext)
1441 { /* Extension part */
1442 ext++;
1443 for (i = 8; *ext && (i < 11); ext++)
1445 unsigned char c = char2dos(*ext, &randomize);
1446 if (c)
1447 newname[i++] = c;
1451 if(randomize)
1452 randomize_dos_name(newname);
1455 static void randomize_dos_name(unsigned char *name)
1457 unsigned char* tilde = NULL; /* ~ location */
1458 unsigned char* lastpt = NULL; /* last point of filename */
1459 unsigned char* nameptr = name; /* working copy of name pointer */
1460 unsigned char num[9]; /* holds number as string */
1461 int i = 0;
1462 int cnt = 1;
1463 int numlen;
1464 int offset;
1466 while(i++ < 8)
1468 /* hunt for ~ and where to put it */
1469 if((!tilde) && (*nameptr == '~'))
1470 tilde = nameptr;
1471 if((!lastpt) && ((*nameptr == ' ' || *nameptr == '~')))
1472 lastpt = nameptr;
1473 nameptr++;
1475 if(tilde)
1477 /* extract current count and increment */
1478 memcpy(num,tilde+1,7-(unsigned int)(tilde-name));
1479 num[7-(unsigned int)(tilde-name)] = 0;
1480 cnt = atoi(num) + 1;
1482 cnt %= 10000000; /* protection */
1483 snprintf(num, 9, "~%d", cnt); /* allow room for trailing zero */
1484 numlen = strlen(num); /* required space */
1485 offset = (unsigned int)(lastpt ? lastpt - name : 8); /* prev startpoint */
1486 if(offset > (8-numlen)) offset = 8-numlen; /* correct for new numlen */
1488 memcpy(&name[offset], num, numlen);
1490 /* in special case of counter overflow: pad with spaces */
1491 for(offset = offset+numlen; offset < 8; offset++)
1492 name[offset] = ' ';
1495 static int update_short_entry( struct fat_file* file, long size, int attr )
1497 unsigned char buf[SECTOR_SIZE];
1498 int sector = file->direntry / DIR_ENTRIES_PER_SECTOR;
1499 unsigned char* entry =
1500 buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR);
1501 unsigned long* sizeptr;
1502 unsigned short* clusptr;
1503 struct fat_file dir;
1504 int rc;
1506 LDEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)\n",
1507 file->firstcluster, file->direntry, size);
1509 /* create a temporary file handle for the dir holding this file */
1510 rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL);
1511 if (rc < 0)
1512 return rc * 10 - 1;
1514 rc = fat_seek( &dir, sector );
1515 if (rc<0)
1516 return rc * 10 - 2;
1518 rc = fat_readwrite(&dir, 1, buf, false);
1519 if (rc < 1)
1520 return rc * 10 - 3;
1522 if (!entry[0] || entry[0] == 0xe5)
1523 panicf("Updating size on empty dir entry %d\n", file->direntry);
1525 entry[FATDIR_ATTR] = attr & 0xFF;
1527 clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
1528 *clusptr = htole16(file->firstcluster >> 16);
1530 clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
1531 *clusptr = htole16(file->firstcluster & 0xffff);
1533 sizeptr = (long*)(entry + FATDIR_FILESIZE);
1534 *sizeptr = htole32(size);
1537 #if CONFIG_RTC
1538 unsigned short time = 0;
1539 unsigned short date = 0;
1540 #else
1541 /* get old time to increment from */
1542 unsigned short time = htole16(*(unsigned short*)(entry+FATDIR_WRTTIME));
1543 unsigned short date = htole16(*(unsigned short*)(entry+FATDIR_WRTDATE));
1544 #endif
1545 fat_time(&date, &time, NULL);
1546 *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time);
1547 *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date);
1548 *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date);
1551 rc = fat_seek( &dir, sector );
1552 if (rc < 0)
1553 return rc * 10 - 4;
1555 rc = fat_readwrite(&dir, 1, buf, true);
1556 if (rc < 1)
1557 return rc * 10 - 5;
1559 return 0;
1562 static int parse_direntry(struct fat_direntry *de, const unsigned char *buf)
1564 int i=0,j=0;
1565 unsigned char c;
1566 bool lowercase;
1568 memset(de, 0, sizeof(struct fat_direntry));
1569 de->attr = buf[FATDIR_ATTR];
1570 de->crttimetenth = buf[FATDIR_CRTTIMETENTH];
1571 de->crtdate = BYTES2INT16(buf,FATDIR_CRTDATE);
1572 de->crttime = BYTES2INT16(buf,FATDIR_CRTTIME);
1573 de->wrtdate = BYTES2INT16(buf,FATDIR_WRTDATE);
1574 de->wrttime = BYTES2INT16(buf,FATDIR_WRTTIME);
1575 de->filesize = BYTES2INT32(buf,FATDIR_FILESIZE);
1576 de->firstcluster = ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSLO)) |
1577 ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16);
1578 /* The double cast is to prevent a sign-extension to be done on CalmRISC16.
1579 (the result of the shift is always considered signed) */
1581 /* fix the name */
1582 lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME);
1583 c = buf[FATDIR_NAME];
1584 if (c == 0x05) /* special kanji char */
1585 c = 0xe5;
1586 i = 0;
1587 while (c != ' ') {
1588 de->name[j++] = lowercase ? tolower(c) : c;
1589 if (++i >= 8)
1590 break;
1591 c = buf[FATDIR_NAME+i];
1593 if (buf[FATDIR_NAME+8] != ' ') {
1594 lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT);
1595 de->name[j++] = '.';
1596 for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++)
1597 de->name[j++] = lowercase ? tolower(c) : c;
1599 return 1;
1602 int fat_open(IF_MV2(int volume,)
1603 long startcluster,
1604 struct fat_file *file,
1605 const struct fat_dir* dir)
1607 file->firstcluster = startcluster;
1608 file->lastcluster = startcluster;
1609 file->lastsector = 0;
1610 file->clusternum = 0;
1611 file->sectornum = 0;
1612 file->eof = false;
1613 #ifdef HAVE_MULTIVOLUME
1614 file->volume = volume;
1615 /* fixme: remove error check when done */
1616 if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted)
1618 LDEBUGF("fat_open() illegal volume %d\n", volume);
1619 return -1;
1621 #endif
1623 /* remember where the file's dir entry is located */
1624 if ( dir ) {
1625 file->direntry = dir->entry - 1;
1626 file->direntries = dir->entrycount;
1627 file->dircluster = dir->file.firstcluster;
1629 LDEBUGF("fat_open(%lx), entry %d\n",startcluster,file->direntry);
1630 return 0;
1633 int fat_create_file(const char* name,
1634 struct fat_file* file,
1635 struct fat_dir* dir)
1637 int rc;
1639 LDEBUGF("fat_create_file(\"%s\",%lx,%lx)\n",name,(long)file,(long)dir);
1640 rc = add_dir_entry(dir, file, name, false, false);
1641 if (!rc) {
1642 file->firstcluster = 0;
1643 file->lastcluster = 0;
1644 file->lastsector = 0;
1645 file->clusternum = 0;
1646 file->sectornum = 0;
1647 file->eof = false;
1650 return rc;
1653 int fat_create_dir(const char* name,
1654 struct fat_dir* newdir,
1655 struct fat_dir* dir)
1657 #ifdef HAVE_MULTIVOLUME
1658 struct bpb* fat_bpb = &fat_bpbs[dir->file.volume];
1659 #else
1660 struct bpb* fat_bpb = &fat_bpbs[0];
1661 #endif
1662 unsigned char buf[SECTOR_SIZE];
1663 int i;
1664 long sector;
1665 int rc;
1666 struct fat_file dummyfile;
1668 LDEBUGF("fat_create_dir(\"%s\",%lx,%lx)\n",name,(long)newdir,(long)dir);
1670 memset(newdir, 0, sizeof(struct fat_dir));
1671 memset(&dummyfile, 0, sizeof(struct fat_file));
1673 /* First, add the entry in the parent directory */
1674 rc = add_dir_entry(dir, &newdir->file, name, true, false);
1675 if (rc < 0)
1676 return rc * 10 - 1;
1678 /* Allocate a new cluster for the directory */
1679 newdir->file.firstcluster = find_free_cluster(IF_MV2(fat_bpb,)
1680 fat_bpb->fsinfo.nextfree);
1681 if(newdir->file.firstcluster == 0)
1682 return -1;
1684 update_fat_entry(IF_MV2(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK);
1686 /* Clear the entire cluster */
1687 memset(buf, 0, sizeof buf);
1688 sector = cluster2sec(IF_MV2(fat_bpb,) newdir->file.firstcluster);
1689 for(i = 0;i < (int)fat_bpb->bpb_secperclus;i++) {
1690 rc = transfer(IF_MV2(fat_bpb,) sector + i, 1, buf, true );
1691 if (rc < 0)
1692 return rc * 10 - 2;
1695 /* Then add the "." entry */
1696 rc = add_dir_entry(newdir, &dummyfile, ".", true, true);
1697 if (rc < 0)
1698 return rc * 10 - 3;
1699 dummyfile.firstcluster = newdir->file.firstcluster;
1700 update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY);
1702 /* and the ".." entry */
1703 rc = add_dir_entry(newdir, &dummyfile, "..", true, true);
1704 if (rc < 0)
1705 return rc * 10 - 4;
1707 /* The root cluster is cluster 0 in the ".." entry */
1708 if(dir->file.firstcluster == fat_bpb->bpb_rootclus)
1709 dummyfile.firstcluster = 0;
1710 else
1711 dummyfile.firstcluster = dir->file.firstcluster;
1712 update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY);
1714 /* Set the firstcluster field in the direntry */
1715 update_short_entry(&newdir->file, 0, FAT_ATTR_DIRECTORY);
1717 rc = flush_fat(IF_MV(fat_bpb));
1718 if (rc < 0)
1719 return rc * 10 - 5;
1721 return rc;
1724 int fat_truncate(const struct fat_file *file)
1726 /* truncate trailing clusters */
1727 long next;
1728 long last = file->lastcluster;
1729 #ifdef HAVE_MULTIVOLUME
1730 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1731 #endif
1733 LDEBUGF("fat_truncate(%lx, %lx)\n", file->firstcluster, last);
1735 for ( last = get_next_cluster(IF_MV2(fat_bpb,) last); last; last = next ) {
1736 next = get_next_cluster(IF_MV2(fat_bpb,) last);
1737 update_fat_entry(IF_MV2(fat_bpb,) last,0);
1739 if (file->lastcluster)
1740 update_fat_entry(IF_MV2(fat_bpb,) file->lastcluster,FAT_EOF_MARK);
1742 return 0;
1745 int fat_closewrite(struct fat_file *file, long size, int attr)
1747 int rc;
1748 #ifdef HAVE_MULTIVOLUME
1749 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1750 #endif
1751 LDEBUGF("fat_closewrite(size=%ld)\n",size);
1753 if (!size) {
1754 /* empty file */
1755 if ( file->firstcluster ) {
1756 update_fat_entry(IF_MV2(fat_bpb,) file->firstcluster, 0);
1757 file->firstcluster = 0;
1761 if (file->dircluster) {
1762 rc = update_short_entry(file, size, attr);
1763 if (rc < 0)
1764 return rc * 10 - 1;
1767 flush_fat(IF_MV(fat_bpb));
1769 #ifdef TEST_FAT
1770 if ( file->firstcluster ) {
1771 /* debug */
1772 #ifdef HAVE_MULTIVOLUME
1773 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1774 #else
1775 struct bpb* fat_bpb = &fat_bpbs[0];
1776 #endif
1777 long count = 0;
1778 long len;
1779 long next;
1780 for ( next = file->firstcluster; next;
1781 next = get_next_cluster(IF_MV2(fat_bpb,) next) ) {
1782 LDEBUGF("cluster %ld: %lx\n", count, next);
1783 count++;
1785 len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE;
1786 LDEBUGF("File is %ld clusters (chainlen=%ld, size=%ld)\n",
1787 count, len, size );
1788 if ( len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE)
1789 panicf("Cluster chain is too long\n");
1790 if ( len < size )
1791 panicf("Cluster chain is too short\n");
1793 #endif
1795 return 0;
1798 static int free_direntries(struct fat_file* file)
1800 unsigned char buf[SECTOR_SIZE];
1801 struct fat_file dir;
1802 int numentries = file->direntries;
1803 unsigned int entry = file->direntry - numentries + 1;
1804 unsigned int sector = entry / DIR_ENTRIES_PER_SECTOR;
1805 int i;
1806 int rc;
1808 /* create a temporary file handle for the dir holding this file */
1809 rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL);
1810 if (rc < 0)
1811 return rc * 10 - 1;
1813 rc = fat_seek( &dir, sector );
1814 if (rc < 0)
1815 return rc * 10 - 2;
1817 rc = fat_readwrite(&dir, 1, buf, false);
1818 if (rc < 1)
1819 return rc * 10 - 3;
1821 for (i=0; i < numentries; i++) {
1822 LDEBUGF("Clearing dir entry %d (%d/%d)\n",
1823 entry, i+1, numentries);
1824 buf[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5;
1825 entry++;
1827 if ( (entry % DIR_ENTRIES_PER_SECTOR) == 0 ) {
1828 /* flush this sector */
1829 rc = fat_seek(&dir, sector);
1830 if (rc < 0)
1831 return rc * 10 - 4;
1833 rc = fat_readwrite(&dir, 1, buf, true);
1834 if (rc < 1)
1835 return rc * 10 - 5;
1837 if ( i+1 < numentries ) {
1838 /* read next sector */
1839 rc = fat_readwrite(&dir, 1, buf, false);
1840 if (rc < 1)
1841 return rc * 10 - 6;
1843 sector++;
1847 if ( entry % DIR_ENTRIES_PER_SECTOR ) {
1848 /* flush this sector */
1849 rc = fat_seek(&dir, sector);
1850 if (rc < 0)
1851 return rc * 10 - 7;
1853 rc = fat_readwrite(&dir, 1, buf, true);
1854 if (rc < 1)
1855 return rc * 10 - 8;
1858 return 0;
1861 int fat_remove(struct fat_file* file)
1863 long next, last = file->firstcluster;
1864 int rc;
1865 #ifdef HAVE_MULTIVOLUME
1866 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1867 #endif
1869 LDEBUGF("fat_remove(%lx)\n",last);
1871 while ( last ) {
1872 next = get_next_cluster(IF_MV2(fat_bpb,) last);
1873 update_fat_entry(IF_MV2(fat_bpb,) last,0);
1874 last = next;
1877 if ( file->dircluster ) {
1878 rc = free_direntries(file);
1879 if (rc < 0)
1880 return rc * 10 - 1;
1883 file->firstcluster = 0;
1884 file->dircluster = 0;
1886 rc = flush_fat(IF_MV(fat_bpb));
1887 if (rc < 0)
1888 return rc * 10 - 2;
1890 return 0;
1893 int fat_rename(struct fat_file* file,
1894 struct fat_dir* dir,
1895 const unsigned char* newname,
1896 long size,
1897 int attr)
1899 int rc;
1900 struct fat_dir olddir;
1901 struct fat_file newfile = *file;
1902 unsigned char buf[SECTOR_SIZE];
1903 unsigned char* entry = NULL;
1904 unsigned short* clusptr = NULL;
1905 unsigned int parentcluster;
1906 #ifdef HAVE_MULTIVOLUME
1907 struct bpb* fat_bpb = &fat_bpbs[file->volume];
1909 if (file->volume != dir->file.volume) {
1910 DEBUGF("No rename across volumes!\n");
1911 return -1;
1913 #else
1914 struct bpb* fat_bpb = &fat_bpbs[0];
1915 #endif
1917 if ( !file->dircluster ) {
1918 DEBUGF("File has no dir cluster!\n");
1919 return -2;
1922 /* create a temporary file handle */
1923 rc = fat_opendir(IF_MV2(file->volume,) &olddir, file->dircluster, NULL);
1924 if (rc < 0)
1925 return rc * 10 - 1;
1927 /* create new name */
1928 rc = add_dir_entry(dir, &newfile, newname, false, false);
1929 if (rc < 0)
1930 return rc * 10 - 2;
1932 /* write size and cluster link */
1933 rc = update_short_entry(&newfile, size, attr);
1934 if (rc < 0)
1935 return rc * 10 - 3;
1937 /* remove old name */
1938 rc = free_direntries(file);
1939 if (rc < 0)
1940 return rc * 10 - 4;
1942 rc = flush_fat(IF_MV(fat_bpb));
1943 if (rc < 0)
1944 return rc * 10 - 5;
1946 /* if renaming a directory, update the .. entry to make sure
1947 it points to its parent directory (we don't check if it was a move) */
1948 if(FAT_ATTR_DIRECTORY == attr) {
1949 /* open the dir that was renamed, we re-use the olddir struct */
1950 rc = fat_opendir(IF_MV2(file->volume,) &olddir, newfile.firstcluster,
1951 NULL);
1952 if (rc < 0)
1953 return rc * 10 - 6;
1955 /* get the first sector of the dir */
1956 rc = fat_seek(&olddir.file, 0);
1957 if (rc < 0)
1958 return rc * 10 - 7;
1960 rc = fat_readwrite(&olddir.file, 1, buf, false);
1961 if (rc < 0)
1962 return rc * 10 - 8;
1964 /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */
1965 if(dir->file.firstcluster == fat_bpb->bpb_rootclus)
1966 parentcluster = 0;
1967 else
1968 parentcluster = dir->file.firstcluster;
1970 entry = buf + DIR_ENTRY_SIZE;
1971 if(strncmp(".. ", entry, 11))
1973 /* .. entry must be second entry according to FAT spec (p.29) */
1974 DEBUGF("Second dir entry is not double-dot!\n");
1975 return rc * 10 - 9;
1977 clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
1978 *clusptr = htole16(parentcluster >> 16);
1980 clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
1981 *clusptr = htole16(parentcluster & 0xffff);
1983 /* write back this sector */
1984 rc = fat_seek(&olddir.file, 0);
1985 if (rc < 0)
1986 return rc * 10 - 7;
1988 rc = fat_readwrite(&olddir.file, 1, buf, true);
1989 if (rc < 1)
1990 return rc * 10 - 8;
1993 return 0;
1996 static long next_write_cluster(struct fat_file* file,
1997 long oldcluster,
1998 long* newsector)
2000 #ifdef HAVE_MULTIVOLUME
2001 struct bpb* fat_bpb = &fat_bpbs[file->volume];
2002 #else
2003 struct bpb* fat_bpb = &fat_bpbs[0];
2004 #endif
2005 long cluster = 0;
2006 long sector;
2008 LDEBUGF("next_write_cluster(%lx,%lx)\n",file->firstcluster, oldcluster);
2010 if (oldcluster)
2011 cluster = get_next_cluster(IF_MV2(fat_bpb,) oldcluster);
2013 if (!cluster) {
2014 if (oldcluster > 0)
2015 cluster = find_free_cluster(IF_MV2(fat_bpb,) oldcluster+1);
2016 else if (oldcluster == 0)
2017 cluster = find_free_cluster(IF_MV2(fat_bpb,)
2018 fat_bpb->fsinfo.nextfree);
2019 #ifdef HAVE_FAT16SUPPORT
2020 else /* negative, pseudo-cluster of the root dir */
2021 return 0; /* impossible to append something to the root */
2022 #endif
2024 if (cluster) {
2025 if (oldcluster)
2026 update_fat_entry(IF_MV2(fat_bpb,) oldcluster, cluster);
2027 else
2028 file->firstcluster = cluster;
2029 update_fat_entry(IF_MV2(fat_bpb,) cluster, FAT_EOF_MARK);
2031 else {
2032 #ifdef TEST_FAT
2033 if (fat_bpb->fsinfo.freecount>0)
2034 panicf("There is free space, but find_free_cluster() "
2035 "didn't find it!\n");
2036 #endif
2037 DEBUGF("next_write_cluster(): Disk full!\n");
2038 return 0;
2041 sector = cluster2sec(IF_MV2(fat_bpb,) cluster);
2042 if (sector<0)
2043 return 0;
2045 *newsector = sector;
2046 return cluster;
2049 static int transfer(IF_MV2(struct bpb* fat_bpb,)
2050 unsigned long start, long count, char* buf, bool write )
2052 #ifndef HAVE_MULTIVOLUME
2053 struct bpb* fat_bpb = &fat_bpbs[0];
2054 #endif
2055 int rc;
2057 LDEBUGF("transfer(s=%lx, c=%lx, %s)\n",
2058 start+ fat_bpb->startsector, count, write?"write":"read");
2059 if (write) {
2060 unsigned long firstallowed;
2061 #ifdef HAVE_FAT16SUPPORT
2062 if (fat_bpb->is_fat16)
2063 firstallowed = fat_bpb->rootdirsector;
2064 else
2065 #endif
2066 firstallowed = fat_bpb->firstdatasector;
2068 if (start < firstallowed)
2069 panicf("Write %ld before data\n", firstallowed - start);
2070 if (start + count > fat_bpb->totalsectors)
2071 panicf("Write %ld after data\n",
2072 start + count - fat_bpb->totalsectors);
2073 rc = ata_write_sectors(IF_MV2(fat_bpb->drive,)
2074 start + fat_bpb->startsector, count, buf);
2076 else
2077 rc = ata_read_sectors(IF_MV2(fat_bpb->drive,)
2078 start + fat_bpb->startsector, count, buf);
2079 if (rc < 0) {
2080 DEBUGF( "transfer() - Couldn't %s sector %lx"
2081 " (error code %d)\n",
2082 write ? "write":"read", start, rc);
2083 return rc;
2085 return 0;
2089 long fat_readwrite( struct fat_file *file, long sectorcount,
2090 void* buf, bool write )
2092 #ifdef HAVE_MULTIVOLUME
2093 struct bpb* fat_bpb = &fat_bpbs[file->volume];
2094 #else
2095 struct bpb* fat_bpb = &fat_bpbs[0];
2096 #endif
2097 long cluster = file->lastcluster;
2098 long sector = file->lastsector;
2099 long clusternum = file->clusternum;
2100 long numsec = file->sectornum;
2101 bool eof = file->eof;
2102 long first=0, last=0;
2103 long i;
2104 int rc;
2106 LDEBUGF( "fat_readwrite(file:%lx,count:0x%lx,buf:%lx,%s)\n",
2107 file->firstcluster,sectorcount,(long)buf,write?"write":"read");
2108 LDEBUGF( "fat_readwrite: sec=%lx numsec=%ld eof=%d\n",
2109 sector,numsec, eof?1:0);
2111 if ( eof && !write)
2112 return 0;
2114 /* find sequential sectors and write them all at once */
2115 for (i=0; (i < sectorcount) && (sector > -1); i++ ) {
2116 numsec++;
2117 if ( numsec > (long)fat_bpb->bpb_secperclus || !cluster ) {
2118 long oldcluster = cluster;
2119 long oldsector = sector;
2120 long oldnumsec = numsec;
2121 if (write)
2122 cluster = next_write_cluster(file, cluster, &sector);
2123 else {
2124 cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster);
2125 sector = cluster2sec(IF_MV2(fat_bpb,) cluster);
2128 clusternum++;
2129 numsec=1;
2131 if (!cluster) {
2132 eof = true;
2133 if ( write ) {
2134 /* remember last cluster, in case
2135 we want to append to the file */
2136 sector = oldsector;
2137 cluster = oldcluster;
2138 numsec = oldnumsec;
2139 clusternum--;
2140 i = -1; /* Error code */
2141 break;
2144 else
2145 eof = false;
2147 else {
2148 if (sector)
2149 sector++;
2150 else {
2151 /* look up first sector of file */
2152 sector = cluster2sec(IF_MV2(fat_bpb,) file->firstcluster);
2153 numsec=1;
2154 #ifdef HAVE_FAT16SUPPORT
2155 if (file->firstcluster < 0)
2156 { /* FAT16 root dir */
2157 sector += fat_bpb->rootdiroffset;
2158 numsec += fat_bpb->rootdiroffset;
2160 #endif
2164 if (!first)
2165 first = sector;
2167 if ( ((sector != first) && (sector != last+1)) || /* not sequential */
2168 (last-first+1 == 256) ) { /* max 256 sectors per ata request */
2169 long count = last - first + 1;
2170 rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write );
2171 if (rc < 0)
2172 return rc * 10 - 1;
2174 buf = (char *)buf + count * SECTOR_SIZE;
2175 first = sector;
2178 if ((i == sectorcount-1) && /* last sector requested */
2179 (!eof))
2181 long count = sector - first + 1;
2182 rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write );
2183 if (rc < 0)
2184 return rc * 10 - 2;
2187 last = sector;
2190 file->lastcluster = cluster;
2191 file->lastsector = sector;
2192 file->clusternum = clusternum;
2193 file->sectornum = numsec;
2194 file->eof = eof;
2196 /* if eof, don't report last block as read/written */
2197 if (eof)
2198 i--;
2200 DEBUGF("Sectors written: %ld\n", i);
2201 return i;
2204 int fat_seek(struct fat_file *file, unsigned long seeksector )
2206 #ifdef HAVE_MULTIVOLUME
2207 struct bpb* fat_bpb = &fat_bpbs[file->volume];
2208 #else
2209 struct bpb* fat_bpb = &fat_bpbs[0];
2210 #endif
2211 long clusternum=0, numclusters=0, sectornum=0, sector=0;
2212 long cluster = file->firstcluster;
2213 long i;
2215 #ifdef HAVE_FAT16SUPPORT
2216 if (cluster < 0) /* FAT16 root dir */
2217 seeksector += fat_bpb->rootdiroffset;
2218 #endif
2220 file->eof = false;
2221 if (seeksector) {
2222 /* we need to find the sector BEFORE the requested, since
2223 the file struct stores the last accessed sector */
2224 seeksector--;
2225 numclusters = clusternum = seeksector / fat_bpb->bpb_secperclus;
2226 sectornum = seeksector % fat_bpb->bpb_secperclus;
2228 if (file->clusternum && clusternum >= file->clusternum)
2230 cluster = file->lastcluster;
2231 numclusters -= file->clusternum;
2234 for (i=0; i<numclusters; i++) {
2235 cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster);
2236 if (!cluster) {
2237 DEBUGF("Seeking beyond the end of the file! "
2238 "(sector %ld, cluster %ld)\n", seeksector, i);
2239 return -1;
2243 sector = cluster2sec(IF_MV2(fat_bpb,) cluster) + sectornum;
2245 else {
2246 sectornum = -1;
2249 LDEBUGF("fat_seek(%lx, %lx) == %lx, %lx, %lx\n",
2250 file->firstcluster, seeksector, cluster, sector, sectornum);
2252 file->lastcluster = cluster;
2253 file->lastsector = sector;
2254 file->clusternum = clusternum;
2255 file->sectornum = sectornum + 1;
2256 return 0;
2259 int fat_opendir(IF_MV2(int volume,)
2260 struct fat_dir *dir, unsigned long startcluster,
2261 const struct fat_dir *parent_dir)
2263 #ifdef HAVE_MULTIVOLUME
2264 struct bpb* fat_bpb = &fat_bpbs[volume];
2265 /* fixme: remove error check when done */
2266 if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted)
2268 LDEBUGF("fat_open() illegal volume %d\n", volume);
2269 return -1;
2271 #else
2272 struct bpb* fat_bpb = &fat_bpbs[0];
2273 #endif
2274 int rc;
2276 dir->entry = 0;
2277 dir->sector = 0;
2279 if (startcluster == 0)
2280 startcluster = fat_bpb->bpb_rootclus;
2282 rc = fat_open(IF_MV2(volume,) startcluster, &dir->file, parent_dir);
2283 if(rc)
2285 DEBUGF( "fat_opendir() - Couldn't open dir"
2286 " (error code %d)\n", rc);
2287 return rc * 10 - 1;
2290 return 0;
2293 /* Copies a segment of long file name (UTF-16 LE encoded) to the
2294 * destination buffer (UTF-8 encoded). Copying is stopped when
2295 * either 0x0000 or 0xffff (FAT pad char) is encountered.
2296 * Trailing \0 is also appended at the end of the UTF8-encoded
2297 * string.
2299 * utf16src utf16 (little endian) segment to copy
2300 * utf16count max number of the utf16-characters to copy
2301 * utf8dst where to write UTF8-encoded string to
2303 * returns the number of UTF-16 characters actually copied
2305 static int fat_copy_long_name_segment(unsigned char *utf16src,
2306 int utf16count, unsigned char *utf8dst) {
2307 int cnt = 0;
2308 while ((utf16count--) > 0) {
2309 unsigned short ucs = utf16src[0] | (utf16src[1] << 8);
2310 if ((ucs == 0) || (ucs == FAT_LONGNAME_PAD_UCS)) {
2311 break;
2313 utf8dst = utf8encode(ucs, utf8dst);
2314 utf16src += 2;
2315 cnt++;
2317 *utf8dst = 0;
2318 return cnt;
2321 int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
2323 bool done = false;
2324 int i;
2325 int rc;
2326 unsigned char firstbyte;
2327 /* Long file names are stored in special entries. Each entry holds
2328 up to 13 characters. Names can be max 255 chars (not bytes!) long
2329 hence max 20 entries are required. */
2330 int longarray[20];
2331 int longs=0;
2332 int sectoridx=0;
2333 unsigned char* cached_buf = dir->sectorcache[0];
2335 dir->entrycount = 0;
2337 while(!done)
2339 if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector )
2341 rc = fat_readwrite(&dir->file, 1, cached_buf, false);
2342 if (rc == 0) {
2343 /* eof */
2344 entry->name[0] = 0;
2345 break;
2347 if (rc < 0) {
2348 DEBUGF( "fat_getnext() - Couldn't read dir"
2349 " (error code %d)\n", rc);
2350 return rc * 10 - 1;
2352 dir->sector = dir->file.lastsector;
2355 for (i = dir->entry % DIR_ENTRIES_PER_SECTOR;
2356 i < DIR_ENTRIES_PER_SECTOR; i++)
2358 unsigned int entrypos = i * DIR_ENTRY_SIZE;
2360 firstbyte = cached_buf[entrypos];
2361 dir->entry++;
2363 if (firstbyte == 0xe5) {
2364 /* free entry */
2365 sectoridx = 0;
2366 dir->entrycount = 0;
2367 continue;
2370 if (firstbyte == 0) {
2371 /* last entry */
2372 entry->name[0] = 0;
2373 dir->entrycount = 0;
2374 return 0;
2377 dir->entrycount++;
2379 /* longname entry? */
2380 if ( ( cached_buf[entrypos + FATDIR_ATTR] &
2381 FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) {
2382 longarray[longs++] = entrypos + sectoridx;
2384 else {
2385 if ( parse_direntry(entry,
2386 &cached_buf[entrypos]) ) {
2388 /* don't return volume id entry */
2389 if ( (entry->attr &
2390 (FAT_ATTR_VOLUME_ID|FAT_ATTR_DIRECTORY))
2391 == FAT_ATTR_VOLUME_ID)
2392 continue;
2394 /* replace shortname with longname? */
2395 if ( longs ) {
2396 int j;
2397 /* This should be enough to hold any name segment
2398 utf8-encoded */
2399 unsigned char shortname[13]; /* 8+3+dot+\0 */
2400 /* Add 1 for trailing \0 */
2401 unsigned char longname_utf8segm[6*4 + 1];
2402 int longname_utf8len = 0;
2403 /* Temporarily store it */
2404 strcpy(shortname, entry->name);
2405 entry->name[0] = 0;
2407 /* iterate backwards through the dir entries */
2408 for (j=longs-1; j>=0; j--) {
2409 unsigned char* ptr = cached_buf;
2410 int index = longarray[j];
2411 /* current or cached sector? */
2412 if ( sectoridx >= SECTOR_SIZE ) {
2413 if ( sectoridx >= SECTOR_SIZE*2 ) {
2414 if ( ( index >= SECTOR_SIZE ) &&
2415 ( index < SECTOR_SIZE*2 ))
2416 ptr = dir->sectorcache[1];
2417 else
2418 ptr = dir->sectorcache[2];
2420 else {
2421 if ( index < SECTOR_SIZE )
2422 ptr = dir->sectorcache[1];
2425 index &= SECTOR_SIZE-1;
2428 /* Try to append each segment of the long name.
2429 Check if we'd exceed the buffer.
2430 Also check for FAT padding characters 0xFFFF. */
2431 if (fat_copy_long_name_segment(ptr + index + 1, 5,
2432 longname_utf8segm) == 0) break;
2433 /* logf("SG: %s, EN: %s", longname_utf8segm,
2434 entry->name); */
2435 longname_utf8len += strlen(longname_utf8segm);
2436 if (longname_utf8len < FAT_FILENAME_BYTES)
2437 strcat(entry->name, longname_utf8segm);
2438 else
2439 break;
2441 if (fat_copy_long_name_segment(ptr + index + 14, 6,
2442 longname_utf8segm) == 0) break;
2443 /* logf("SG: %s, EN: %s", longname_utf8segm,
2444 entry->name); */
2445 longname_utf8len += strlen(longname_utf8segm);
2446 if (longname_utf8len < FAT_FILENAME_BYTES)
2447 strcat(entry->name, longname_utf8segm);
2448 else
2449 break;
2451 if (fat_copy_long_name_segment(ptr + index + 28, 2,
2452 longname_utf8segm) == 0) break;
2453 /* logf("SG: %s, EN: %s", longname_utf8segm,
2454 entry->name); */
2455 longname_utf8len += strlen(longname_utf8segm);
2456 if (longname_utf8len < FAT_FILENAME_BYTES)
2457 strcat(entry->name, longname_utf8segm);
2458 else
2459 break;
2462 /* Does the utf8-encoded name fit into the entry? */
2463 if (longname_utf8len >= FAT_FILENAME_BYTES) {
2464 /* Take the short DOS name. Need to utf8-encode it
2465 since it may contain chars from the upper half of
2466 the OEM code page which wouldn't be a valid utf8.
2467 Beware: this file will be shown with strange
2468 glyphs in file browser since unicode 0x80 to 0x9F
2469 are control characters. */
2470 logf("SN-DOS: %s", shortname);
2471 unsigned char *utf8;
2472 utf8 = iso_decode(shortname, entry->name, -1,
2473 strlen(shortname));
2474 *utf8 = 0;
2475 logf("SN: %s", entry->name);
2476 } else {
2477 /* logf("LN: %s", entry->name);
2478 logf("LNLen: %d (%c)", longname_utf8len,
2479 entry->name[0]); */
2482 done = true;
2483 sectoridx = 0;
2484 i++;
2485 break;
2490 /* save this sector, for longname use */
2491 if ( sectoridx )
2492 memcpy( dir->sectorcache[2], dir->sectorcache[0], SECTOR_SIZE );
2493 else
2494 memcpy( dir->sectorcache[1], dir->sectorcache[0], SECTOR_SIZE );
2495 sectoridx += SECTOR_SIZE;
2498 return 0;
2501 unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume))
2503 #ifndef HAVE_MULTIVOLUME
2504 const int volume = 0;
2505 #endif
2506 struct bpb* fat_bpb = &fat_bpbs[volume];
2507 return fat_bpb->bpb_secperclus * SECTOR_SIZE;
2510 #ifdef HAVE_MULTIVOLUME
2511 bool fat_ismounted(int volume)
2513 return (volume<NUM_VOLUMES && fat_bpbs[volume].mounted);
2515 #endif