- For volumes with unset creation dates, set the ticks field of their
[AROS.git] / rom / filesys / fat / fat.c
blob38c1d4e5cbb1686905b9db7aa4287adf631604b7
1 /*
2 * fat-handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2015 The AROS Development Team
7 * This program is free software; you can redistribute it and/or modify it
8 * under the same terms as AROS itself.
10 * $Id$
13 #include <aros/macros.h>
14 #include <exec/errors.h>
15 #include <exec/types.h>
16 #include <dos/dos.h>
17 #include <dos/dosextens.h>
18 #include <dos/filehandler.h>
20 #include <proto/exec.h>
21 #include <proto/dos.h>
22 #include <proto/utility.h>
23 #include <proto/timer.h>
24 #include <clib/alib_protos.h>
26 #include <clib/macros.h>
28 #include <string.h>
29 #include <ctype.h>
31 #include "fat_fs.h"
32 #include "fat_protos.h"
34 #define DEBUG DEBUG_MISC
35 #include "debug.h"
37 static const UBYTE default_oem_name[] = "MSWIN4.1";
38 static const UBYTE default_filsystype[] = "FAT16 ";
40 static const ULONG fat16_cluster_thresholds[] =
42 8400,
43 32680,
44 262144,
45 524288,
46 1048576,
47 0xFFFFFFFF
50 static const ULONG fat32_cluster_thresholds[] =
52 16777216,
53 33554432,
54 67108864,
55 0xFFFFFFFF
58 /* 01-01-1981 */
59 static const struct DateStamp unset_date_limit =
61 1096,
66 static LONG GetVolumeIdentity(struct FSSuper *sb,
67 struct VolumeIdentity *volume);
69 /* helper function to get the location of a fat entry for a cluster. it used
70 * to be a define until it got too crazy */
71 static UBYTE *GetFatEntryPtr(struct FSSuper *sb, ULONG offset, APTR *rb,
72 UWORD fat_no) {
73 D(struct Globals *glob = sb->glob);
74 ULONG entry_cache_block = offset >> sb->fat_cachesize_bits;
75 ULONG entry_cache_offset = offset & (sb->fat_cachesize - 1);
76 ULONG num;
77 UWORD i;
79 /* if the target cluster is not within the currently loaded chunk of fat,
80 * we need to get the right data in */
81 if (sb->fat_cache_block != entry_cache_block
82 || sb->fat_cache_no != fat_no) {
83 D(bug("[fat] loading %ld FAT sectors starting at sector %ld\n", sb->fat_blocks_count, entry_cache_block << (sb->fat_cachesize_bits - sb->sectorsize_bits)));
84 /* put the old ones back */
85 if (sb->fat_cache_block != 0xffffffff) {
86 for (i = 0; i < sb->fat_blocks_count; i++)
87 Cache_FreeBlock(sb->cache, sb->fat_blocks[i]);
88 sb->fat_cache_block = 0xffffffff;
91 /* load some more */
92 num = sb->first_device_sector + sb->first_fat_sector
93 + sb->fat_size * fat_no + (entry_cache_block
94 << (sb->fat_cachesize_bits - sb->sectorsize_bits));
95 for (i = 0; i < sb->fat_blocks_count; i++) {
96 LONG ioerr;
98 sb->fat_blocks[i] =
99 Cache_GetBlock(sb->cache, num + i, &sb->fat_buffers[i], &ioerr);
101 /* FIXME: Handle IO errors on cache read! */
102 if (sb->fat_blocks[i] == NULL) {
103 while (i-- != 0)
104 Cache_FreeBlock(sb->cache, sb->fat_blocks[i]);
105 return NULL;
109 /* remember where we are for next time */
110 sb->fat_cache_block = entry_cache_block;
111 sb->fat_cache_no = fat_no;
114 /* give the block back if they asked for it (needed to mark the block
115 * dirty if they're writing) */
116 if (rb != NULL)
117 *rb = sb->fat_blocks[entry_cache_offset >> sb->sectorsize_bits];
119 /* compute the pointer location and return it */
120 return sb->fat_buffers[entry_cache_offset >> sb->sectorsize_bits] +
121 (entry_cache_offset & (sb->sectorsize - 1));
124 /* FAT12 has, as the name suggests, 12-bit FAT entries. This means that two
125 * entries are condensed into three bytes, like so:
127 * entry: aaaaaaaa aaaabbbb bbbbbbbb
128 * bytes: xxxxxxxx xxxxxxxx xxxxxxxx
130 * To get at the entry we want, we find and grab the word starting at either
131 * byte 0 or 1 of the three-byte set, then shift up or down as needed. FATdoc
132 * 1.03 p16-17 describes the method
134 * The only tricky bit is if the word falls such that the first byte is the
135 * last byte of the block and the second byte is the first byte of the next
136 * block. Since our block data are stored within cache block structures, a
137 * simple cast won't do (hell, the second block may not even be in memory if
138 * we're at the end of the FAT cache). So we get it a byte at a time, and
139 * build the word ourselves.
141 static ULONG GetFat12Entry(struct FSSuper *sb, ULONG n) {
142 D(struct Globals *glob = sb->glob);
143 ULONG offset = n + n/2;
144 UWORD val;
146 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
147 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
149 val = *GetFatEntryPtr(sb, offset + 1, NULL, 0) << 8;
150 val |= *GetFatEntryPtr(sb, offset, NULL, 0);
152 else
153 val = AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, offset, NULL, 0)));
155 if (n & 1)
156 val >>= 4;
157 else
158 val &= 0xfff;
160 return val;
164 * FAT16 and FAT32, on the other hand, have nice neat entry widths, so simple
165 * word/long casts are fine. There's also no chance that the entry can be
166 * split across blocks. Why can't everything be this simple?
168 static ULONG GetFat16Entry(struct FSSuper *sb, ULONG n) {
169 return AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, n << 1, NULL, 0)));
172 static ULONG GetFat32Entry(struct FSSuper *sb, ULONG n) {
173 return AROS_LE2LONG(*((ULONG *) GetFatEntryPtr(sb, n << 2, NULL, 0)))
174 & 0x0fffffff;
177 static void SetFat12Entry(struct FSSuper *sb, ULONG n, ULONG val) {
178 D(struct Globals *glob = sb->glob);
179 APTR b;
180 ULONG offset = n + n/2;
181 BOOL boundary = FALSE;
182 UWORD *fat = NULL, newval, i;
184 for (i = 0; i < sb->fat_count; i++)
186 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
187 boundary = TRUE;
189 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
191 newval = *GetFatEntryPtr(sb, offset + 1, NULL, i) << 8;
192 newval |= *GetFatEntryPtr(sb, offset, NULL, i);
194 else {
195 fat = (UWORD *) GetFatEntryPtr(sb, offset, &b, i);
196 newval = AROS_LE2WORD(*fat);
199 if (n & 1) {
200 val <<= 4;
201 newval = (newval & 0xf) | val;
203 else {
204 newval = (newval & 0xf000) | val;
207 if (boundary) {
208 /* XXX ideally we'd mark both blocks dirty at the same time or
209 * only do it once if they're the same block. unfortunately any
210 * old value of b is invalid after a call to GetFatEntryPtr, as
211 * it may have swapped the previous cache out. This is probably
212 * safe enough. */
213 *GetFatEntryPtr(sb, offset+1, &b, i) = newval >> 8;
214 Cache_MarkBlockDirty(sb->cache, b);
215 *GetFatEntryPtr(sb, offset, &b, i) = newval & 0xff;
216 Cache_MarkBlockDirty(sb->cache, b);
218 else {
219 *fat = AROS_WORD2LE(newval);
220 Cache_MarkBlockDirty(sb->cache, b);
225 static void SetFat16Entry(struct FSSuper *sb, ULONG n, ULONG val) {
226 APTR b;
227 UWORD i;
229 for (i = 0; i < sb->fat_count; i++)
231 *((UWORD *) GetFatEntryPtr(sb, n << 1, &b, i)) =
232 AROS_WORD2LE((UWORD) val);
233 Cache_MarkBlockDirty(sb->cache, b);
237 static void SetFat32Entry(struct FSSuper *sb, ULONG n, ULONG val) {
238 APTR b;
239 ULONG *fat;
240 UWORD i;
242 for (i = 0; i < sb->fat_count; i++)
244 fat = (ULONG *) GetFatEntryPtr(sb, n << 2, &b, i);
246 *fat = (*fat & 0xf0000000) | val;
248 Cache_MarkBlockDirty(sb->cache, b);
252 LONG ReadFATSuper(struct FSSuper *sb ) {
253 struct Globals *glob = sb->glob;
254 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
255 LONG err;
256 ULONG bsize = de->de_SizeBlock * 4, total_sectors, id;
257 struct FATBootSector *boot;
258 struct FATEBPB *ebpb;
259 struct FATFSInfo *fsinfo;
260 BOOL invalid = FALSE;
261 ULONG end;
262 LONG i;
263 struct DirHandle dh;
264 struct DirEntry dir_entry;
265 APTR block_ref;
266 UBYTE *fat_block;
268 D(bug("[fat] reading boot sector\n"));
270 boot = AllocMem(bsize, MEMF_ANY);
271 if (!boot)
272 return ERROR_NO_FREE_STORE;
274 sb->first_device_sector =
275 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
277 /* Get a preliminary total-sectors value so we don't risk going outside
278 * partition limits */
279 sb->total_sectors =
280 de->de_BlocksPerTrack * de->de_Surfaces * (de->de_HighCyl + 1)
281 - sb->first_device_sector;
283 D(bug("[fat] boot sector at sector %ld\n", sb->first_device_sector));
286 * Read the boot sector. We go direct because we don't have a cache yet,
287 * and can't create one until we know the sector size, which is held in
288 * the boot sector. In practice it doesn't matter - we're going to use
289 * this once and once only.
291 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize, (UBYTE *)boot, glob)) != 0) {
292 D(bug("[fat] couldn't read boot block (%ld)\n", err));
293 FreeMem(boot, bsize);
294 return err;
297 D(bug("\tBoot sector:\n"));
299 sb->sectorsize = AROS_LE2WORD(boot->bpb_bytes_per_sect);
300 sb->sectorsize_bits = log2(sb->sectorsize);
301 D(bug("\tSectorSize = %ld\n", sb->sectorsize));
302 D(bug("\tSectorSize Bits = %ld\n", sb->sectorsize_bits));
304 sb->cluster_sectors = boot->bpb_sect_per_clust;
305 sb->clustersize = sb->sectorsize * boot->bpb_sect_per_clust;
306 sb->clustersize_bits = log2(sb->clustersize);
307 sb->cluster_sectors_bits = sb->clustersize_bits - sb->sectorsize_bits;
309 D(bug("\tSectorsPerCluster = %ld\n", (ULONG)boot->bpb_sect_per_clust));
310 D(bug("\tClusterSize = %ld\n", sb->clustersize));
311 D(bug("\tClusterSize Bits = %ld\n", sb->clustersize_bits));
312 D(bug("\tCluster Sectors Bits = %ld\n", sb->cluster_sectors_bits));
314 sb->first_fat_sector = AROS_LE2WORD(boot->bpb_rsvd_sect_count);
315 D(bug("\tFirst FAT Sector = %ld\n", sb->first_fat_sector));
317 sb->fat_count = boot->bpb_num_fats;
318 D(bug("\tNumber of FATs = %d\n", sb->fat_count));
320 if (boot->bpb_fat_size_16 != 0)
321 sb->fat_size = AROS_LE2WORD(boot->bpb_fat_size_16);
322 else
323 sb->fat_size = AROS_LE2LONG(boot->ebpbs.ebpb32.bpb_fat_size_32);
324 D(bug("\tFAT Size = %ld\n", sb->fat_size));
326 if (boot->bpb_total_sectors_16 != 0)
327 total_sectors = AROS_LE2WORD(boot->bpb_total_sectors_16);
328 else
329 total_sectors = AROS_LE2LONG(boot->bpb_total_sectors_32);
330 D(bug("\tTotal Sectors = %ld\n", sb->total_sectors));
332 /* Check that the boot block's sector count is the same as the
333 * partition's sector count. This stops a resized partition being
334 * mounted before reformatting */
335 if (total_sectors != sb->total_sectors)
336 invalid = TRUE;
338 sb->rootdir_sectors = ((AROS_LE2WORD(boot->bpb_root_entries_count) * sizeof(struct FATDirEntry)) + (sb->sectorsize - 1)) >> sb->sectorsize_bits;
339 D(bug("\tRootDir Sectors = %ld\n", sb->rootdir_sectors));
341 sb->data_sectors = sb->total_sectors - (sb->first_fat_sector + (sb->fat_count * sb->fat_size) + sb->rootdir_sectors);
342 D(bug("\tData Sectors = %ld\n", sb->data_sectors));
344 sb->clusters_count = sb->data_sectors >> sb->cluster_sectors_bits;
345 D(bug("\tClusters Count = %ld\n", sb->clusters_count));
347 sb->first_rootdir_sector = sb->first_fat_sector + (sb->fat_count * sb->fat_size);
348 D(bug("\tFirst RootDir Sector = %ld\n", sb->first_rootdir_sector));
350 sb->first_data_sector = sb->first_fat_sector + (sb->fat_count * sb->fat_size) + sb->rootdir_sectors;
351 D(bug("\tFirst Data Sector = %ld\n", sb->first_data_sector));
353 /* check if disk is in fact a FAT filesystem */
355 /* valid sector size: 512, 1024, 2048, 4096 */
356 if (sb->sectorsize != 512 && sb->sectorsize != 1024 && sb->sectorsize != 2048 && sb->sectorsize != 4096)
357 invalid = TRUE;
359 /* valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
360 if ((boot->bpb_sect_per_clust & (boot->bpb_sect_per_clust - 1)) != 0 || boot->bpb_sect_per_clust == 0 || boot->bpb_sect_per_clust > 128)
361 invalid = TRUE;
363 /* valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
364 if (sb->clustersize > 64 * 1024)
365 invalid = TRUE;
367 if (sb->first_fat_sector == 0)
368 invalid = TRUE;
370 if (sb->fat_count == 0)
371 invalid = TRUE;
373 if (boot->bpb_media < 0xF0)
374 invalid = TRUE;
376 /* FAT "signature" */
377 if (boot->bpb_signature[0] != 0x55 || boot->bpb_signature[1] != 0xaa)
378 invalid = TRUE;
380 if (invalid) {
381 D(bug("\tInvalid FAT Boot Sector\n"));
382 FreeMem(boot, bsize);
383 return ERROR_NOT_A_DOS_DISK;
385 end = 0xFFFFFFFF / sb->sectorsize;
386 if ((sb->first_device_sector + sb->total_sectors - 1 > end) && (glob->readcmd == CMD_READ)) {
387 D(bug("\tDevice is too large\n"));
388 FreeMem(boot, bsize);
389 return IOERR_BADADDRESS;
392 sb->cache = Cache_CreateCache(glob, 64, 64, sb->sectorsize, SysBase);
394 if (sb->clusters_count < 4085) {
395 D(bug("\tFAT12 filesystem detected\n"));
396 sb->type = 12;
397 sb->eoc_mark = 0x0FFF;
398 sb->func_get_fat_entry = GetFat12Entry;
399 sb->func_set_fat_entry = SetFat12Entry;
401 else if (sb->clusters_count < 65525) {
402 D(bug("\tFAT16 filesystem detected\n"));
403 sb->type = 16;
404 sb->eoc_mark = 0xFFFF;
405 sb->func_get_fat_entry = GetFat16Entry;
406 sb->func_set_fat_entry = SetFat16Entry;
408 else {
409 D(bug("\tFAT32 filesystem detected\n"));
410 sb->type = 32;
411 sb->eoc_mark = 0x0FFFFFFF;
412 sb->func_get_fat_entry = GetFat32Entry;
413 sb->func_set_fat_entry = SetFat32Entry;
415 glob->sb = sb;
417 /* setup the FAT cache and load the first blocks */
418 sb->fat_cachesize = 4096;
419 sb->fat_cachesize_bits = log2(sb->fat_cachesize);
420 sb->fat_cache_block = 0xffffffff;
422 sb->fat_blocks_count =
423 MIN(sb->fat_size, sb->fat_cachesize >> sb->sectorsize_bits);
424 sb->fat_blocks = AllocVecPooled(glob->mempool,
425 sizeof(APTR) * sb->fat_blocks_count);
426 sb->fat_buffers = AllocVecPooled(glob->mempool,
427 sizeof(APTR) * sb->fat_blocks_count);
429 if (sb->type != 32) { /* FAT 12/16 */
430 /* setup volume id */
431 sb->volume_id = AROS_LE2LONG(boot->ebpbs.ebpb.bs_volid);
433 /* location of root directory */
434 sb->rootdir_cluster = 0;
435 sb->rootdir_sector = sb->first_rootdir_sector;
436 ebpb = &boot->ebpbs.ebpb;
438 else {
439 /* setup volume id */
440 sb->volume_id = AROS_LE2LONG(boot->ebpbs.ebpb32.ebpb.bs_volid);
442 /* location of root directory */
443 sb->rootdir_cluster = AROS_LE2LONG(boot->ebpbs.ebpb32.bpb_root_cluster);
444 sb->rootdir_sector = 0;
445 ebpb = &boot->ebpbs.ebpb32.ebpb;
448 D(bug("[fat] rootdir at cluster %ld sector %ld\n", sb->rootdir_cluster, sb->rootdir_sector));
450 /* Initialise the root directory if this is a newly formatted volume */
451 if (glob->formatting)
453 /* Clear all FAT sectors */
454 for (i = 0; i < sb->fat_size * 2; i++) {
455 LONG ioerr;
456 block_ref = Cache_GetBlock(sb->cache,
457 sb->first_device_sector + sb->first_fat_sector + i,
458 &fat_block, &ioerr);
459 /* FIXME: Handle IO errors on cache read! */
460 memset(fat_block, 0, bsize);
461 if (i == 0) {
462 /* The first two entries are special */
463 if (sb->type == 32)
464 *(UQUAD *)fat_block = AROS_QUAD2LE(0x0FFFFFFF0FFFFFF8);
465 else if (sb->type == 16)
466 *(ULONG *)fat_block = AROS_LONG2LE(0xFFFFFFF8);
467 else
468 *(ULONG *)fat_block = AROS_LONG2LE(0x00FFFFF8);
470 Cache_MarkBlockDirty(sb->cache, block_ref);
471 Cache_FreeBlock(sb->cache, block_ref);
474 /* allocate first cluster of the root directory */
475 if (sb->type == 32)
476 AllocCluster(sb, sb->rootdir_cluster);
478 /* get a handle on the root directory */
479 InitDirHandle(sb, 0, &dh, FALSE);
481 /* clear all entries */
482 for (i = 0; GetDirEntry(&dh, i, &dir_entry) == 0; i++) {
483 memset(&dir_entry.e.entry, 0, sizeof(struct FATDirEntry));
484 UpdateDirEntry(&dir_entry);
487 SetVolumeName(sb, ebpb->bs_vollab, 11);
489 ReleaseDirHandle(&dh);
490 glob->formatting = FALSE;
491 D(bug("\tRoot dir created.\n"));
494 if (GetVolumeIdentity(sb, &(sb->volume)) != 0) {
495 LONG i;
496 UBYTE *uu = (void *)&sb->volume_id;
498 /* No volume name entry, so construct name from serial number */
499 for (i=1; i<10;) {
500 int d;
502 if (i==5)
503 sb->volume.name[i++]='-';
505 d = (*uu) & 0x0f;
506 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
507 d = ((*uu) & 0xf0)>>4;
508 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
510 uu++;
513 sb->volume.name[i] = '\0';
514 sb->volume.name[0] = 9;
517 /* Many FAT volumes do not have a creation date set, with the result
518 * that two volumes with the same name are likely to be indistinguishable
519 * on the DOS list. To work around this problem, we set the ds_Tick field
520 * of such volumes' dol_VolumeDate timestamp to a pseudo-random value based
521 * on the serial number. Since there are 3000 ticks in a minute, we use an
522 * 11-bit hash value in the range 0 to 2047.
524 if (CompareDates(&sb->volume.create_time, &unset_date_limit) > 0) {
525 id = sb->volume_id;
526 sb->volume.create_time.ds_Days = 0;
527 sb->volume.create_time.ds_Minute = 0;
528 sb->volume.create_time.ds_Tick = (id >> 22 ^ id >> 11 ^ id) & 0x7FF;
529 D(bug("[FAT] Set hash time to %ld ticks\n",
530 sb->volume.create_time.ds_Tick));
533 /* get initial number of free clusters */
534 sb->free_clusters = -1;
535 sb->next_cluster = -1;
536 if (sb->type == 32) {
537 LONG ioerr;
538 sb->fsinfo_block = Cache_GetBlock(sb->cache, sb->first_device_sector
539 + AROS_LE2WORD(boot->ebpbs.ebpb32.bpb_fs_info), (UBYTE **)&fsinfo,
540 &ioerr);
541 if (sb->fsinfo_block != NULL) {
542 if (fsinfo->lead_sig == AROS_LONG2LE(FSI_LEAD_SIG)
543 && fsinfo->struct_sig == AROS_LONG2LE(FSI_STRUCT_SIG)
544 && fsinfo->trail_sig == AROS_LONG2LE(FSI_TRAIL_SIG)) {
545 sb->free_clusters = AROS_LE2LONG(fsinfo->free_count);
546 sb->next_cluster = AROS_LE2LONG(fsinfo->next_free);
547 D(bug("[fat] valid FATFSInfo block found\n"));
548 sb->fsinfo_buffer = fsinfo;
550 else
551 Cache_FreeBlock(sb->cache, sb->fsinfo_block);
552 } else {
553 /* FIXME: Report IO errors to the user! */
556 if (sb->free_clusters == -1)
557 CountFreeClusters(sb);
558 if (sb->next_cluster == -1)
559 sb->next_cluster = 2;
561 D(bug("\tFAT Filesystem successfully detected.\n"));
562 D(bug("\tFree Clusters = %ld\n", sb->free_clusters));
563 D(bug("\tNext Free Cluster = %ld\n", sb->next_cluster));
564 FreeMem(boot, bsize);
565 return 0;
568 static LONG GetVolumeIdentity(struct FSSuper *sb,
569 struct VolumeIdentity *volume) {
570 struct Globals *glob = sb->glob;
571 struct DirHandle dh;
572 struct DirEntry de;
573 LONG err;
574 int i;
576 D(bug("[fat] searching root directory for volume name\n"));
578 /* search the directory for the volume id entry. it would've been nice to
579 * just use GetNextDirEntry but I didn't want a flag or something to tell
580 * it not to skip the volume name */
581 InitDirHandle(sb, sb->rootdir_cluster, &dh, FALSE);
583 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
585 /* match the volume id entry */
586 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
587 && de.e.entry.name[0] != 0xe5) {
588 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
590 /* copy the name in. volume->name is a BSTR */
592 volume->name[1] = de.e.entry.name[0];
594 for (i = 1; i < 11; i++) {
595 if (volume->name[i] == ' ')
596 volume->name[i+1] = de.e.entry.name[i];
597 else
598 volume->name[i+1] = tolower(de.e.entry.name[i]);
601 for (i = 10; volume->name[i+1] == ' '; i--);
602 volume->name[i+2] = '\0';
603 volume->name[0] = strlen(&(volume->name[1]));
605 /* get the volume creation date too */
606 ConvertFATDate(de.e.entry.create_date, de.e.entry.create_time, &volume->create_time, glob);
608 D(bug("[fat] volume name is '%s'\n", &(volume->name[1])));
610 break;
613 /* bail out if we hit the end of the dir */
614 if (de.e.entry.name[0] == 0x00) {
615 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
616 err = ERROR_OBJECT_NOT_FOUND;
617 break;
621 ReleaseDirHandle(&dh);
622 return err;
625 LONG FormatFATVolume(const UBYTE *name, UWORD len, struct Globals *glob) {
626 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
627 LONG err;
628 ULONG bsize = de->de_SizeBlock * 4;
629 struct FATBootSector *boot;
630 struct FATEBPB *ebpb;
631 struct FATFSInfo *fsinfo;
632 UWORD type, i, root_entries_count;
633 struct EClockVal eclock;
634 ULONG sectors_per_cluster = 0, sector_count, first_fat_sector,
635 fat_size, root_dir_sectors, first_device_sector, temp1, temp2;
637 /* Decide on FAT type based on number of sectors */
638 sector_count = (de->de_HighCyl - de->de_LowCyl + 1)
639 * de->de_Surfaces * de->de_BlocksPerTrack;
640 if (sector_count < 4085)
641 type = 12;
642 else if (sector_count < 1024 * 1024)
643 type = 16;
644 else
645 type = 32;
647 D(bug("[fat] writing boot sector\n"));
649 /* Decide on cluster size and root dir entries */
650 first_fat_sector = 1;
651 if (type == 12) {
652 if (sector_count == 1440) {
653 sectors_per_cluster = 2;
654 root_entries_count = 112;
655 } else if (sector_count == 2880) {
656 sectors_per_cluster = 1;
657 root_entries_count = 224;
658 } else if (sector_count == 5760) {
659 sectors_per_cluster = 2;
660 root_entries_count = 240;
661 } else {
662 /* We only support some common 3.5" floppy formats */
663 return ERROR_NOT_IMPLEMENTED;
665 } else if (type == 16) {
666 for (i = 0; fat16_cluster_thresholds[i] < sector_count; i++);
667 sectors_per_cluster = 1 << i;
668 root_entries_count = 512;
669 } else {
670 for (i = 0; fat32_cluster_thresholds[i] < sector_count; i++);
671 sectors_per_cluster = 8 << i;
672 root_entries_count = 0;
673 first_fat_sector = 32;
676 D(bug("\tFirst FAT Sector = %ld\n", first_fat_sector));
678 /* Determine FAT size */
679 root_dir_sectors = (root_entries_count * 32 + (bsize - 1)) / bsize;
680 temp1 = sector_count - (first_fat_sector + root_dir_sectors);
681 temp2 = 256 * sectors_per_cluster + 2;
682 if (type == 32)
683 temp2 /= 2;
684 fat_size = (temp1 + temp2 - 1) / temp2;
686 boot = AllocMem(bsize, MEMF_CLEAR);
687 if (!boot)
688 return ERROR_NO_FREE_STORE;
690 /* Install x86 infinite loop boot code to keep major OSes happy */
691 boot->bs_jmp_boot[0] = 0xEB;
692 boot->bs_jmp_boot[1] = 0xFE;
693 boot->bs_jmp_boot[2] = 0x90;
695 CopyMem(default_oem_name, boot->bs_oem_name, 8);
697 boot->bpb_bytes_per_sect = AROS_WORD2LE(bsize);
698 boot->bpb_sect_per_clust = sectors_per_cluster;
700 boot->bpb_rsvd_sect_count = AROS_WORD2LE(first_fat_sector);
702 boot->bpb_num_fats = 2;
704 boot->bpb_root_entries_count = AROS_WORD2LE(root_entries_count);
706 if (sector_count < 0x10000 && type != 32)
707 boot->bpb_total_sectors_16 = AROS_WORD2LE(sector_count);
708 else
709 boot->bpb_total_sectors_32 = AROS_LONG2LE(sector_count);
711 boot->bpb_media = 0xF8;
713 boot->bpb_sect_per_track = AROS_WORD2LE(de->de_BlocksPerTrack);
714 boot->bpb_num_heads = AROS_WORD2LE(de->de_Surfaces);
715 boot->bpb_hidden_sect = AROS_LONG2LE(de->de_Reserved);
717 if (type == 32) {
718 boot->ebpbs.ebpb32.bpb_fat_size_32 = AROS_LONG2LE(fat_size);
719 boot->ebpbs.ebpb32.bpb_root_cluster = AROS_LONG2LE(2);
720 boot->ebpbs.ebpb32.bpb_fs_info = AROS_WORD2LE(1);
721 boot->ebpbs.ebpb32.bpb_back_bootsec = AROS_WORD2LE(6);
722 ebpb = &boot->ebpbs.ebpb32.ebpb;
724 else {
725 boot->bpb_fat_size_16 = AROS_WORD2LE(fat_size);
726 ebpb = &boot->ebpbs.ebpb;
729 ebpb->bs_drvnum = 0x80;
730 ebpb->bs_bootsig = 0x29;
732 /* Generate a pseudo-random serial number. Not the original algorithm,
733 * but it shouldn't matter */
734 ReadEClock(&eclock);
735 ebpb->bs_volid = FastRand(eclock.ev_lo ^ eclock.ev_hi);
737 /* copy volume name in */
738 for (i = 0; i < 11; i++)
739 if (i < len)
740 ebpb->bs_vollab[i] = toupper(name[i]);
741 else
742 ebpb->bs_vollab[i] = ' ';
744 CopyMem(default_filsystype, ebpb->bs_filsystype, 8);
745 if (type != 16) {
746 if (type == 32)
747 ebpb->bs_filsystype[3] = '3';
748 ebpb->bs_filsystype[4] = '2';
751 boot->bpb_signature[0] = 0x55;
752 boot->bpb_signature[1] = 0xaa;
754 /* Write the boot sector */
755 first_device_sector =
756 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
758 D(bug("[fat] boot sector at sector %ld\n", first_device_sector));
760 if ((err = AccessDisk(TRUE, first_device_sector, 1, bsize, (UBYTE *)boot, glob)) != 0) {
761 D(bug("[fat] couldn't write boot block (%ld)\n", err));
762 FreeMem(boot, bsize);
763 return err;
766 /* Write back-up boot sector and FS info sector */
767 if (type == 32) {
768 if ((err = AccessDisk(TRUE, first_device_sector + 6, 1, bsize,
769 (UBYTE *)boot, glob)) != 0) {
770 D(bug("[fat] couldn't write back-up boot block (%ld)\n", err));
771 FreeMem(boot, bsize);
772 return err;
775 fsinfo = (APTR)boot;
776 memset(fsinfo, 0, bsize);
778 fsinfo->lead_sig = AROS_LONG2LE(FSI_LEAD_SIG);
779 fsinfo->struct_sig = AROS_LONG2LE(FSI_STRUCT_SIG);
780 fsinfo->trail_sig = AROS_LONG2LE(FSI_TRAIL_SIG);
781 fsinfo->free_count = AROS_LONG2LE(0xFFFFFFFF);
782 fsinfo->next_free = AROS_LONG2LE(0xFFFFFFFF);
784 if ((err = AccessDisk(TRUE, first_device_sector + 1, 1, bsize,
785 (UBYTE *)fsinfo, glob)) != 0) {
786 D(bug("[fat] couldn't write back-up boot block (%ld)\n", err));
787 FreeMem(boot, bsize);
788 return err;
792 FreeMem(boot, bsize);
794 glob->formatting = TRUE;
796 return 0;
799 LONG SetVolumeName(struct FSSuper *sb, UBYTE *name, UWORD len) {
800 struct Globals *glob = sb->glob;
801 struct DirHandle dh;
802 struct DirEntry de;
803 LONG err;
804 int i;
805 struct DosEnvec *dosenv = BADDR(glob->fssm->fssm_Environ);
806 ULONG bsize = dosenv->de_SizeBlock * 4;
807 struct FATBootSector *boot;
809 /* truncate name if necessary */
810 if (len > FAT_MAX_SHORT_NAME)
811 len = FAT_MAX_SHORT_NAME;
813 /* read boot block */
814 boot = AllocMem(bsize, MEMF_ANY);
815 if (!boot)
816 return ERROR_NO_FREE_STORE;
818 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize, (UBYTE *)boot, glob)) != 0) {
819 D(bug("[fat] couldn't read boot block (%ld)\n", err));
820 FreeMem(boot, bsize);
821 return err;
824 D(bug("[fat] searching root directory for volume name\n"));
826 /* search the directory for the volume id entry. it would've been nice to
827 * just use GetNextDirEntry but I didn't want a flag or something to tell
828 * it not to skip the volume name */
829 InitDirHandle(sb, 0, &dh, FALSE);
831 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
833 /* match the volume id entry */
834 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
835 && de.e.entry.name[0] != 0xe5) {
836 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
837 err = 0;
838 break;
841 /* bail out if we hit the end of the dir */
842 if (de.e.entry.name[0] == 0x00) {
843 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
844 err = ERROR_OBJECT_NOT_FOUND;
845 break;
849 /* create a new volume id entry if there wasn't one */
850 if (err != 0) {
851 err = AllocDirEntry(&dh, 0, &de);
852 if (err == 0)
853 FillDirEntry(&de, ATTR_VOLUME_ID, 0);
856 /* copy the name in */
857 if (err == 0) {
858 for (i = 0; i < FAT_MAX_SHORT_NAME; i++)
859 if (i < len)
860 de.e.entry.name[i] = toupper(name[i]);
861 else
862 de.e.entry.name[i] = ' ';
864 if ((err = UpdateDirEntry(&de)) != 0) {
865 D(bug("[fat] couldn't change volume name\n"));
866 return err;
870 /* copy name to boot block as well, and save */
871 if (sb->type == 32)
872 CopyMem(de.e.entry.name, boot->ebpbs.ebpb32.ebpb.bs_vollab,
873 FAT_MAX_SHORT_NAME);
874 else
875 CopyMem(de.e.entry.name, boot->ebpbs.ebpb.bs_vollab,
876 FAT_MAX_SHORT_NAME);
878 if ((err = AccessDisk(TRUE, sb->first_device_sector, 1, bsize,
879 (UBYTE *)boot, glob)) != 0)
880 D(bug("[fat] couldn't write boot block (%ld)\n", err));
881 FreeMem(boot, bsize);
883 /* update name in sb */
884 sb->volume.name[0] = len;
885 sb->volume.name[1] = toupper(name[0]);
886 for (i = 1; i < len; i++)
887 sb->volume.name[i + 1] = tolower(name[i]);
888 sb->volume.name[len + 1] = '\0';
890 D(bug("[fat] new volume name is '%s'\n", &(sb->volume.name[1])));
892 ReleaseDirHandle(&dh);
893 return err;
896 LONG FindFreeCluster(struct FSSuper *sb, ULONG *rcluster) {
897 D(struct Globals *glob = sb->glob);
898 ULONG cluster = 0;
899 BOOL found = FALSE;
901 for (cluster = sb->next_cluster;
902 cluster < 2 + sb->clusters_count && !found;
903 cluster++)
905 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
907 *rcluster = cluster;
908 found = TRUE;
912 if (!found)
914 for (cluster = 2; cluster < sb->next_cluster && !found;
915 cluster++)
917 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
919 *rcluster = cluster;
920 found = TRUE;
925 if (!found) {
926 D(bug("[fat] no more free clusters, we're out of space\n"));
927 return ERROR_DISK_FULL;
930 sb->next_cluster = *rcluster;
932 D(bug("[fat] found free cluster %ld\n", *rcluster));
934 return 0;
937 void FreeFATSuper(struct FSSuper *sb) {
938 struct Globals *glob = sb->glob;
939 D(bug("\tRemoving Super Block from memory\n"));
940 Cache_DestroyCache(sb->cache);
941 FreeVecPooled(glob->mempool, sb->fat_buffers);
942 sb->fat_buffers = NULL;
943 FreeVecPooled(glob->mempool, sb->fat_blocks);
944 sb->fat_blocks = NULL;
947 /* see how many unused clusters are available */
948 void CountFreeClusters(struct FSSuper *sb) {
949 D(struct Globals *glob = sb->glob);
950 ULONG cluster = 0;
951 ULONG free = 0;
953 /* loop over all the data clusters */
954 for (cluster = 2; cluster < sb->clusters_count + 2; cluster++)
956 /* record the free ones */
957 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
958 free++;
961 /* put the value away for later */
962 sb->free_clusters = free;
964 D(bug("\tfree clusters: %ld\n", free));
967 void AllocCluster(struct FSSuper *sb, ULONG cluster) {
968 SET_NEXT_CLUSTER(sb, cluster, sb->eoc_mark);
969 sb->free_clusters--;
970 if (sb->fsinfo_buffer != NULL) {
971 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
972 sb->fsinfo_buffer->next_free = AROS_LONG2LE(sb->next_cluster);
973 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
977 void FreeCluster(struct FSSuper *sb, ULONG cluster) {
978 SET_NEXT_CLUSTER(sb, cluster, 0);
979 sb->free_clusters++;
980 if (sb->fsinfo_buffer != NULL) {
981 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
982 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
986 void ConvertFATDate(UWORD date, UWORD time, struct DateStamp *ds, struct Globals *glob) {
987 ULONG year, month, day, hours, mins, secs;
988 struct ClockData clock_data;
990 /* date bits: yyyy yyym mmmd dddd */
991 year = (date & 0xfe00) >> 9; /* bits 15-9 */
992 month = (date & 0x01e0) >> 5; /* bits 8-5 */
993 day = date & 0x001f; /* bits 4-0 */
995 /* time bits: hhhh hmmm mmms ssss */
996 hours = (time & 0xf800) >> 11; /* bits 15-11 */
997 mins = (time & 0x07e0) >> 5; /* bits 10-5 */
998 secs = time & 0x001f; /* bits 4-0 */
1000 D(bug("[fat] converting fat date: year %d month %d day %d hours %d mins %d secs %d\n", year, month, day, hours, mins, secs));
1002 if (month < 1 || month > 12 || day < 1 || day > 31 || hours > 23 ||
1003 mins > 59 || secs > 29) {
1004 D(bug("[fat] invalid fat date: using 01-01-1978 instead\n"));
1005 secs = 0;
1006 } else {
1007 clock_data.year = 1980 + year;
1008 clock_data.month = month;
1009 clock_data.mday = day;
1010 clock_data.hour = hours;
1011 clock_data.min = mins;
1012 clock_data.sec = secs << 1;
1013 secs = Date2Amiga(&clock_data);
1016 /* calculate days since 1978-01-01 (DOS epoch) */
1017 ds->ds_Days = secs / (60 * 60 * 24);
1019 /* minutes since midnight */
1020 ds->ds_Minute = secs / 60 % (24 * 60);
1022 /* 1/50 sec ticks since last minute */
1023 ds->ds_Tick = secs % 60 * TICKS_PER_SECOND;
1025 D(bug("[fat] converted fat date: days %ld minutes %ld ticks %ld\n", ds->ds_Days, ds->ds_Minute, ds->ds_Tick));
1028 void ConvertDOSDate(struct DateStamp *ds, UWORD *date, UWORD *time, struct Globals *glob) {
1029 ULONG secs;
1031 /* convert datestamp to seconds since 1978 */
1032 secs = ds->ds_Days * 60 * 60 * 24 + ds->ds_Minute * 60
1033 + ds->ds_Tick / TICKS_PER_SECOND;
1035 ConvertSysDate(secs, date, time, glob);
1038 void ConvertSysDate(ULONG secs, UWORD *date, UWORD *time, struct Globals *glob) {
1039 ULONG year, month, day, hours, mins;
1040 struct ClockData clock_data;
1042 /* Round up to next even second because of FAT's two-second granularity */
1043 secs = (secs & ~1) + 2;
1045 /* convert seconds since 1978 to calendar/time data */
1046 Amiga2Date(secs, &clock_data);
1048 /* get values used in FAT dates */
1049 year = clock_data.year - 1980;
1050 month = clock_data.month - 0;
1051 day = clock_data.mday;
1052 hours = clock_data.hour;
1053 mins = clock_data.min;
1054 secs = clock_data.sec >> 1;
1056 /* all that remains is to bit-encode the whole lot */
1058 /* date bits: yyyy yyym mmmd dddd */
1059 *date = (((ULONG) year) << 9) | (((ULONG) month) << 5) | day;
1061 /* time bits: hhhh hmmm mmms ssss */
1062 *time = (((ULONG) hours) << 11) | (((ULONG) mins) << 5) | secs;