- Implemented ACTION_FORMAT.
[AROS.git] / rom / filesys / fat / fat.c
blob664693af52ba39962a5cbe2950fe6cd7a4933e88
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 /* helper function to get the location of a fat entry for a cluster. it used
59 * to be a define until it got too crazy */
60 static UBYTE *GetFatEntryPtr(struct FSSuper *sb, ULONG offset, APTR *rb,
61 UWORD fat_no) {
62 ULONG entry_cache_block = offset >> sb->fat_cachesize_bits;
63 ULONG entry_cache_offset = offset & (sb->fat_cachesize - 1);
64 ULONG num;
65 UWORD i;
67 /* if the target cluster is not within the currently loaded chunk of fat,
68 * we need to get the right data in */
69 if (sb->fat_cache_block != entry_cache_block
70 || sb->fat_cache_no != fat_no) {
71 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)));
72 /* put the old ones back */
73 if (sb->fat_cache_block != 0xffffffff)
74 for (i = 0; i < sb->fat_blocks_count; i++)
75 Cache_FreeBlock(sb->cache, sb->fat_blocks[i]);
77 /* load some more */
78 num = sb->first_device_sector + sb->first_fat_sector
79 + sb->fat_size * fat_no + (entry_cache_block
80 << (sb->fat_cachesize_bits - sb->sectorsize_bits));
81 for (i = 0; i < sb->fat_blocks_count; i++)
82 sb->fat_blocks[i] =
83 Cache_GetBlock(sb->cache, num + i, &sb->fat_buffers[i]);
85 /* remember where we are for next time */
86 sb->fat_cache_block = entry_cache_block;
87 sb->fat_cache_no = fat_no;
90 /* give the block back if they asked for it (needed to mark the block
91 * dirty if they're writing) */
92 if (rb != NULL)
93 *rb = sb->fat_blocks[entry_cache_offset >> sb->sectorsize_bits];
95 /* compute the pointer location and return it */
96 return sb->fat_buffers[entry_cache_offset >> sb->sectorsize_bits] +
97 (entry_cache_offset & (sb->sectorsize - 1));
100 /* FAT12 has, as the name suggests, 12-bit FAT entries. This means that two
101 * entries are condensed into three bytes, like so:
103 * entry: aaaaaaaa aaaabbbb bbbbbbbb
104 * bytes: xxxxxxxx xxxxxxxx xxxxxxxx
106 * To get at the entry we want, we find and grab the word starting at either
107 * byte 0 or 1 of the three-byte set, then shift up or down as needed. FATdoc
108 * 1.03 p16-17 describes the method
110 * The only tricky bit is if the word falls such that the first byte is the
111 * last byte of the block and the second byte is the first byte of the next
112 * block. Since our block data are stored within cache block structures, a
113 * simple cast won't do (hell, the second block may not even be in memory if
114 * we're at the end of the FAT cache). So we get it a byte at a time, and
115 * build the word ourselves.
117 static ULONG GetFat12Entry(struct FSSuper *sb, ULONG n) {
118 ULONG offset = n + n/2;
119 UWORD val;
121 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
122 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
124 val = *GetFatEntryPtr(sb, offset + 1, NULL, 0) << 8;
125 val |= *GetFatEntryPtr(sb, offset, NULL, 0);
127 else
128 val = AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, offset, NULL, 0)));
130 if (n & 1)
131 val >>= 4;
132 else
133 val &= 0xfff;
135 return val;
139 * FAT16 and FAT32, on the other hand, have nice neat entry widths, so simple
140 * word/long casts are fine. There's also no chance that the entry can be
141 * split across blocks. Why can't everything be this simple?
143 static ULONG GetFat16Entry(struct FSSuper *sb, ULONG n) {
144 return AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, n << 1, NULL, 0)));
147 static ULONG GetFat32Entry(struct FSSuper *sb, ULONG n) {
148 return AROS_LE2LONG(*((ULONG *) GetFatEntryPtr(sb, n << 2, NULL, 0)))
149 & 0x0fffffff;
152 static void SetFat12Entry(struct FSSuper *sb, ULONG n, ULONG val) {
153 APTR b;
154 ULONG offset = n + n/2;
155 BOOL boundary = FALSE;
156 UWORD *fat = NULL, newval, i;
158 for (i = 0; i < sb->fat_count; i++)
160 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
161 boundary = TRUE;
163 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
165 newval = *GetFatEntryPtr(sb, offset + 1, NULL, i) << 8;
166 newval |= *GetFatEntryPtr(sb, offset, NULL, i);
168 else {
169 fat = (UWORD *) GetFatEntryPtr(sb, offset, &b, i);
170 newval = AROS_LE2WORD(*fat);
173 if (n & 1) {
174 val <<= 4;
175 newval = (newval & 0xf) | val;
177 else {
178 newval = (newval & 0xf000) | val;
181 if (boundary) {
182 /* XXX ideally we'd mark both blocks dirty at the same time or
183 * only do it once if they're the same block. unfortunately any
184 * old value of b is invalid after a call to GetFatEntryPtr, as
185 * it may have swapped the previous cache out. This is probably
186 * safe enough. */
187 *GetFatEntryPtr(sb, offset+1, &b, i) = newval >> 8;
188 Cache_MarkBlockDirty(sb->cache, b);
189 *GetFatEntryPtr(sb, offset, &b, i) = newval & 0xff;
190 Cache_MarkBlockDirty(sb->cache, b);
192 else {
193 *fat = AROS_WORD2LE(newval);
194 Cache_MarkBlockDirty(sb->cache, b);
199 static void SetFat16Entry(struct FSSuper *sb, ULONG n, ULONG val) {
200 APTR b;
201 UWORD i;
203 for (i = 0; i < sb->fat_count; i++)
205 *((UWORD *) GetFatEntryPtr(sb, n << 1, &b, i)) =
206 AROS_WORD2LE((UWORD) val);
207 Cache_MarkBlockDirty(sb->cache, b);
211 static void SetFat32Entry(struct FSSuper *sb, ULONG n, ULONG val) {
212 APTR b;
213 ULONG *fat;
214 UWORD i;
216 for (i = 0; i < sb->fat_count; i++)
218 fat = (ULONG *) GetFatEntryPtr(sb, n << 2, &b, i);
220 *fat = (*fat & 0xf0000000) | val;
222 Cache_MarkBlockDirty(sb->cache, b);
226 LONG ReadFATSuper(struct FSSuper *sb ) {
227 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
228 LONG err;
229 ULONG bsize = de->de_SizeBlock * 4, total_sectors;
230 struct FATBootSector *boot;
231 struct FATEBPB *ebpb;
232 struct FATFSInfo *fsinfo;
233 BOOL invalid = FALSE;
234 ULONG end;
235 LONG i;
236 struct DirHandle dh;
237 struct DirEntry dir_entry;
238 APTR block_ref;
239 UBYTE *fat_block;
241 D(bug("[fat] reading boot sector\n"));
243 boot = AllocMem(bsize, MEMF_ANY);
244 if (!boot)
245 return ERROR_NO_FREE_STORE;
247 sb->first_device_sector =
248 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
250 /* Get a preliminary total-sectors value so we don't risk going outside
251 * partition limits */
252 sb->total_sectors =
253 de->de_BlocksPerTrack * de->de_Surfaces * (de->de_HighCyl + 1)
254 - sb->first_device_sector;
256 D(bug("[fat] boot sector at sector %ld\n", sb->first_device_sector));
259 * Read the boot sector. We go direct because we don't have a cache yet,
260 * and can't create one until we know the sector size, which is held in
261 * the boot sector. In practice it doesn't matter - we're going to use
262 * this once and once only.
264 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize, (UBYTE *)boot)) != 0) {
265 D(bug("[fat] couldn't read boot block (%ld)\n", err));
266 FreeMem(boot, bsize);
267 return err;
270 D(bug("\tBoot sector:\n"));
272 sb->sectorsize = AROS_LE2WORD(boot->bpb_bytes_per_sect);
273 sb->sectorsize_bits = log2(sb->sectorsize);
274 D(bug("\tSectorSize = %ld\n", sb->sectorsize));
275 D(bug("\tSectorSize Bits = %ld\n", sb->sectorsize_bits));
277 sb->cluster_sectors = boot->bpb_sect_per_clust;
278 sb->clustersize = sb->sectorsize * boot->bpb_sect_per_clust;
279 sb->clustersize_bits = log2(sb->clustersize);
280 sb->cluster_sectors_bits = sb->clustersize_bits - sb->sectorsize_bits;
282 D(bug("\tSectorsPerCluster = %ld\n", (ULONG)boot->bpb_sect_per_clust));
283 D(bug("\tClusterSize = %ld\n", sb->clustersize));
284 D(bug("\tClusterSize Bits = %ld\n", sb->clustersize_bits));
285 D(bug("\tCluster Sectors Bits = %ld\n", sb->cluster_sectors_bits));
287 sb->first_fat_sector = AROS_LE2WORD(boot->bpb_rsvd_sect_count);
288 D(bug("\tFirst FAT Sector = %ld\n", sb->first_fat_sector));
290 sb->fat_count = boot->bpb_num_fats;
291 D(bug("\tNumber of FATs = %d\n", sb->fat_count));
293 if (boot->bpb_fat_size_16 != 0)
294 sb->fat_size = AROS_LE2WORD(boot->bpb_fat_size_16);
295 else
296 sb->fat_size = AROS_LE2LONG(boot->ebpbs.ebpb32.bpb_fat_size_32);
297 D(bug("\tFAT Size = %ld\n", sb->fat_size));
299 if (boot->bpb_total_sectors_16 != 0)
300 total_sectors = AROS_LE2WORD(boot->bpb_total_sectors_16);
301 else
302 total_sectors = AROS_LE2LONG(boot->bpb_total_sectors_32);
303 D(bug("\tTotal Sectors = %ld\n", sb->total_sectors));
305 /* Check that the boot block's sector count is the same as the
306 * partition's sector count. This stops a resized partition being
307 * mounted before reformatting */
308 if (total_sectors != sb->total_sectors)
309 invalid = TRUE;
311 sb->rootdir_sectors = ((AROS_LE2WORD(boot->bpb_root_entries_count) * sizeof(struct FATDirEntry)) + (sb->sectorsize - 1)) >> sb->sectorsize_bits;
312 D(bug("\tRootDir Sectors = %ld\n", sb->rootdir_sectors));
314 sb->data_sectors = sb->total_sectors - (sb->first_fat_sector + (sb->fat_count * sb->fat_size) + sb->rootdir_sectors);
315 D(bug("\tData Sectors = %ld\n", sb->data_sectors));
317 sb->clusters_count = sb->data_sectors >> sb->cluster_sectors_bits;
318 D(bug("\tClusters Count = %ld\n", sb->clusters_count));
320 sb->first_rootdir_sector = sb->first_fat_sector + (sb->fat_count * sb->fat_size);
321 D(bug("\tFirst RootDir Sector = %ld\n", sb->first_rootdir_sector));
323 sb->first_data_sector = sb->first_fat_sector + (sb->fat_count * sb->fat_size) + sb->rootdir_sectors;
324 D(bug("\tFirst Data Sector = %ld\n", sb->first_data_sector));
326 /* check if disk is in fact a FAT filesystem */
328 /* valid sector size: 512, 1024, 2048, 4096 */
329 if (sb->sectorsize != 512 && sb->sectorsize != 1024 && sb->sectorsize != 2048 && sb->sectorsize != 4096)
330 invalid = TRUE;
332 /* valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
333 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)
334 invalid = TRUE;
336 /* valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
337 if (sb->clustersize > 64 * 1024)
338 invalid = TRUE;
340 if (sb->first_fat_sector == 0)
341 invalid = TRUE;
343 if (sb->fat_count == 0)
344 invalid = TRUE;
346 if (boot->bpb_media < 0xF0)
347 invalid = TRUE;
349 /* FAT "signature" */
350 if (boot->bpb_signature[0] != 0x55 || boot->bpb_signature[1] != 0xaa)
351 invalid = TRUE;
353 if (invalid) {
354 D(bug("\tInvalid FAT Boot Sector\n"));
355 FreeMem(boot, bsize);
356 return ERROR_NOT_A_DOS_DISK;
358 end = 0xFFFFFFFF / sb->sectorsize;
359 if ((sb->first_device_sector + sb->total_sectors - 1 > end) && (glob->readcmd == CMD_READ)) {
360 D(bug("\tDevice is too large\n"));
361 FreeMem(boot, bsize);
362 return IOERR_BADADDRESS;
365 sb->cache = Cache_CreateCache(64, 64, sb->sectorsize);
367 if (sb->clusters_count < 4085) {
368 D(bug("\tFAT12 filesystem detected\n"));
369 sb->type = 12;
370 sb->eoc_mark = 0x0FFF;
371 sb->func_get_fat_entry = GetFat12Entry;
372 sb->func_set_fat_entry = SetFat12Entry;
374 else if (sb->clusters_count < 65525) {
375 D(bug("\tFAT16 filesystem detected\n"));
376 sb->type = 16;
377 sb->eoc_mark = 0xFFFF;
378 sb->func_get_fat_entry = GetFat16Entry;
379 sb->func_set_fat_entry = SetFat16Entry;
381 else {
382 D(bug("\tFAT32 filesystem detected\n"));
383 sb->type = 32;
384 sb->eoc_mark = 0x0FFFFFFF;
385 sb->func_get_fat_entry = GetFat32Entry;
386 sb->func_set_fat_entry = SetFat32Entry;
388 glob->sb = sb;
390 /* setup the FAT cache and load the first blocks */
391 sb->fat_cachesize = 4096;
392 sb->fat_cachesize_bits = log2(sb->fat_cachesize);
393 sb->fat_cache_block = 0xffffffff;
395 sb->fat_blocks_count =
396 MIN(sb->fat_size, sb->fat_cachesize >> sb->sectorsize_bits);
397 sb->fat_blocks = AllocVecPooled(glob->mempool,
398 sizeof(APTR) * sb->fat_blocks_count);
399 sb->fat_buffers = AllocVecPooled(glob->mempool,
400 sizeof(APTR) * sb->fat_blocks_count);
402 if (sb->type != 32) { /* FAT 12/16 */
403 /* setup volume id */
404 sb->volume_id = AROS_LE2LONG(boot->ebpbs.ebpb.bs_volid);
406 /* location of root directory */
407 sb->rootdir_cluster = 0;
408 sb->rootdir_sector = sb->first_rootdir_sector;
409 ebpb = &boot->ebpbs.ebpb;
411 else {
412 /* setup volume id */
413 sb->volume_id = AROS_LE2LONG(boot->ebpbs.ebpb32.ebpb.bs_volid);
415 /* location of root directory */
416 sb->rootdir_cluster = AROS_LE2LONG(boot->ebpbs.ebpb32.bpb_root_cluster);
417 sb->rootdir_sector = 0;
418 ebpb = &boot->ebpbs.ebpb32.ebpb;
421 D(bug("[fat] rootdir at cluster %ld sector %ld\n", sb->rootdir_cluster, sb->rootdir_sector));
423 /* Initialise the root directory if this is a newly formatted volume */
424 if (glob->formatting)
426 /* Clear all FAT sectors */
427 for (i = 0; i < sb->fat_size * 2; i++) {
428 block_ref = Cache_GetBlock(sb->cache,
429 sb->first_device_sector + sb->first_fat_sector + i,
430 &fat_block);
431 memset(fat_block, 0, bsize);
432 if (i == 0) {
433 /* The first two entries are special */
434 if (sb->type == 32)
435 *(UQUAD *)fat_block = AROS_QUAD2LE(0x0FFFFFFF0FFFFFF8);
436 else if (sb->type == 16)
437 *(ULONG *)fat_block = AROS_LONG2LE(0xFFFFFFF8);
438 else
439 *(ULONG *)fat_block = AROS_LONG2LE(0x00FFFFF8);
441 Cache_MarkBlockDirty(sb->cache, block_ref);
442 Cache_FreeBlock(sb->cache, block_ref);
445 /* allocate first cluster of the root directory */
446 if (sb->type == 32)
447 AllocCluster(sb, sb->rootdir_cluster);
449 /* get a handle on the root directory */
450 InitDirHandle(sb, 0, &dh, FALSE);
452 /* clear all entries */
453 for (i = 0; GetDirEntry(&dh, i, &dir_entry) == 0; i++) {
454 memset(&dir_entry.e.entry, 0, sizeof(struct FATDirEntry));
455 UpdateDirEntry(&dir_entry);
458 SetVolumeName(sb, ebpb->bs_vollab, 11);
460 ReleaseDirHandle(&dh);
461 glob->formatting = FALSE;
462 D(bug("\tRoot dir created.\n"));
465 if (GetVolumeIdentity(sb, &(sb->volume)) != 0) {
466 LONG i;
467 UBYTE *uu = (void *)&sb->volume_id;
469 /* No volume name entry, so construct name from serial number */
470 for (i=1; i<10;) {
471 int d;
473 if (i==5)
474 sb->volume.name[i++]='-';
476 d = (*uu) & 0x0f;
477 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
478 d = ((*uu) & 0xf0)>>4;
479 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
481 uu++;
484 sb->volume.name[i] = '\0';
485 sb->volume.name[0] = 9;
488 /* get initial number of free clusters */
489 sb->free_clusters = -1;
490 sb->next_cluster = -1;
491 if (sb->type == 32) {
492 sb->fsinfo_block = Cache_GetBlock(sb->cache, sb->first_device_sector
493 + AROS_LE2WORD(boot->ebpbs.ebpb32.bpb_fs_info), (UBYTE **)&fsinfo);
494 if (sb->fsinfo_block != NULL) {
495 if (fsinfo->lead_sig == AROS_LONG2LE(FSI_LEAD_SIG)
496 && fsinfo->struct_sig == AROS_LONG2LE(FSI_STRUCT_SIG)
497 && fsinfo->trail_sig == AROS_LONG2LE(FSI_TRAIL_SIG)) {
498 sb->free_clusters = AROS_LE2LONG(fsinfo->free_count);
499 sb->next_cluster = AROS_LE2LONG(fsinfo->next_free);
500 D(bug("[fat] valid FATFSInfo block found\n"));
501 sb->fsinfo_buffer = fsinfo;
503 else
504 Cache_FreeBlock(sb->cache, sb->fsinfo_block);
507 if (sb->free_clusters == -1)
508 CountFreeClusters(sb);
509 if (sb->next_cluster == -1)
510 sb->next_cluster = 2;
512 D(bug("\tFAT Filesystem successfully detected.\n"));
513 D(bug("\tFree Clusters = %ld\n", sb->free_clusters));
514 D(bug("\tNext Free Cluster = %ld\n", sb->next_cluster));
515 FreeMem(boot, bsize);
516 return 0;
519 LONG GetVolumeIdentity(struct FSSuper *sb, struct VolumeIdentity *volume) {
520 struct DirHandle dh;
521 struct DirEntry de;
522 LONG err;
523 int i;
525 D(bug("[fat] searching root directory for volume name\n"));
527 /* search the directory for the volume id entry. it would've been nice to
528 * just use GetNextDirEntry but I didn't want a flag or something to tell
529 * it not to skip the volume name */
530 InitDirHandle(sb, sb->rootdir_cluster, &dh, FALSE);
532 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
534 /* match the volume id entry */
535 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
536 && de.e.entry.name[0] != 0xe5) {
537 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
539 /* copy the name in. volume->name is a BSTR */
541 volume->name[1] = de.e.entry.name[0];
543 for (i = 1; i < 11; i++) {
544 if (volume->name[i] == ' ')
545 volume->name[i+1] = de.e.entry.name[i];
546 else
547 volume->name[i+1] = tolower(de.e.entry.name[i]);
550 for (i = 10; volume->name[i+1] == ' '; i--);
551 volume->name[i+2] = '\0';
552 volume->name[0] = strlen(&(volume->name[1]));
554 /* get the volume creation date too */
555 ConvertFATDate(de.e.entry.create_date, de.e.entry.create_time, &volume->create_time);
557 D(bug("[fat] volume name is '%s'\n", &(volume->name[1])));
559 break;
562 /* bail out if we hit the end of the dir */
563 if (de.e.entry.name[0] == 0x00) {
564 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
565 err = ERROR_OBJECT_NOT_FOUND;
566 break;
570 ReleaseDirHandle(&dh);
571 return err;
574 LONG FormatFATVolume(const UBYTE *name, UWORD len) {
575 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
576 LONG err;
577 ULONG bsize = de->de_SizeBlock * 4;
578 struct FATBootSector *boot;
579 struct FATEBPB *ebpb;
580 struct FATFSInfo *fsinfo;
581 UWORD type, i, root_entries_count;
582 struct EClockVal eclock;
583 ULONG sectors_per_cluster = 0, sector_count, first_fat_sector,
584 fat_size, root_dir_sectors, first_device_sector, temp1, temp2;
586 /* Decide on FAT type based on number of sectors */
587 sector_count = (de->de_HighCyl - de->de_LowCyl + 1)
588 * de->de_Surfaces * de->de_BlocksPerTrack;
589 if (sector_count < 4085)
590 type = 12;
591 else if (sector_count < 1024 * 1024)
592 type = 16;
593 else
594 type = 32;
596 D(bug("[fat] writing boot sector\n"));
598 /* Decide on cluster size and root dir entries */
599 first_fat_sector = 1;
600 if (type == 12) {
601 if (sector_count == 1440) {
602 sectors_per_cluster = 2;
603 root_entries_count = 112;
604 } else if (sector_count == 2880) {
605 sectors_per_cluster = 1;
606 root_entries_count = 224;
607 } else if (sector_count == 5760) {
608 sectors_per_cluster = 2;
609 root_entries_count = 240;
610 } else {
611 /* We only support some common 3.5" floppy formats */
612 return ERROR_NOT_IMPLEMENTED;
614 } else if (type == 16) {
615 for (i = 0; fat16_cluster_thresholds[i] < sector_count; i++);
616 sectors_per_cluster = 1 << i;
617 root_entries_count = 512;
618 } else {
619 for (i = 0; fat32_cluster_thresholds[i] < sector_count; i++);
620 sectors_per_cluster = 8 << i;
621 root_entries_count = 0;
622 first_fat_sector = 32;
625 D(bug("\tFirst FAT Sector = %ld\n", first_fat_sector));
627 /* Determine FAT size */
628 root_dir_sectors = (root_entries_count * 32 + (bsize - 1)) / bsize;
629 temp1 = sector_count - (first_fat_sector + root_dir_sectors);
630 temp2 = 256 * sectors_per_cluster + 2;
631 if (type == 32)
632 temp2 /= 2;
633 fat_size = (temp1 + temp2 - 1) / temp2;
635 boot = AllocMem(bsize, MEMF_CLEAR);
636 if (!boot)
637 return ERROR_NO_FREE_STORE;
639 /* Install x86 infinite loop boot code to keep major OSes happy */
640 boot->bs_jmp_boot[0] = 0xEB;
641 boot->bs_jmp_boot[1] = 0xFE;
642 boot->bs_jmp_boot[2] = 0x90;
644 CopyMem(default_oem_name, boot->bs_oem_name, 8);
646 boot->bpb_bytes_per_sect = AROS_WORD2LE(bsize);
647 boot->bpb_sect_per_clust = sectors_per_cluster;
649 boot->bpb_rsvd_sect_count = AROS_WORD2LE(first_fat_sector);
651 boot->bpb_num_fats = 2;
653 boot->bpb_root_entries_count = AROS_WORD2LE(root_entries_count);
655 if (sector_count < 0x10000 && type != 32)
656 boot->bpb_total_sectors_16 = AROS_WORD2LE(sector_count);
657 else
658 boot->bpb_total_sectors_32 = AROS_LONG2LE(sector_count);
660 boot->bpb_media = 0xF8;
662 boot->bpb_sect_per_track = AROS_WORD2LE(de->de_BlocksPerTrack);
663 boot->bpb_num_heads = AROS_WORD2LE(de->de_Surfaces);
664 boot->bpb_hidden_sect = AROS_LONG2LE(de->de_Reserved);
666 if (type == 32) {
667 boot->ebpbs.ebpb32.bpb_fat_size_32 = AROS_LONG2LE(fat_size);
668 boot->ebpbs.ebpb32.bpb_root_cluster = AROS_LONG2LE(2);
669 boot->ebpbs.ebpb32.bpb_fs_info = AROS_WORD2LE(1);
670 boot->ebpbs.ebpb32.bpb_back_bootsec = AROS_WORD2LE(6);
671 ebpb = &boot->ebpbs.ebpb32.ebpb;
673 else {
674 boot->bpb_fat_size_16 = AROS_WORD2LE(fat_size);
675 ebpb = &boot->ebpbs.ebpb;
678 ebpb->bs_drvnum = 0x80;
679 ebpb->bs_bootsig = 0x29;
681 /* Generate a pseudo-random serial number. Not the original algorithm,
682 * but it shouldn't matter */
683 ReadEClock(&eclock);
684 ebpb->bs_volid = FastRand(eclock.ev_lo ^ eclock.ev_hi);
686 /* copy volume name in */
687 for (i = 0; i < 11; i++)
688 if (i < len)
689 ebpb->bs_vollab[i] = toupper(name[i]);
690 else
691 ebpb->bs_vollab[i] = ' ';
693 CopyMem(default_filsystype, ebpb->bs_filsystype, 8);
694 if (type != 16) {
695 if (type == 32)
696 ebpb->bs_filsystype[3] = '3';
697 ebpb->bs_filsystype[4] = '2';
700 boot->bpb_signature[0] = 0x55;
701 boot->bpb_signature[1] = 0xaa;
703 /* Write the boot sector */
704 first_device_sector =
705 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
707 D(bug("[fat] boot sector at sector %ld\n", first_device_sector));
709 if ((err = AccessDisk(TRUE, first_device_sector, 1, bsize, (UBYTE *)boot)) != 0) {
710 D(bug("[fat] couldn't write boot block (%ld)\n", err));
711 FreeMem(boot, bsize);
712 return err;
715 /* Write back-up boot sector and FS info sector */
716 if (type == 32) {
717 if ((err = AccessDisk(TRUE, first_device_sector + 6, 1, bsize,
718 (UBYTE *)boot)) != 0) {
719 D(bug("[fat] couldn't write back-up boot block (%ld)\n", err));
720 FreeMem(boot, bsize);
721 return err;
724 fsinfo = (APTR)boot;
725 memset(fsinfo, 0, bsize);
727 fsinfo->lead_sig = AROS_LONG2LE(FSI_LEAD_SIG);
728 fsinfo->struct_sig = AROS_LONG2LE(FSI_STRUCT_SIG);
729 fsinfo->trail_sig = AROS_LONG2LE(FSI_TRAIL_SIG);
730 fsinfo->free_count = AROS_LONG2LE(0xFFFFFFFF);
731 fsinfo->next_free = AROS_LONG2LE(0xFFFFFFFF);
733 if ((err = AccessDisk(TRUE, first_device_sector + 1, 1, bsize,
734 (UBYTE *)fsinfo)) != 0) {
735 D(bug("[fat] couldn't write back-up boot block (%ld)\n", err));
736 FreeMem(boot, bsize);
737 return err;
741 FreeMem(boot, bsize);
743 glob->formatting = TRUE;
745 return 0;
748 LONG SetVolumeName(struct FSSuper *sb, UBYTE *name, UWORD len) {
749 struct DirHandle dh;
750 struct DirEntry de;
751 LONG err;
752 int i;
753 struct DosEnvec *dosenv = BADDR(glob->fssm->fssm_Environ);
754 ULONG bsize = dosenv->de_SizeBlock * 4;
755 struct FATBootSector *boot;
757 /* read boot block */
758 boot = AllocMem(bsize, MEMF_ANY);
759 if (!boot)
760 return ERROR_NO_FREE_STORE;
762 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize, (UBYTE *)boot)) != 0) {
763 D(bug("[fat] couldn't read boot block (%ld)\n", err));
764 FreeMem(boot, bsize);
765 return err;
768 D(bug("[fat] searching root directory for volume name\n"));
770 /* search the directory for the volume id entry. it would've been nice to
771 * just use GetNextDirEntry but I didn't want a flag or something to tell
772 * it not to skip the volume name */
773 InitDirHandle(sb, 0, &dh, FALSE);
775 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
777 /* match the volume id entry */
778 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
779 && de.e.entry.name[0] != 0xe5) {
780 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
781 err = 0;
782 break;
785 /* bail out if we hit the end of the dir */
786 if (de.e.entry.name[0] == 0x00) {
787 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
788 err = ERROR_OBJECT_NOT_FOUND;
789 break;
793 /* create a new volume id entry if there wasn't one */
794 if (err != 0) {
795 err = AllocDirEntry(&dh, 0, &de);
796 if (err == 0)
797 FillDirEntry(&de, ATTR_VOLUME_ID, 0);
800 /* copy the name in */
801 if (err == 0) {
802 for (i = 0; i < 11; i++)
803 if (i < len)
804 de.e.entry.name[i] = toupper(name[i]);
805 else
806 de.e.entry.name[i] = ' ';
808 if ((err = UpdateDirEntry(&de)) != 0) {
809 D(bug("[fat] couldn't change volume name\n"));
810 return err;
814 /* copy name to boot block as well, and save */
815 if (sb->type == 32)
816 CopyMem(de.e.entry.name, boot->ebpbs.ebpb32.ebpb.bs_vollab, 11);
817 else
818 CopyMem(de.e.entry.name, boot->ebpbs.ebpb.bs_vollab, 11);
820 if ((err = AccessDisk(TRUE, sb->first_device_sector, 1, bsize,
821 (UBYTE *)boot)) != 0)
822 D(bug("[fat] couldn't write boot block (%ld)\n", err));
823 FreeMem(boot, bsize);
825 /* update name in sb */
826 sb->volume.name[0] = len <= 11 ? len : 11;
827 CopyMem(name, &(sb->volume.name[1]), sb->volume.name[0]);
828 sb->volume.name[sb->volume.name[0]+1] = '\0';
830 D(bug("[fat] new volume name is '%s'\n", &(sb->volume.name[1])));
832 ReleaseDirHandle(&dh);
833 return err;
836 LONG FindFreeCluster(struct FSSuper *sb, ULONG *rcluster) {
837 ULONG cluster = 0;
838 BOOL found = FALSE;
840 for (cluster = sb->next_cluster;
841 cluster < 2 + sb->clusters_count && !found;
842 cluster++)
844 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
846 *rcluster = cluster;
847 found = TRUE;
851 if (!found)
853 for (cluster = 2; cluster < sb->next_cluster && !found;
854 cluster++)
856 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
858 *rcluster = cluster;
859 found = TRUE;
864 if (!found) {
865 D(bug("[fat] no more free clusters, we're out of space\n"));
866 return ERROR_DISK_FULL;
869 sb->next_cluster = *rcluster;
871 D(bug("[fat] found free cluster %ld\n", *rcluster));
873 return 0;
876 void FreeFATSuper(struct FSSuper *sb) {
877 D(bug("\tRemoving Super Block from memory\n"));
878 Cache_DestroyCache(sb->cache);
879 FreeVecPooled(glob->mempool, sb->fat_buffers);
880 sb->fat_buffers = NULL;
881 FreeVecPooled(glob->mempool, sb->fat_blocks);
882 sb->fat_blocks = NULL;
885 /* see how many unused clusters are available */
886 void CountFreeClusters(struct FSSuper *sb) {
887 ULONG cluster = 0;
888 ULONG free = 0;
890 /* loop over all the data clusters */
891 for (cluster = 2; cluster < sb->clusters_count + 2; cluster++)
893 /* record the free ones */
894 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
895 free++;
898 /* put the value away for later */
899 sb->free_clusters = free;
901 D(bug("\tfree clusters: %ld\n", free));
904 void AllocCluster(struct FSSuper *sb, ULONG cluster) {
905 SET_NEXT_CLUSTER(sb, cluster, sb->eoc_mark);
906 sb->free_clusters--;
907 if (sb->fsinfo_buffer != NULL) {
908 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
909 sb->fsinfo_buffer->next_free = AROS_LONG2LE(sb->next_cluster);
910 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
914 void FreeCluster(struct FSSuper *sb, ULONG cluster) {
915 SET_NEXT_CLUSTER(sb, cluster, 0);
916 sb->free_clusters++;
917 if (sb->fsinfo_buffer != NULL) {
918 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
919 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
923 void ConvertFATDate(UWORD date, UWORD time, struct DateStamp *ds) {
924 ULONG year, month, day, hours, mins, secs;
925 struct ClockData clock_data;
927 /* date bits: yyyy yyym mmmd dddd */
928 year = (date & 0xfe00) >> 9; /* bits 15-9 */
929 month = (date & 0x01e0) >> 5; /* bits 8-5 */
930 day = date & 0x001f; /* bits 4-0 */
932 /* time bits: hhhh hmmm mmms ssss */
933 hours = (time & 0xf800) >> 11; /* bits 15-11 */
934 mins = (time & 0x07e0) >> 5; /* bits 10-5 */
935 secs = time & 0x001f; /* bits 4-0 */
937 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));
939 clock_data.year = 1980 + year;
940 clock_data.month = month;
941 clock_data.mday = day;
942 clock_data.hour = hours;
943 clock_data.min = mins;
944 clock_data.sec = secs << 1;
945 secs = Date2Amiga(&clock_data);
947 /* calculate days since 1978-01-01 (DOS epoch) */
948 ds->ds_Days = secs / (60 * 60 * 24);
950 /* minutes since midnight */
951 ds->ds_Minute = secs / 60 % (24 * 60);
953 /* 1/50 sec ticks since last minute */
954 ds->ds_Tick = secs % 60 * TICKS_PER_SECOND;
956 D(bug("[fat] converted fat date: days %ld minutes %ld ticks %ld\n", ds->ds_Days, ds->ds_Minute, ds->ds_Tick));
959 void ConvertAROSDate(struct DateStamp *ds, UWORD *date, UWORD *time) {
960 ULONG year, month, day, hours, mins, secs;
961 struct ClockData clock_data;
963 /* convert datestamp to seconds since 1978 */
964 secs = ds->ds_Days * 60 * 60 * 24 + ds->ds_Minute * 60
965 + ds->ds_Tick / TICKS_PER_SECOND;
967 /* Round up to next even second because of FAT's two-second granularity */
968 secs = (secs & ~1) + 2;
970 /* convert seconds since 1978 to calendar/time data */
971 Amiga2Date(secs, &clock_data);
973 /* get values used in FAT dates */
974 year = clock_data.year - 1980;
975 month = clock_data.month - 0;
976 day = clock_data.mday;
977 hours = clock_data.hour;
978 mins = clock_data.min;
979 secs = clock_data.sec >> 1;
981 /* all that remains is to bit-encode the whole lot */
983 /* date bits: yyyy yyym mmmd dddd */
984 *date = (((ULONG) year) << 9) | (((ULONG) month) << 5) | day;
986 /* time bits: hhhh hmmm mmms ssss */
987 *time = (((ULONG) hours) << 11) | (((ULONG) mins) << 5) | secs;