Made comments and debug output more consistent.
[AROS.git] / rom / filesys / fat / fat.c
blob9355f12090c66c709fc7ddb3f157818ca0008b24
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)
74 D(struct Globals *glob = sb->glob);
75 ULONG entry_cache_block = offset >> sb->fat_cachesize_bits;
76 ULONG entry_cache_offset = offset & (sb->fat_cachesize - 1);
77 ULONG num;
78 UWORD i;
80 /* If the target cluster is not within the currently loaded chunk of fat,
81 * we need to get the right data in */
82 if (sb->fat_cache_block != entry_cache_block
83 || sb->fat_cache_no != fat_no)
85 D(bug("[fat] loading %ld FAT sectors starting at sector %ld\n",
86 sb->fat_blocks_count,
87 entry_cache_block << (sb->fat_cachesize_bits -
88 sb->sectorsize_bits)));
90 /* Put the old ones back */
91 if (sb->fat_cache_block != 0xffffffff)
93 for (i = 0; i < sb->fat_blocks_count; i++)
94 Cache_FreeBlock(sb->cache, sb->fat_blocks[i]);
95 sb->fat_cache_block = 0xffffffff;
98 /* Load some more */
99 num = sb->first_device_sector + sb->first_fat_sector
100 + sb->fat_size * fat_no + (entry_cache_block
101 << (sb->fat_cachesize_bits - sb->sectorsize_bits));
102 for (i = 0; i < sb->fat_blocks_count; i++)
104 sb->fat_blocks[i] =
105 Cache_GetBlock(sb->cache, num + i, &sb->fat_buffers[i]);
107 /* FIXME: Handle IO errors on cache read! */
108 if (sb->fat_blocks[i] == NULL)
110 while (i-- != 0)
111 Cache_FreeBlock(sb->cache, sb->fat_blocks[i]);
112 return NULL;
116 /* Remember where we are for next time */
117 sb->fat_cache_block = entry_cache_block;
118 sb->fat_cache_no = fat_no;
121 /* Give the block back if they asked for it (needed to mark the block
122 * dirty if they're writing) */
123 if (rb != NULL)
124 *rb = sb->fat_blocks[entry_cache_offset >> sb->sectorsize_bits];
126 /* Compute the pointer location and return it */
127 return sb->fat_buffers[entry_cache_offset >> sb->sectorsize_bits] +
128 (entry_cache_offset & (sb->sectorsize - 1));
131 /* FAT12 has, as the name suggests, 12-bit FAT entries. This means that two
132 * entries are condensed into three bytes, like so:
134 * entry: aaaaaaaa aaaabbbb bbbbbbbb
135 * bytes: xxxxxxxx xxxxxxxx xxxxxxxx
137 * To get at the entry we want, we find and grab the word starting at either
138 * byte 0 or 1 of the three-byte set, then shift up or down as needed. FATdoc
139 * 1.03 p16-17 describes the method
141 * The only tricky bit is if the word falls such that the first byte is the
142 * last byte of the block and the second byte is the first byte of the next
143 * block. Since our block data are stored within cache block structures, a
144 * simple cast won't do (hell, the second block may not even be in memory if
145 * we're at the end of the FAT cache). So we get it a byte at a time, and
146 * build the word ourselves.
148 static ULONG GetFat12Entry(struct FSSuper *sb, ULONG n)
150 D(struct Globals *glob = sb->glob);
151 ULONG offset = n + n / 2;
152 UWORD val;
154 if ((offset & (sb->sectorsize - 1)) == sb->sectorsize - 1)
156 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
158 val = *GetFatEntryPtr(sb, offset + 1, NULL, 0) << 8;
159 val |= *GetFatEntryPtr(sb, offset, NULL, 0);
161 else
162 val = AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, offset, NULL, 0)));
164 if (n & 1)
165 val >>= 4;
166 else
167 val &= 0xfff;
169 return val;
173 * FAT16 and FAT32, on the other hand, have nice neat entry widths, so simple
174 * word/long casts are fine. There's also no chance that the entry can be
175 * split across blocks. Why can't everything be this simple?
177 static ULONG GetFat16Entry(struct FSSuper *sb, ULONG n)
179 return AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, n << 1, NULL, 0)));
182 static ULONG GetFat32Entry(struct FSSuper *sb, ULONG n)
184 return AROS_LE2LONG(*((ULONG *) GetFatEntryPtr(sb, n << 2, NULL, 0)))
185 & 0x0fffffff;
188 static void SetFat12Entry(struct FSSuper *sb, ULONG n, ULONG val)
190 D(struct Globals *glob = sb->glob);
191 APTR b;
192 ULONG offset = n + n / 2;
193 BOOL boundary = FALSE;
194 UWORD *fat = NULL, newval, i;
196 for (i = 0; i < sb->fat_count; i++)
198 if ((offset & (sb->sectorsize - 1)) == sb->sectorsize - 1)
200 boundary = TRUE;
202 D(bug(
203 "[fat] fat12 cluster pair on block boundary, compensating\n"));
205 newval = *GetFatEntryPtr(sb, offset + 1, NULL, i) << 8;
206 newval |= *GetFatEntryPtr(sb, offset, NULL, i);
208 else
210 fat = (UWORD *) GetFatEntryPtr(sb, offset, &b, i);
211 newval = AROS_LE2WORD(*fat);
214 if (n & 1)
216 val <<= 4;
217 newval = (newval & 0xf) | val;
219 else
221 newval = (newval & 0xf000) | val;
224 if (boundary)
226 /* XXX: Ideally we'd mark both blocks dirty at the same time or
227 * only do it once if they're the same block. Unfortunately any
228 * old value of b is invalid after a call to GetFatEntryPtr, as
229 * it may have swapped the previous cache out. This is probably
230 * safe enough. */
231 *GetFatEntryPtr(sb, offset + 1, &b, i) = newval >> 8;
232 Cache_MarkBlockDirty(sb->cache, b);
233 *GetFatEntryPtr(sb, offset, &b, i) = newval & 0xff;
234 Cache_MarkBlockDirty(sb->cache, b);
236 else
238 *fat = AROS_WORD2LE(newval);
239 Cache_MarkBlockDirty(sb->cache, b);
244 static void SetFat16Entry(struct FSSuper *sb, ULONG n, ULONG val)
246 APTR b;
247 UWORD i;
249 for (i = 0; i < sb->fat_count; i++)
251 *((UWORD *) GetFatEntryPtr(sb, n << 1, &b, i)) =
252 AROS_WORD2LE((UWORD) val);
253 Cache_MarkBlockDirty(sb->cache, b);
257 static void SetFat32Entry(struct FSSuper *sb, ULONG n, ULONG val)
259 APTR b;
260 ULONG *fat;
261 UWORD i;
263 for (i = 0; i < sb->fat_count; i++)
265 fat = (ULONG *) GetFatEntryPtr(sb, n << 2, &b, i);
267 *fat = (*fat & 0xf0000000) | val;
269 Cache_MarkBlockDirty(sb->cache, b);
273 LONG ReadFATSuper(struct FSSuper *sb)
275 struct Globals *glob = sb->glob;
276 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
277 LONG err;
278 ULONG bsize = de->de_SizeBlock * 4, total_sectors, id;
279 struct FATBootSector *boot;
280 struct FATEBPB *ebpb;
281 struct FATFSInfo *fsinfo;
282 BOOL invalid = FALSE;
283 ULONG end;
284 LONG i;
285 struct DirHandle dh;
286 struct DirEntry dir_entry;
287 APTR block_ref;
288 UBYTE *fat_block;
290 D(bug("[fat] reading boot sector\n"));
292 boot = AllocMem(bsize, MEMF_ANY);
293 if (!boot)
294 return ERROR_NO_FREE_STORE;
296 sb->first_device_sector =
297 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
299 /* Get a preliminary total-sectors value so we don't risk going outside
300 * partition limits */
301 sb->total_sectors =
302 de->de_BlocksPerTrack * de->de_Surfaces * (de->de_HighCyl + 1)
303 - sb->first_device_sector;
305 D(bug("[fat] boot sector at sector %ld\n", sb->first_device_sector));
308 * Read the boot sector. We go direct because we don't have a cache yet,
309 * and can't create one until we know the sector size, which is held in
310 * the boot sector. In practice it doesn't matter - we're going to use
311 * this once and once only.
313 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize,
314 (UBYTE *) boot, glob)) != 0)
316 D(bug("[fat] couldn't read boot block (%ld)\n", err));
317 FreeMem(boot, bsize);
318 return err;
321 D(bug("\tBoot sector:\n"));
323 sb->sectorsize = AROS_LE2WORD(boot->bpb_bytes_per_sect);
324 sb->sectorsize_bits = log2(sb->sectorsize);
325 D(bug("\tSectorSize = %ld\n", sb->sectorsize));
326 D(bug("\tSectorSize Bits = %ld\n", sb->sectorsize_bits));
328 sb->cluster_sectors = boot->bpb_sect_per_clust;
329 sb->clustersize = sb->sectorsize * boot->bpb_sect_per_clust;
330 sb->clustersize_bits = log2(sb->clustersize);
331 sb->cluster_sectors_bits = sb->clustersize_bits - sb->sectorsize_bits;
333 D(bug("\tSectorsPerCluster = %ld\n", (ULONG) boot->bpb_sect_per_clust));
334 D(bug("\tClusterSize = %ld\n", sb->clustersize));
335 D(bug("\tClusterSize Bits = %ld\n", sb->clustersize_bits));
336 D(bug("\tCluster Sectors Bits = %ld\n", sb->cluster_sectors_bits));
338 sb->first_fat_sector = AROS_LE2WORD(boot->bpb_rsvd_sect_count);
339 D(bug("\tFirst FAT Sector = %ld\n", sb->first_fat_sector));
341 sb->fat_count = boot->bpb_num_fats;
342 D(bug("\tNumber of FATs = %d\n", sb->fat_count));
344 if (boot->bpb_fat_size_16 != 0)
345 sb->fat_size = AROS_LE2WORD(boot->bpb_fat_size_16);
346 else
347 sb->fat_size = AROS_LE2LONG(boot->ebpbs.ebpb32.bpb_fat_size_32);
348 D(bug("\tFAT Size = %ld\n", sb->fat_size));
350 if (boot->bpb_total_sectors_16 != 0)
351 total_sectors = AROS_LE2WORD(boot->bpb_total_sectors_16);
352 else
353 total_sectors = AROS_LE2LONG(boot->bpb_total_sectors_32);
354 D(bug("\tTotal Sectors = %ld\n", sb->total_sectors));
356 /* Check that the boot block's sector count is the same as the
357 * partition's sector count. This stops a resized partition being
358 * mounted before reformatting */
359 if (total_sectors != sb->total_sectors)
360 invalid = TRUE;
362 sb->rootdir_sectors = ((AROS_LE2WORD(boot->bpb_root_entries_count)
363 * sizeof(struct FATDirEntry)) + (sb->sectorsize - 1))
364 >> sb->sectorsize_bits;
365 D(bug("\tRootDir Sectors = %ld\n", sb->rootdir_sectors));
367 sb->data_sectors = sb->total_sectors - (sb->first_fat_sector
368 + (sb->fat_count * sb->fat_size) + sb->rootdir_sectors);
369 D(bug("\tData Sectors = %ld\n", sb->data_sectors));
371 sb->clusters_count = sb->data_sectors >> sb->cluster_sectors_bits;
372 D(bug("\tClusters Count = %ld\n", sb->clusters_count));
374 sb->first_rootdir_sector =
375 sb->first_fat_sector + (sb->fat_count * sb->fat_size);
376 D(bug("\tFirst RootDir Sector = %ld\n", sb->first_rootdir_sector));
378 sb->first_data_sector =
379 sb->first_fat_sector + (sb->fat_count * sb->fat_size)
380 + sb->rootdir_sectors;
381 D(bug("\tFirst Data Sector = %ld\n", sb->first_data_sector));
383 /* Check if disk is in fact a FAT filesystem */
385 /* Valid sector size: 512, 1024, 2048, 4096 */
386 if (sb->sectorsize != 512 && sb->sectorsize != 1024
387 && sb->sectorsize != 2048 && sb->sectorsize != 4096)
388 invalid = TRUE;
390 /* Valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
391 if ((boot->bpb_sect_per_clust & (boot->bpb_sect_per_clust - 1)) != 0
392 || boot->bpb_sect_per_clust == 0 || boot->bpb_sect_per_clust > 128)
393 invalid = TRUE;
395 /* Valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
396 if (sb->clustersize > 64 * 1024)
397 invalid = TRUE;
399 if (sb->first_fat_sector == 0)
400 invalid = TRUE;
402 if (sb->fat_count == 0)
403 invalid = TRUE;
405 if (boot->bpb_media < 0xF0)
406 invalid = TRUE;
408 /* FAT "signature" */
409 if (boot->bpb_signature[0] != 0x55 || boot->bpb_signature[1] != 0xaa)
410 invalid = TRUE;
412 if (invalid)
414 D(bug("\tInvalid FAT Boot Sector\n"));
415 FreeMem(boot, bsize);
416 return ERROR_NOT_A_DOS_DISK;
418 end = 0xFFFFFFFF / sb->sectorsize;
419 if ((sb->first_device_sector + sb->total_sectors - 1 > end)
420 && (glob->readcmd == CMD_READ))
422 D(bug("\tDevice is too large\n"));
423 FreeMem(boot, bsize);
424 return IOERR_BADADDRESS;
427 sb->cache = Cache_CreateCache(glob, 64, 64, sb->sectorsize, SysBase,
428 DOSBase);
430 if (sb->clusters_count < 4085)
432 D(bug("\tFAT12 filesystem detected\n"));
433 sb->type = 12;
434 sb->eoc_mark = 0x0FFF;
435 sb->func_get_fat_entry = GetFat12Entry;
436 sb->func_set_fat_entry = SetFat12Entry;
438 else if (sb->clusters_count < 65525)
440 D(bug("\tFAT16 filesystem detected\n"));
441 sb->type = 16;
442 sb->eoc_mark = 0xFFFF;
443 sb->func_get_fat_entry = GetFat16Entry;
444 sb->func_set_fat_entry = SetFat16Entry;
446 else
448 D(bug("\tFAT32 filesystem detected\n"));
449 sb->type = 32;
450 sb->eoc_mark = 0x0FFFFFFF;
451 sb->func_get_fat_entry = GetFat32Entry;
452 sb->func_set_fat_entry = SetFat32Entry;
454 glob->sb = sb;
456 /* Set up the FAT cache and load the first blocks */
457 sb->fat_cachesize = 4096;
458 sb->fat_cachesize_bits = log2(sb->fat_cachesize);
459 sb->fat_cache_block = 0xffffffff;
461 sb->fat_blocks_count =
462 MIN(sb->fat_size, sb->fat_cachesize >> sb->sectorsize_bits);
463 sb->fat_blocks = AllocVecPooled(glob->mempool,
464 sizeof(APTR) * sb->fat_blocks_count);
465 sb->fat_buffers = AllocVecPooled(glob->mempool,
466 sizeof(APTR) * sb->fat_blocks_count);
468 if (sb->type != 32)
470 /* Set up volume ID */
471 sb->volume_id = AROS_LE2LONG(boot->ebpbs.ebpb.bs_volid);
473 /* Location of root directory */
474 sb->rootdir_cluster = 0;
475 sb->rootdir_sector = sb->first_rootdir_sector;
476 ebpb = &boot->ebpbs.ebpb;
478 else
480 /* Set up volume ID */
481 sb->volume_id = AROS_LE2LONG(boot->ebpbs.ebpb32.ebpb.bs_volid);
483 /* Location of root directory */
484 sb->rootdir_cluster =
485 AROS_LE2LONG(boot->ebpbs.ebpb32.bpb_root_cluster);
486 sb->rootdir_sector = 0;
487 ebpb = &boot->ebpbs.ebpb32.ebpb;
490 D(bug("[fat] rootdir at cluster %ld sector %ld\n", sb->rootdir_cluster,
491 sb->rootdir_sector));
493 /* Initialise the root directory if this is a newly formatted volume */
494 if (glob->formatting)
496 /* Clear all FAT sectors */
497 for (i = 0; i < sb->fat_size * 2; i++)
499 block_ref = Cache_GetBlock(sb->cache,
500 sb->first_device_sector + sb->first_fat_sector + i,
501 &fat_block);
502 /* FIXME: Handle IO errors on cache read! */
503 memset(fat_block, 0, bsize);
504 if (i == 0)
506 /* The first two entries are special */
507 if (sb->type == 32)
508 *(UQUAD *) fat_block = AROS_QUAD2LE(0x0FFFFFFF0FFFFFF8);
509 else if (sb->type == 16)
510 *(ULONG *) fat_block = AROS_LONG2LE(0xFFFFFFF8);
511 else
512 *(ULONG *) fat_block = AROS_LONG2LE(0x00FFFFF8);
514 Cache_MarkBlockDirty(sb->cache, block_ref);
515 Cache_FreeBlock(sb->cache, block_ref);
518 /* Allocate first cluster of the root directory */
519 if (sb->type == 32)
520 AllocCluster(sb, sb->rootdir_cluster);
522 /* Get a handle on the root directory */
523 InitDirHandle(sb, 0, &dh, FALSE, glob);
525 /* Clear all entries */
526 for (i = 0; GetDirEntry(&dh, i, &dir_entry, glob) == 0; i++)
528 memset(&dir_entry.e.entry, 0, sizeof(struct FATDirEntry));
529 UpdateDirEntry(&dir_entry, glob);
532 SetVolumeName(sb, ebpb->bs_vollab, FAT_MAX_SHORT_NAME);
534 ReleaseDirHandle(&dh, glob);
535 glob->formatting = FALSE;
536 D(bug("\tRoot dir created.\n"));
539 if (GetVolumeIdentity(sb, &(sb->volume)) != 0)
541 LONG i;
542 UBYTE *uu = (void *)&sb->volume_id;
544 /* No volume name entry, so construct name from serial number */
545 for (i = 1; i < 10;)
547 int d;
549 if (i == 5)
550 sb->volume.name[i++] = '-';
552 d = (*uu) & 0x0f;
553 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
554 d = ((*uu) & 0xf0) >> 4;
555 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
557 uu++;
560 sb->volume.name[i] = '\0';
561 sb->volume.name[0] = 9;
564 /* Many FAT volumes do not have a creation date set, with the result
565 * that two volumes with the same name are likely to be indistinguishable
566 * on the DOS list. To work around this problem, we set the ds_Tick field
567 * of such volumes' dol_VolumeDate timestamp to a pseudo-random value based
568 * on the serial number. Since there are 3000 ticks in a minute, we use an
569 * 11-bit hash value in the range 0 to 2047.
571 if (CompareDates(&sb->volume.create_time, &unset_date_limit) > 0)
573 id = sb->volume_id;
574 sb->volume.create_time.ds_Days = 0;
575 sb->volume.create_time.ds_Minute = 0;
576 sb->volume.create_time.ds_Tick = (id >> 22 ^ id >> 11 ^ id) & 0x7FF;
577 D(bug("[fat] Set hash time to %ld ticks\n",
578 sb->volume.create_time.ds_Tick));
581 /* Get initial number of free clusters */
582 sb->free_clusters = -1;
583 sb->next_cluster = -1;
584 if (sb->type == 32)
586 sb->fsinfo_block = Cache_GetBlock(sb->cache, sb->first_device_sector
587 + AROS_LE2WORD(boot->ebpbs.ebpb32.bpb_fs_info),
588 (UBYTE **) &fsinfo);
589 if (sb->fsinfo_block != NULL)
591 if (fsinfo->lead_sig == AROS_LONG2LE(FSI_LEAD_SIG)
592 && fsinfo->struct_sig == AROS_LONG2LE(FSI_STRUCT_SIG)
593 && fsinfo->trail_sig == AROS_LONG2LE(FSI_TRAIL_SIG))
595 sb->free_clusters = AROS_LE2LONG(fsinfo->free_count);
596 sb->next_cluster = AROS_LE2LONG(fsinfo->next_free);
597 D(bug("[fat] valid FATFSInfo block found\n"));
598 sb->fsinfo_buffer = fsinfo;
600 else
601 Cache_FreeBlock(sb->cache, sb->fsinfo_block);
603 else
605 /* FIXME: Report IO errors to the user! */
608 if (sb->free_clusters == -1)
609 CountFreeClusters(sb);
610 if (sb->next_cluster == -1)
611 sb->next_cluster = 2;
613 D(bug("\tFAT Filesystem successfully detected.\n"));
614 D(bug("\tFree Clusters = %ld\n", sb->free_clusters));
615 D(bug("\tNext Free Cluster = %ld\n", sb->next_cluster));
616 FreeMem(boot, bsize);
617 return 0;
620 static LONG GetVolumeIdentity(struct FSSuper *sb,
621 struct VolumeIdentity *volume)
623 struct Globals *glob = sb->glob;
624 struct DirHandle dh;
625 struct DirEntry de;
626 LONG err;
627 int i;
629 D(bug("[fat] searching root directory for volume name\n"));
631 /* Search the directory for the volume ID entry. It would've been nice to
632 * just use GetNextDirEntry but I didn't want a flag or something to tell
633 * it not to skip the volume name */
634 InitDirHandle(sb, sb->rootdir_cluster, &dh, FALSE, glob);
636 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de, glob)) == 0)
639 /* Match the volume ID entry */
640 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
641 && de.e.entry.name[0] != 0xe5)
643 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
645 /* Copy the name in. 'volume->name' is a BSTR */
646 volume->name[1] = de.e.entry.name[0];
647 for (i = 1; i < FAT_MAX_SHORT_NAME; i++)
649 if (volume->name[i] == ' ')
650 volume->name[i + 1] = de.e.entry.name[i];
651 else
652 volume->name[i + 1] = tolower(de.e.entry.name[i]);
655 for (i = 10; volume->name[i + 1] == ' '; i--);
656 volume->name[i + 2] = '\0';
657 volume->name[0] = strlen(&(volume->name[1]));
659 /* Get the volume creation date too */
660 ConvertFATDate(de.e.entry.create_date, de.e.entry.create_time,
661 &volume->create_time, glob);
663 D(bug("[fat] volume name is '%s'\n", &(volume->name[1])));
665 break;
668 /* Bail out if we hit the end of the dir */
669 if (de.e.entry.name[0] == 0x00)
671 D(bug("[fat] found end-of-directory marker,"
672 " volume name entry not found\n"));
673 err = ERROR_OBJECT_NOT_FOUND;
674 break;
678 ReleaseDirHandle(&dh, glob);
679 return err;
682 LONG FormatFATVolume(const UBYTE *name, UWORD len, struct Globals *glob)
684 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
685 LONG err;
686 ULONG bsize = de->de_SizeBlock * 4;
687 struct FATBootSector *boot;
688 struct FATEBPB *ebpb;
689 struct FATFSInfo *fsinfo;
690 UWORD type, i, root_entries_count;
691 struct EClockVal eclock;
692 ULONG sectors_per_cluster = 0, sector_count, first_fat_sector,
693 fat_size, root_dir_sectors, first_device_sector, temp1, temp2;
695 /* Decide on FAT type based on number of sectors */
696 sector_count = (de->de_HighCyl - de->de_LowCyl + 1)
697 * de->de_Surfaces * de->de_BlocksPerTrack;
698 if (sector_count < 4085)
699 type = 12;
700 else if (sector_count < 1024 * 1024)
701 type = 16;
702 else
703 type = 32;
705 D(bug("[fat] writing boot sector\n"));
707 /* Decide on cluster size and root dir entries */
708 first_fat_sector = 1;
709 if (type == 12)
711 if (sector_count == 1440)
713 sectors_per_cluster = 2;
714 root_entries_count = 112;
716 else if (sector_count == 2880)
718 sectors_per_cluster = 1;
719 root_entries_count = 224;
721 else if (sector_count == 5760)
723 sectors_per_cluster = 2;
724 root_entries_count = 240;
726 else
728 /* We only support some common 3.5" floppy formats */
729 return ERROR_NOT_IMPLEMENTED;
732 else if (type == 16)
734 for (i = 0; fat16_cluster_thresholds[i] < sector_count; i++);
735 sectors_per_cluster = 1 << i;
736 root_entries_count = 512;
738 else
740 for (i = 0; fat32_cluster_thresholds[i] < sector_count; i++);
741 sectors_per_cluster = 8 << i;
742 root_entries_count = 0;
743 first_fat_sector = 32;
746 D(bug("\tFirst FAT Sector = %ld\n", first_fat_sector));
748 /* Determine FAT size */
749 root_dir_sectors = (root_entries_count * 32 + (bsize - 1)) / bsize;
750 temp1 = sector_count - (first_fat_sector + root_dir_sectors);
751 temp2 = 256 * sectors_per_cluster + 2;
752 if (type == 32)
753 temp2 /= 2;
754 fat_size = (temp1 + temp2 - 1) / temp2;
756 boot = AllocMem(bsize, MEMF_CLEAR);
757 if (!boot)
758 return ERROR_NO_FREE_STORE;
760 /* Install x86 infinite loop boot code to keep major OSes happy */
761 boot->bs_jmp_boot[0] = 0xEB;
762 boot->bs_jmp_boot[1] = 0xFE;
763 boot->bs_jmp_boot[2] = 0x90;
765 CopyMem(default_oem_name, boot->bs_oem_name, 8);
767 boot->bpb_bytes_per_sect = AROS_WORD2LE(bsize);
768 boot->bpb_sect_per_clust = sectors_per_cluster;
770 boot->bpb_rsvd_sect_count = AROS_WORD2LE(first_fat_sector);
772 boot->bpb_num_fats = 2;
774 boot->bpb_root_entries_count = AROS_WORD2LE(root_entries_count);
776 if (sector_count < 0x10000 && type != 32)
777 boot->bpb_total_sectors_16 = AROS_WORD2LE(sector_count);
778 else
779 boot->bpb_total_sectors_32 = AROS_LONG2LE(sector_count);
781 boot->bpb_media = 0xF8;
783 boot->bpb_sect_per_track = AROS_WORD2LE(de->de_BlocksPerTrack);
784 boot->bpb_num_heads = AROS_WORD2LE(de->de_Surfaces);
785 boot->bpb_hidden_sect = AROS_LONG2LE(de->de_Reserved);
787 if (type == 32)
789 boot->ebpbs.ebpb32.bpb_fat_size_32 = AROS_LONG2LE(fat_size);
790 boot->ebpbs.ebpb32.bpb_root_cluster = AROS_LONG2LE(2);
791 boot->ebpbs.ebpb32.bpb_fs_info = AROS_WORD2LE(1);
792 boot->ebpbs.ebpb32.bpb_back_bootsec = AROS_WORD2LE(6);
793 ebpb = &boot->ebpbs.ebpb32.ebpb;
795 else
797 boot->bpb_fat_size_16 = AROS_WORD2LE(fat_size);
798 ebpb = &boot->ebpbs.ebpb;
801 ebpb->bs_drvnum = 0x80;
802 ebpb->bs_bootsig = 0x29;
804 /* Generate a pseudo-random serial number. Not the original algorithm,
805 * but it shouldn't matter */
806 ReadEClock(&eclock);
807 ebpb->bs_volid = FastRand(eclock.ev_lo ^ eclock.ev_hi);
809 /* Copy volume name in */
810 for (i = 0; i < FAT_MAX_SHORT_NAME; i++)
811 if (i < len)
812 ebpb->bs_vollab[i] = toupper(name[i]);
813 else
814 ebpb->bs_vollab[i] = ' ';
816 CopyMem(default_filsystype, ebpb->bs_filsystype, 8);
817 if (type != 16)
819 if (type == 32)
820 ebpb->bs_filsystype[3] = '3';
821 ebpb->bs_filsystype[4] = '2';
824 boot->bpb_signature[0] = 0x55;
825 boot->bpb_signature[1] = 0xaa;
827 /* Write the boot sector */
828 first_device_sector =
829 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
831 D(bug("[fat] boot sector at sector %ld\n", first_device_sector));
833 if ((err = AccessDisk(TRUE, first_device_sector, 1, bsize,
834 (UBYTE *) boot, glob)) != 0)
836 D(bug("[fat] couldn't write boot block (%ld)\n", err));
837 FreeMem(boot, bsize);
838 return err;
841 /* Write back-up boot sector and FS info sector */
842 if (type == 32)
844 if ((err = AccessDisk(TRUE, first_device_sector + 6, 1, bsize,
845 (UBYTE *) boot, glob)) != 0)
847 D(bug("[fat] couldn't write back-up boot block (%ld)\n", err));
848 FreeMem(boot, bsize);
849 return err;
852 fsinfo = (APTR) boot;
853 memset(fsinfo, 0, bsize);
855 fsinfo->lead_sig = AROS_LONG2LE(FSI_LEAD_SIG);
856 fsinfo->struct_sig = AROS_LONG2LE(FSI_STRUCT_SIG);
857 fsinfo->trail_sig = AROS_LONG2LE(FSI_TRAIL_SIG);
858 fsinfo->free_count = AROS_LONG2LE(0xFFFFFFFF);
859 fsinfo->next_free = AROS_LONG2LE(0xFFFFFFFF);
861 if ((err = AccessDisk(TRUE, first_device_sector + 1, 1, bsize,
862 (UBYTE *) fsinfo, glob)) != 0)
864 D(bug("[fat] couldn't write back-up boot block (%ld)\n", err));
865 FreeMem(boot, bsize);
866 return err;
870 FreeMem(boot, bsize);
872 glob->formatting = TRUE;
874 return 0;
877 LONG SetVolumeName(struct FSSuper *sb, UBYTE *name, UWORD len)
879 struct Globals *glob = sb->glob;
880 struct DirHandle dh;
881 struct DirEntry de;
882 LONG err;
883 int i;
884 struct DosEnvec *dosenv = BADDR(glob->fssm->fssm_Environ);
885 ULONG bsize = dosenv->de_SizeBlock * 4;
886 struct FATBootSector *boot;
888 /* Truncate name if necessary */
889 if (len > FAT_MAX_SHORT_NAME)
890 len = FAT_MAX_SHORT_NAME;
892 /* Read boot block */
893 boot = AllocMem(bsize, MEMF_ANY);
894 if (!boot)
895 return ERROR_NO_FREE_STORE;
897 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize,
898 (UBYTE *) boot, glob)) != 0)
900 D(bug("[fat] couldn't read boot block (%ld)\n", err));
901 FreeMem(boot, bsize);
902 return err;
905 D(bug("[fat] searching root directory for volume name\n"));
907 /* Search the directory for the volume ID entry. It would've been nice to
908 * just use GetNextDirEntry but I didn't want a flag or something to tell
909 * it not to skip the volume name */
910 InitDirHandle(sb, 0, &dh, FALSE, glob);
912 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de, glob)) == 0)
915 /* Match the volume ID entry */
916 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
917 && de.e.entry.name[0] != 0xe5)
919 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
920 err = 0;
921 break;
924 /* Bail out if we hit the end of the dir */
925 if (de.e.entry.name[0] == 0x00)
927 D(bug("[fat] found end-of-directory marker,"
928 " volume name entry not found\n"));
929 err = ERROR_OBJECT_NOT_FOUND;
930 break;
934 /* Create a new volume ID entry if there wasn't one */
935 if (err != 0)
937 err = AllocDirEntry(&dh, 0, &de, glob);
938 if (err == 0)
939 FillDirEntry(&de, ATTR_VOLUME_ID, 0, glob);
942 /* Copy the name in */
943 if (err == 0)
945 for (i = 0; i < FAT_MAX_SHORT_NAME; i++)
946 if (i < len)
947 de.e.entry.name[i] = toupper(name[i]);
948 else
949 de.e.entry.name[i] = ' ';
951 if ((err = UpdateDirEntry(&de, glob)) != 0)
953 D(bug("[fat] couldn't change volume name\n"));
954 return err;
958 /* Copy name to boot block as well, and save */
959 if (sb->type == 32)
960 CopyMem(de.e.entry.name, boot->ebpbs.ebpb32.ebpb.bs_vollab,
961 FAT_MAX_SHORT_NAME);
962 else
963 CopyMem(de.e.entry.name, boot->ebpbs.ebpb.bs_vollab,
964 FAT_MAX_SHORT_NAME);
966 if ((err = AccessDisk(TRUE, sb->first_device_sector, 1, bsize,
967 (UBYTE *) boot, glob)) != 0)
968 D(bug("[fat] couldn't write boot block (%ld)\n", err));
969 FreeMem(boot, bsize);
971 /* Update name in SB */
972 sb->volume.name[0] = len;
973 sb->volume.name[1] = toupper(name[0]);
974 for (i = 1; i < len; i++)
975 sb->volume.name[i + 1] = tolower(name[i]);
976 sb->volume.name[len + 1] = '\0';
978 D(bug("[fat] new volume name is '%s'\n", &(sb->volume.name[1])));
980 ReleaseDirHandle(&dh, glob);
981 return err;
984 LONG FindFreeCluster(struct FSSuper *sb, ULONG *rcluster)
986 D(struct Globals *glob = sb->glob);
987 ULONG cluster = 0;
988 BOOL found = FALSE;
990 for (cluster = sb->next_cluster;
991 cluster < 2 + sb->clusters_count && !found; cluster++)
993 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
995 *rcluster = cluster;
996 found = TRUE;
1000 if (!found)
1002 for (cluster = 2; cluster < sb->next_cluster && !found; cluster++)
1004 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
1006 *rcluster = cluster;
1007 found = TRUE;
1012 if (!found)
1014 D(bug("[fat] no more free clusters, we're out of space\n"));
1015 return ERROR_DISK_FULL;
1018 sb->next_cluster = *rcluster;
1020 D(bug("[fat] found free cluster %ld\n", *rcluster));
1022 return 0;
1025 void FreeFATSuper(struct FSSuper *sb)
1027 struct Globals *glob = sb->glob;
1028 D(bug("\tRemoving Super Block from memory\n"));
1029 Cache_DestroyCache(sb->cache);
1030 FreeVecPooled(glob->mempool, sb->fat_buffers);
1031 sb->fat_buffers = NULL;
1032 FreeVecPooled(glob->mempool, sb->fat_blocks);
1033 sb->fat_blocks = NULL;
1036 /* See how many unused clusters are available */
1037 void CountFreeClusters(struct FSSuper *sb)
1039 D(struct Globals *glob = sb->glob);
1040 ULONG cluster = 0;
1041 ULONG free = 0;
1043 /* Loop over all the data clusters */
1044 for (cluster = 2; cluster < sb->clusters_count + 2; cluster++)
1046 /* Record the free ones */
1047 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
1048 free++;
1051 /* Put the value away for later */
1052 sb->free_clusters = free;
1054 D(bug("\tfree clusters: %ld\n", free));
1057 void AllocCluster(struct FSSuper *sb, ULONG cluster)
1059 SET_NEXT_CLUSTER(sb, cluster, sb->eoc_mark);
1060 sb->free_clusters--;
1061 if (sb->fsinfo_buffer != NULL)
1063 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
1064 sb->fsinfo_buffer->next_free = AROS_LONG2LE(sb->next_cluster);
1065 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
1069 void FreeCluster(struct FSSuper *sb, ULONG cluster)
1071 SET_NEXT_CLUSTER(sb, cluster, 0);
1072 sb->free_clusters++;
1073 if (sb->fsinfo_buffer != NULL)
1075 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
1076 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
1080 void ConvertFATDate(UWORD date, UWORD time, struct DateStamp *ds,
1081 struct Globals *glob)
1083 ULONG year, month, day, hours, mins, secs;
1084 struct ClockData clock_data;
1086 /* Date bits: yyyy yyym mmmd dddd */
1087 year = (date & 0xfe00) >> 9; /* Bits 15-9 */
1088 month = (date & 0x01e0) >> 5; /* bits 8-5 */
1089 day = date & 0x001f; /* Bits 4-0 */
1091 /* Time bits: hhhh hmmm mmms ssss */
1092 hours = (time & 0xf800) >> 11; /* Bits 15-11 */
1093 mins = (time & 0x07e0) >> 5; /* Bits 10-5 */
1094 secs = time & 0x001f; /* Bits 4-0 */
1096 D(bug("[fat] converting fat date: year %d month %d day %d hours %d"
1097 " mins %d secs %d\n", year, month, day, hours, mins, secs));
1099 if (month < 1 || month > 12 || day < 1 || day > 31 || hours > 23 ||
1100 mins > 59 || secs > 29)
1102 D(bug("[fat] invalid fat date: using 01-01-1978 instead\n"));
1103 secs = 0;
1105 else
1107 clock_data.year = 1980 + year;
1108 clock_data.month = month;
1109 clock_data.mday = day;
1110 clock_data.hour = hours;
1111 clock_data.min = mins;
1112 clock_data.sec = secs << 1;
1113 secs = Date2Amiga(&clock_data);
1116 /* Calculate days since 1978-01-01 (DOS epoch) */
1117 ds->ds_Days = secs / (60 * 60 * 24);
1119 /* Minutes since midnight */
1120 ds->ds_Minute = secs / 60 % (24 * 60);
1122 /* 1/50 sec ticks since last minute */
1123 ds->ds_Tick = secs % 60 * TICKS_PER_SECOND;
1125 D(bug("[fat] converted fat date: days %ld minutes %ld ticks %ld\n",
1126 ds->ds_Days, ds->ds_Minute, ds->ds_Tick));
1129 void ConvertDOSDate(struct DateStamp *ds, UWORD * date, UWORD * time,
1130 struct Globals *glob)
1132 ULONG year, month, day, hours, mins, secs;
1133 struct ClockData clock_data;
1135 /* Convert datestamp to seconds since 1978 */
1136 secs = ds->ds_Days * 60 * 60 * 24 + ds->ds_Minute * 60
1137 + ds->ds_Tick / TICKS_PER_SECOND;
1139 /* Round up to next even second because of FAT's two-second granularity */
1140 secs = (secs & ~1) + 2;
1142 /* Convert seconds since 1978 to calendar/time data */
1143 Amiga2Date(secs, &clock_data);
1145 /* Get values used in FAT dates */
1146 year = clock_data.year - 1980;
1147 month = clock_data.month - 0;
1148 day = clock_data.mday;
1149 hours = clock_data.hour;
1150 mins = clock_data.min;
1151 secs = clock_data.sec >> 1;
1153 /* All that remains is to bit-encode the whole lot */
1155 /* Date bits: yyyy yyym mmmd dddd */
1156 *date = (((ULONG) year) << 9) | (((ULONG) month) << 5) | day;
1158 /* Time bits: hhhh hmmm mmms ssss */
1159 *time = (((ULONG) hours) << 11) | (((ULONG) mins) << 5) | secs;