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.
13 #include <aros/macros.h>
14 #include <exec/errors.h>
15 #include <exec/types.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>
32 #include "fat_protos.h"
34 #define DEBUG DEBUG_MISC
37 static const UBYTE default_oem_name
[] = "MSWIN4.1";
38 static const UBYTE default_filsystype
[] = "FAT16 ";
40 static const ULONG fat16_cluster_thresholds
[] =
50 static const ULONG fat32_cluster_thresholds
[] =
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
,
62 ULONG entry_cache_block
= offset
>> sb
->fat_cachesize_bits
;
63 ULONG entry_cache_offset
= offset
& (sb
->fat_cachesize
- 1);
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
]);
76 sb
->fat_cache_block
= 0xffffffff;
80 num
= sb
->first_device_sector
+ sb
->first_fat_sector
81 + sb
->fat_size
* fat_no
+ (entry_cache_block
82 << (sb
->fat_cachesize_bits
- sb
->sectorsize_bits
));
83 for (i
= 0; i
< sb
->fat_blocks_count
; i
++) {
87 Cache_GetBlock(sb
->cache
, num
+ i
, &sb
->fat_buffers
[i
], &ioerr
);
89 /* FIXME: Handle IO errors on cache read! */
90 if (sb
->fat_blocks
[i
] == NULL
) {
92 Cache_FreeBlock(sb
->cache
, sb
->fat_blocks
[i
]);
97 /* remember where we are for next time */
98 sb
->fat_cache_block
= entry_cache_block
;
99 sb
->fat_cache_no
= fat_no
;
102 /* give the block back if they asked for it (needed to mark the block
103 * dirty if they're writing) */
105 *rb
= sb
->fat_blocks
[entry_cache_offset
>> sb
->sectorsize_bits
];
107 /* compute the pointer location and return it */
108 return sb
->fat_buffers
[entry_cache_offset
>> sb
->sectorsize_bits
] +
109 (entry_cache_offset
& (sb
->sectorsize
- 1));
112 /* FAT12 has, as the name suggests, 12-bit FAT entries. This means that two
113 * entries are condensed into three bytes, like so:
115 * entry: aaaaaaaa aaaabbbb bbbbbbbb
116 * bytes: xxxxxxxx xxxxxxxx xxxxxxxx
118 * To get at the entry we want, we find and grab the word starting at either
119 * byte 0 or 1 of the three-byte set, then shift up or down as needed. FATdoc
120 * 1.03 p16-17 describes the method
122 * The only tricky bit is if the word falls such that the first byte is the
123 * last byte of the block and the second byte is the first byte of the next
124 * block. Since our block data are stored within cache block structures, a
125 * simple cast won't do (hell, the second block may not even be in memory if
126 * we're at the end of the FAT cache). So we get it a byte at a time, and
127 * build the word ourselves.
129 static ULONG
GetFat12Entry(struct FSSuper
*sb
, ULONG n
) {
130 ULONG offset
= n
+ n
/2;
133 if ((offset
& (sb
->sectorsize
-1)) == sb
->sectorsize
-1) {
134 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
136 val
= *GetFatEntryPtr(sb
, offset
+ 1, NULL
, 0) << 8;
137 val
|= *GetFatEntryPtr(sb
, offset
, NULL
, 0);
140 val
= AROS_LE2WORD(*((UWORD
*) GetFatEntryPtr(sb
, offset
, NULL
, 0)));
151 * FAT16 and FAT32, on the other hand, have nice neat entry widths, so simple
152 * word/long casts are fine. There's also no chance that the entry can be
153 * split across blocks. Why can't everything be this simple?
155 static ULONG
GetFat16Entry(struct FSSuper
*sb
, ULONG n
) {
156 return AROS_LE2WORD(*((UWORD
*) GetFatEntryPtr(sb
, n
<< 1, NULL
, 0)));
159 static ULONG
GetFat32Entry(struct FSSuper
*sb
, ULONG n
) {
160 return AROS_LE2LONG(*((ULONG
*) GetFatEntryPtr(sb
, n
<< 2, NULL
, 0)))
164 static void SetFat12Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
) {
166 ULONG offset
= n
+ n
/2;
167 BOOL boundary
= FALSE
;
168 UWORD
*fat
= NULL
, newval
, i
;
170 for (i
= 0; i
< sb
->fat_count
; i
++)
172 if ((offset
& (sb
->sectorsize
-1)) == sb
->sectorsize
-1) {
175 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
177 newval
= *GetFatEntryPtr(sb
, offset
+ 1, NULL
, i
) << 8;
178 newval
|= *GetFatEntryPtr(sb
, offset
, NULL
, i
);
181 fat
= (UWORD
*) GetFatEntryPtr(sb
, offset
, &b
, i
);
182 newval
= AROS_LE2WORD(*fat
);
187 newval
= (newval
& 0xf) | val
;
190 newval
= (newval
& 0xf000) | val
;
194 /* XXX ideally we'd mark both blocks dirty at the same time or
195 * only do it once if they're the same block. unfortunately any
196 * old value of b is invalid after a call to GetFatEntryPtr, as
197 * it may have swapped the previous cache out. This is probably
199 *GetFatEntryPtr(sb
, offset
+1, &b
, i
) = newval
>> 8;
200 Cache_MarkBlockDirty(sb
->cache
, b
);
201 *GetFatEntryPtr(sb
, offset
, &b
, i
) = newval
& 0xff;
202 Cache_MarkBlockDirty(sb
->cache
, b
);
205 *fat
= AROS_WORD2LE(newval
);
206 Cache_MarkBlockDirty(sb
->cache
, b
);
211 static void SetFat16Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
) {
215 for (i
= 0; i
< sb
->fat_count
; i
++)
217 *((UWORD
*) GetFatEntryPtr(sb
, n
<< 1, &b
, i
)) =
218 AROS_WORD2LE((UWORD
) val
);
219 Cache_MarkBlockDirty(sb
->cache
, b
);
223 static void SetFat32Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
) {
228 for (i
= 0; i
< sb
->fat_count
; i
++)
230 fat
= (ULONG
*) GetFatEntryPtr(sb
, n
<< 2, &b
, i
);
232 *fat
= (*fat
& 0xf0000000) | val
;
234 Cache_MarkBlockDirty(sb
->cache
, b
);
238 LONG
ReadFATSuper(struct FSSuper
*sb
) {
239 struct Globals
*glob
= sb
->glob
;
240 struct DosEnvec
*de
= BADDR(glob
->fssm
->fssm_Environ
);
242 ULONG bsize
= de
->de_SizeBlock
* 4, total_sectors
;
243 struct FATBootSector
*boot
;
244 struct FATEBPB
*ebpb
;
245 struct FATFSInfo
*fsinfo
;
246 BOOL invalid
= FALSE
;
250 struct DirEntry dir_entry
;
254 D(bug("[fat] reading boot sector\n"));
256 boot
= AllocMem(bsize
, MEMF_ANY
);
258 return ERROR_NO_FREE_STORE
;
260 sb
->first_device_sector
=
261 de
->de_BlocksPerTrack
* de
->de_Surfaces
* de
->de_LowCyl
;
263 /* Get a preliminary total-sectors value so we don't risk going outside
264 * partition limits */
266 de
->de_BlocksPerTrack
* de
->de_Surfaces
* (de
->de_HighCyl
+ 1)
267 - sb
->first_device_sector
;
269 D(bug("[fat] boot sector at sector %ld\n", sb
->first_device_sector
));
272 * Read the boot sector. We go direct because we don't have a cache yet,
273 * and can't create one until we know the sector size, which is held in
274 * the boot sector. In practice it doesn't matter - we're going to use
275 * this once and once only.
277 if ((err
= AccessDisk(FALSE
, sb
->first_device_sector
, 1, bsize
, (UBYTE
*)boot
, glob
)) != 0) {
278 D(bug("[fat] couldn't read boot block (%ld)\n", err
));
279 FreeMem(boot
, bsize
);
283 D(bug("\tBoot sector:\n"));
285 sb
->sectorsize
= AROS_LE2WORD(boot
->bpb_bytes_per_sect
);
286 sb
->sectorsize_bits
= log2(sb
->sectorsize
);
287 D(bug("\tSectorSize = %ld\n", sb
->sectorsize
));
288 D(bug("\tSectorSize Bits = %ld\n", sb
->sectorsize_bits
));
290 sb
->cluster_sectors
= boot
->bpb_sect_per_clust
;
291 sb
->clustersize
= sb
->sectorsize
* boot
->bpb_sect_per_clust
;
292 sb
->clustersize_bits
= log2(sb
->clustersize
);
293 sb
->cluster_sectors_bits
= sb
->clustersize_bits
- sb
->sectorsize_bits
;
295 D(bug("\tSectorsPerCluster = %ld\n", (ULONG
)boot
->bpb_sect_per_clust
));
296 D(bug("\tClusterSize = %ld\n", sb
->clustersize
));
297 D(bug("\tClusterSize Bits = %ld\n", sb
->clustersize_bits
));
298 D(bug("\tCluster Sectors Bits = %ld\n", sb
->cluster_sectors_bits
));
300 sb
->first_fat_sector
= AROS_LE2WORD(boot
->bpb_rsvd_sect_count
);
301 D(bug("\tFirst FAT Sector = %ld\n", sb
->first_fat_sector
));
303 sb
->fat_count
= boot
->bpb_num_fats
;
304 D(bug("\tNumber of FATs = %d\n", sb
->fat_count
));
306 if (boot
->bpb_fat_size_16
!= 0)
307 sb
->fat_size
= AROS_LE2WORD(boot
->bpb_fat_size_16
);
309 sb
->fat_size
= AROS_LE2LONG(boot
->ebpbs
.ebpb32
.bpb_fat_size_32
);
310 D(bug("\tFAT Size = %ld\n", sb
->fat_size
));
312 if (boot
->bpb_total_sectors_16
!= 0)
313 total_sectors
= AROS_LE2WORD(boot
->bpb_total_sectors_16
);
315 total_sectors
= AROS_LE2LONG(boot
->bpb_total_sectors_32
);
316 D(bug("\tTotal Sectors = %ld\n", sb
->total_sectors
));
318 /* Check that the boot block's sector count is the same as the
319 * partition's sector count. This stops a resized partition being
320 * mounted before reformatting */
321 if (total_sectors
!= sb
->total_sectors
)
324 sb
->rootdir_sectors
= ((AROS_LE2WORD(boot
->bpb_root_entries_count
) * sizeof(struct FATDirEntry
)) + (sb
->sectorsize
- 1)) >> sb
->sectorsize_bits
;
325 D(bug("\tRootDir Sectors = %ld\n", sb
->rootdir_sectors
));
327 sb
->data_sectors
= sb
->total_sectors
- (sb
->first_fat_sector
+ (sb
->fat_count
* sb
->fat_size
) + sb
->rootdir_sectors
);
328 D(bug("\tData Sectors = %ld\n", sb
->data_sectors
));
330 sb
->clusters_count
= sb
->data_sectors
>> sb
->cluster_sectors_bits
;
331 D(bug("\tClusters Count = %ld\n", sb
->clusters_count
));
333 sb
->first_rootdir_sector
= sb
->first_fat_sector
+ (sb
->fat_count
* sb
->fat_size
);
334 D(bug("\tFirst RootDir Sector = %ld\n", sb
->first_rootdir_sector
));
336 sb
->first_data_sector
= sb
->first_fat_sector
+ (sb
->fat_count
* sb
->fat_size
) + sb
->rootdir_sectors
;
337 D(bug("\tFirst Data Sector = %ld\n", sb
->first_data_sector
));
339 /* check if disk is in fact a FAT filesystem */
341 /* valid sector size: 512, 1024, 2048, 4096 */
342 if (sb
->sectorsize
!= 512 && sb
->sectorsize
!= 1024 && sb
->sectorsize
!= 2048 && sb
->sectorsize
!= 4096)
345 /* valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
346 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)
349 /* valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
350 if (sb
->clustersize
> 64 * 1024)
353 if (sb
->first_fat_sector
== 0)
356 if (sb
->fat_count
== 0)
359 if (boot
->bpb_media
< 0xF0)
362 /* FAT "signature" */
363 if (boot
->bpb_signature
[0] != 0x55 || boot
->bpb_signature
[1] != 0xaa)
367 D(bug("\tInvalid FAT Boot Sector\n"));
368 FreeMem(boot
, bsize
);
369 return ERROR_NOT_A_DOS_DISK
;
371 end
= 0xFFFFFFFF / sb
->sectorsize
;
372 if ((sb
->first_device_sector
+ sb
->total_sectors
- 1 > end
) && (glob
->readcmd
== CMD_READ
)) {
373 D(bug("\tDevice is too large\n"));
374 FreeMem(boot
, bsize
);
375 return IOERR_BADADDRESS
;
378 sb
->cache
= Cache_CreateCache(glob
, 64, 64, sb
->sectorsize
, SysBase
);
380 if (sb
->clusters_count
< 4085) {
381 D(bug("\tFAT12 filesystem detected\n"));
383 sb
->eoc_mark
= 0x0FFF;
384 sb
->func_get_fat_entry
= GetFat12Entry
;
385 sb
->func_set_fat_entry
= SetFat12Entry
;
387 else if (sb
->clusters_count
< 65525) {
388 D(bug("\tFAT16 filesystem detected\n"));
390 sb
->eoc_mark
= 0xFFFF;
391 sb
->func_get_fat_entry
= GetFat16Entry
;
392 sb
->func_set_fat_entry
= SetFat16Entry
;
395 D(bug("\tFAT32 filesystem detected\n"));
397 sb
->eoc_mark
= 0x0FFFFFFF;
398 sb
->func_get_fat_entry
= GetFat32Entry
;
399 sb
->func_set_fat_entry
= SetFat32Entry
;
403 /* setup the FAT cache and load the first blocks */
404 sb
->fat_cachesize
= 4096;
405 sb
->fat_cachesize_bits
= log2(sb
->fat_cachesize
);
406 sb
->fat_cache_block
= 0xffffffff;
408 sb
->fat_blocks_count
=
409 MIN(sb
->fat_size
, sb
->fat_cachesize
>> sb
->sectorsize_bits
);
410 sb
->fat_blocks
= AllocVecPooled(glob
->mempool
,
411 sizeof(APTR
) * sb
->fat_blocks_count
);
412 sb
->fat_buffers
= AllocVecPooled(glob
->mempool
,
413 sizeof(APTR
) * sb
->fat_blocks_count
);
415 if (sb
->type
!= 32) { /* FAT 12/16 */
416 /* setup volume id */
417 sb
->volume_id
= AROS_LE2LONG(boot
->ebpbs
.ebpb
.bs_volid
);
419 /* location of root directory */
420 sb
->rootdir_cluster
= 0;
421 sb
->rootdir_sector
= sb
->first_rootdir_sector
;
422 ebpb
= &boot
->ebpbs
.ebpb
;
425 /* setup volume id */
426 sb
->volume_id
= AROS_LE2LONG(boot
->ebpbs
.ebpb32
.ebpb
.bs_volid
);
428 /* location of root directory */
429 sb
->rootdir_cluster
= AROS_LE2LONG(boot
->ebpbs
.ebpb32
.bpb_root_cluster
);
430 sb
->rootdir_sector
= 0;
431 ebpb
= &boot
->ebpbs
.ebpb32
.ebpb
;
434 D(bug("[fat] rootdir at cluster %ld sector %ld\n", sb
->rootdir_cluster
, sb
->rootdir_sector
));
436 /* Initialise the root directory if this is a newly formatted volume */
437 if (glob
->formatting
)
439 /* Clear all FAT sectors */
440 for (i
= 0; i
< sb
->fat_size
* 2; i
++) {
442 block_ref
= Cache_GetBlock(sb
->cache
,
443 sb
->first_device_sector
+ sb
->first_fat_sector
+ i
,
445 /* FIXME: Handle IO errors on cache read! */
446 memset(fat_block
, 0, bsize
);
448 /* The first two entries are special */
450 *(UQUAD
*)fat_block
= AROS_QUAD2LE(0x0FFFFFFF0FFFFFF8);
451 else if (sb
->type
== 16)
452 *(ULONG
*)fat_block
= AROS_LONG2LE(0xFFFFFFF8);
454 *(ULONG
*)fat_block
= AROS_LONG2LE(0x00FFFFF8);
456 Cache_MarkBlockDirty(sb
->cache
, block_ref
);
457 Cache_FreeBlock(sb
->cache
, block_ref
);
460 /* allocate first cluster of the root directory */
462 AllocCluster(sb
, sb
->rootdir_cluster
);
464 /* get a handle on the root directory */
465 InitDirHandle(sb
, 0, &dh
, FALSE
);
467 /* clear all entries */
468 for (i
= 0; GetDirEntry(&dh
, i
, &dir_entry
) == 0; i
++) {
469 memset(&dir_entry
.e
.entry
, 0, sizeof(struct FATDirEntry
));
470 UpdateDirEntry(&dir_entry
);
473 SetVolumeName(sb
, ebpb
->bs_vollab
, 11);
475 ReleaseDirHandle(&dh
);
476 glob
->formatting
= FALSE
;
477 D(bug("\tRoot dir created.\n"));
480 if (GetVolumeIdentity(sb
, &(sb
->volume
)) != 0) {
482 UBYTE
*uu
= (void *)&sb
->volume_id
;
484 /* No volume name entry, so construct name from serial number */
489 sb
->volume
.name
[i
++]='-';
492 sb
->volume
.name
[i
++] = (d
< 10) ? '0' + d
: 'A' - 10 + d
;
493 d
= ((*uu
) & 0xf0)>>4;
494 sb
->volume
.name
[i
++] = (d
< 10) ? '0' + d
: 'A' - 10 + d
;
499 sb
->volume
.name
[i
] = '\0';
500 sb
->volume
.name
[0] = 9;
503 /* get initial number of free clusters */
504 sb
->free_clusters
= -1;
505 sb
->next_cluster
= -1;
506 if (sb
->type
== 32) {
508 sb
->fsinfo_block
= Cache_GetBlock(sb
->cache
, sb
->first_device_sector
509 + AROS_LE2WORD(boot
->ebpbs
.ebpb32
.bpb_fs_info
), (UBYTE
**)&fsinfo
,
511 if (sb
->fsinfo_block
!= NULL
) {
512 if (fsinfo
->lead_sig
== AROS_LONG2LE(FSI_LEAD_SIG
)
513 && fsinfo
->struct_sig
== AROS_LONG2LE(FSI_STRUCT_SIG
)
514 && fsinfo
->trail_sig
== AROS_LONG2LE(FSI_TRAIL_SIG
)) {
515 sb
->free_clusters
= AROS_LE2LONG(fsinfo
->free_count
);
516 sb
->next_cluster
= AROS_LE2LONG(fsinfo
->next_free
);
517 D(bug("[fat] valid FATFSInfo block found\n"));
518 sb
->fsinfo_buffer
= fsinfo
;
521 Cache_FreeBlock(sb
->cache
, sb
->fsinfo_block
);
523 /* FIXME: Report IO errors to the user! */
526 if (sb
->free_clusters
== -1)
527 CountFreeClusters(sb
);
528 if (sb
->next_cluster
== -1)
529 sb
->next_cluster
= 2;
531 D(bug("\tFAT Filesystem successfully detected.\n"));
532 D(bug("\tFree Clusters = %ld\n", sb
->free_clusters
));
533 D(bug("\tNext Free Cluster = %ld\n", sb
->next_cluster
));
534 FreeMem(boot
, bsize
);
538 LONG
GetVolumeIdentity(struct FSSuper
*sb
, struct VolumeIdentity
*volume
) {
539 struct Globals
*glob
= sb
->glob
;
545 D(bug("[fat] searching root directory for volume name\n"));
547 /* search the directory for the volume id entry. it would've been nice to
548 * just use GetNextDirEntry but I didn't want a flag or something to tell
549 * it not to skip the volume name */
550 InitDirHandle(sb
, sb
->rootdir_cluster
, &dh
, FALSE
);
552 while ((err
= GetDirEntry(&dh
, dh
.cur_index
+ 1, &de
)) == 0) {
554 /* match the volume id entry */
555 if ((de
.e
.entry
.attr
& ATTR_VOLUME_ID_MASK
) == ATTR_VOLUME_ID
556 && de
.e
.entry
.name
[0] != 0xe5) {
557 D(bug("[fat] found volume id entry %ld\n", dh
.cur_index
));
559 /* copy the name in. volume->name is a BSTR */
561 volume
->name
[1] = de
.e
.entry
.name
[0];
563 for (i
= 1; i
< 11; i
++) {
564 if (volume
->name
[i
] == ' ')
565 volume
->name
[i
+1] = de
.e
.entry
.name
[i
];
567 volume
->name
[i
+1] = tolower(de
.e
.entry
.name
[i
]);
570 for (i
= 10; volume
->name
[i
+1] == ' '; i
--);
571 volume
->name
[i
+2] = '\0';
572 volume
->name
[0] = strlen(&(volume
->name
[1]));
574 /* get the volume creation date too */
575 ConvertFATDate(de
.e
.entry
.create_date
, de
.e
.entry
.create_time
, &volume
->create_time
, glob
);
577 D(bug("[fat] volume name is '%s'\n", &(volume
->name
[1])));
582 /* bail out if we hit the end of the dir */
583 if (de
.e
.entry
.name
[0] == 0x00) {
584 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
585 err
= ERROR_OBJECT_NOT_FOUND
;
590 ReleaseDirHandle(&dh
);
594 LONG
FormatFATVolume(const UBYTE
*name
, UWORD len
, struct Globals
*glob
) {
595 struct DosEnvec
*de
= BADDR(glob
->fssm
->fssm_Environ
);
597 ULONG bsize
= de
->de_SizeBlock
* 4;
598 struct FATBootSector
*boot
;
599 struct FATEBPB
*ebpb
;
600 struct FATFSInfo
*fsinfo
;
601 UWORD type
, i
, root_entries_count
;
602 struct EClockVal eclock
;
603 ULONG sectors_per_cluster
= 0, sector_count
, first_fat_sector
,
604 fat_size
, root_dir_sectors
, first_device_sector
, temp1
, temp2
;
606 /* Decide on FAT type based on number of sectors */
607 sector_count
= (de
->de_HighCyl
- de
->de_LowCyl
+ 1)
608 * de
->de_Surfaces
* de
->de_BlocksPerTrack
;
609 if (sector_count
< 4085)
611 else if (sector_count
< 1024 * 1024)
616 D(bug("[fat] writing boot sector\n"));
618 /* Decide on cluster size and root dir entries */
619 first_fat_sector
= 1;
621 if (sector_count
== 1440) {
622 sectors_per_cluster
= 2;
623 root_entries_count
= 112;
624 } else if (sector_count
== 2880) {
625 sectors_per_cluster
= 1;
626 root_entries_count
= 224;
627 } else if (sector_count
== 5760) {
628 sectors_per_cluster
= 2;
629 root_entries_count
= 240;
631 /* We only support some common 3.5" floppy formats */
632 return ERROR_NOT_IMPLEMENTED
;
634 } else if (type
== 16) {
635 for (i
= 0; fat16_cluster_thresholds
[i
] < sector_count
; i
++);
636 sectors_per_cluster
= 1 << i
;
637 root_entries_count
= 512;
639 for (i
= 0; fat32_cluster_thresholds
[i
] < sector_count
; i
++);
640 sectors_per_cluster
= 8 << i
;
641 root_entries_count
= 0;
642 first_fat_sector
= 32;
645 D(bug("\tFirst FAT Sector = %ld\n", first_fat_sector
));
647 /* Determine FAT size */
648 root_dir_sectors
= (root_entries_count
* 32 + (bsize
- 1)) / bsize
;
649 temp1
= sector_count
- (first_fat_sector
+ root_dir_sectors
);
650 temp2
= 256 * sectors_per_cluster
+ 2;
653 fat_size
= (temp1
+ temp2
- 1) / temp2
;
655 boot
= AllocMem(bsize
, MEMF_CLEAR
);
657 return ERROR_NO_FREE_STORE
;
659 /* Install x86 infinite loop boot code to keep major OSes happy */
660 boot
->bs_jmp_boot
[0] = 0xEB;
661 boot
->bs_jmp_boot
[1] = 0xFE;
662 boot
->bs_jmp_boot
[2] = 0x90;
664 CopyMem(default_oem_name
, boot
->bs_oem_name
, 8);
666 boot
->bpb_bytes_per_sect
= AROS_WORD2LE(bsize
);
667 boot
->bpb_sect_per_clust
= sectors_per_cluster
;
669 boot
->bpb_rsvd_sect_count
= AROS_WORD2LE(first_fat_sector
);
671 boot
->bpb_num_fats
= 2;
673 boot
->bpb_root_entries_count
= AROS_WORD2LE(root_entries_count
);
675 if (sector_count
< 0x10000 && type
!= 32)
676 boot
->bpb_total_sectors_16
= AROS_WORD2LE(sector_count
);
678 boot
->bpb_total_sectors_32
= AROS_LONG2LE(sector_count
);
680 boot
->bpb_media
= 0xF8;
682 boot
->bpb_sect_per_track
= AROS_WORD2LE(de
->de_BlocksPerTrack
);
683 boot
->bpb_num_heads
= AROS_WORD2LE(de
->de_Surfaces
);
684 boot
->bpb_hidden_sect
= AROS_LONG2LE(de
->de_Reserved
);
687 boot
->ebpbs
.ebpb32
.bpb_fat_size_32
= AROS_LONG2LE(fat_size
);
688 boot
->ebpbs
.ebpb32
.bpb_root_cluster
= AROS_LONG2LE(2);
689 boot
->ebpbs
.ebpb32
.bpb_fs_info
= AROS_WORD2LE(1);
690 boot
->ebpbs
.ebpb32
.bpb_back_bootsec
= AROS_WORD2LE(6);
691 ebpb
= &boot
->ebpbs
.ebpb32
.ebpb
;
694 boot
->bpb_fat_size_16
= AROS_WORD2LE(fat_size
);
695 ebpb
= &boot
->ebpbs
.ebpb
;
698 ebpb
->bs_drvnum
= 0x80;
699 ebpb
->bs_bootsig
= 0x29;
701 /* Generate a pseudo-random serial number. Not the original algorithm,
702 * but it shouldn't matter */
704 ebpb
->bs_volid
= FastRand(eclock
.ev_lo
^ eclock
.ev_hi
);
706 /* copy volume name in */
707 for (i
= 0; i
< 11; i
++)
709 ebpb
->bs_vollab
[i
] = toupper(name
[i
]);
711 ebpb
->bs_vollab
[i
] = ' ';
713 CopyMem(default_filsystype
, ebpb
->bs_filsystype
, 8);
716 ebpb
->bs_filsystype
[3] = '3';
717 ebpb
->bs_filsystype
[4] = '2';
720 boot
->bpb_signature
[0] = 0x55;
721 boot
->bpb_signature
[1] = 0xaa;
723 /* Write the boot sector */
724 first_device_sector
=
725 de
->de_BlocksPerTrack
* de
->de_Surfaces
* de
->de_LowCyl
;
727 D(bug("[fat] boot sector at sector %ld\n", first_device_sector
));
729 if ((err
= AccessDisk(TRUE
, first_device_sector
, 1, bsize
, (UBYTE
*)boot
, glob
)) != 0) {
730 D(bug("[fat] couldn't write boot block (%ld)\n", err
));
731 FreeMem(boot
, bsize
);
735 /* Write back-up boot sector and FS info sector */
737 if ((err
= AccessDisk(TRUE
, first_device_sector
+ 6, 1, bsize
,
738 (UBYTE
*)boot
, glob
)) != 0) {
739 D(bug("[fat] couldn't write back-up boot block (%ld)\n", err
));
740 FreeMem(boot
, bsize
);
745 memset(fsinfo
, 0, bsize
);
747 fsinfo
->lead_sig
= AROS_LONG2LE(FSI_LEAD_SIG
);
748 fsinfo
->struct_sig
= AROS_LONG2LE(FSI_STRUCT_SIG
);
749 fsinfo
->trail_sig
= AROS_LONG2LE(FSI_TRAIL_SIG
);
750 fsinfo
->free_count
= AROS_LONG2LE(0xFFFFFFFF);
751 fsinfo
->next_free
= AROS_LONG2LE(0xFFFFFFFF);
753 if ((err
= AccessDisk(TRUE
, first_device_sector
+ 1, 1, bsize
,
754 (UBYTE
*)fsinfo
, glob
)) != 0) {
755 D(bug("[fat] couldn't write back-up boot block (%ld)\n", err
));
756 FreeMem(boot
, bsize
);
761 FreeMem(boot
, bsize
);
763 glob
->formatting
= TRUE
;
768 LONG
SetVolumeName(struct FSSuper
*sb
, UBYTE
*name
, UWORD len
) {
769 struct Globals
*glob
= sb
->glob
;
774 struct DosEnvec
*dosenv
= BADDR(glob
->fssm
->fssm_Environ
);
775 ULONG bsize
= dosenv
->de_SizeBlock
* 4;
776 struct FATBootSector
*boot
;
778 /* truncate name if necessary */
779 if (len
> FAT_MAX_SHORT_NAME
)
780 len
= FAT_MAX_SHORT_NAME
;
782 /* read boot block */
783 boot
= AllocMem(bsize
, MEMF_ANY
);
785 return ERROR_NO_FREE_STORE
;
787 if ((err
= AccessDisk(FALSE
, sb
->first_device_sector
, 1, bsize
, (UBYTE
*)boot
, glob
)) != 0) {
788 D(bug("[fat] couldn't read boot block (%ld)\n", err
));
789 FreeMem(boot
, bsize
);
793 D(bug("[fat] searching root directory for volume name\n"));
795 /* search the directory for the volume id entry. it would've been nice to
796 * just use GetNextDirEntry but I didn't want a flag or something to tell
797 * it not to skip the volume name */
798 InitDirHandle(sb
, 0, &dh
, FALSE
);
800 while ((err
= GetDirEntry(&dh
, dh
.cur_index
+ 1, &de
)) == 0) {
802 /* match the volume id entry */
803 if ((de
.e
.entry
.attr
& ATTR_VOLUME_ID_MASK
) == ATTR_VOLUME_ID
804 && de
.e
.entry
.name
[0] != 0xe5) {
805 D(bug("[fat] found volume id entry %ld\n", dh
.cur_index
));
810 /* bail out if we hit the end of the dir */
811 if (de
.e
.entry
.name
[0] == 0x00) {
812 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
813 err
= ERROR_OBJECT_NOT_FOUND
;
818 /* create a new volume id entry if there wasn't one */
820 err
= AllocDirEntry(&dh
, 0, &de
);
822 FillDirEntry(&de
, ATTR_VOLUME_ID
, 0);
825 /* copy the name in */
827 for (i
= 0; i
< FAT_MAX_SHORT_NAME
; i
++)
829 de
.e
.entry
.name
[i
] = toupper(name
[i
]);
831 de
.e
.entry
.name
[i
] = ' ';
833 if ((err
= UpdateDirEntry(&de
)) != 0) {
834 D(bug("[fat] couldn't change volume name\n"));
839 /* copy name to boot block as well, and save */
841 CopyMem(de
.e
.entry
.name
, boot
->ebpbs
.ebpb32
.ebpb
.bs_vollab
,
844 CopyMem(de
.e
.entry
.name
, boot
->ebpbs
.ebpb
.bs_vollab
,
847 if ((err
= AccessDisk(TRUE
, sb
->first_device_sector
, 1, bsize
,
848 (UBYTE
*)boot
, glob
)) != 0)
849 D(bug("[fat] couldn't write boot block (%ld)\n", err
));
850 FreeMem(boot
, bsize
);
852 /* update name in sb */
853 sb
->volume
.name
[0] = len
;
854 sb
->volume
.name
[1] = toupper(name
[0]);
855 for (i
= 1; i
< len
; i
++)
856 sb
->volume
.name
[i
+ 1] = tolower(name
[i
]);
857 sb
->volume
.name
[len
+ 1] = '\0';
859 D(bug("[fat] new volume name is '%s'\n", &(sb
->volume
.name
[1])));
861 ReleaseDirHandle(&dh
);
865 LONG
FindFreeCluster(struct FSSuper
*sb
, ULONG
*rcluster
) {
869 for (cluster
= sb
->next_cluster
;
870 cluster
< 2 + sb
->clusters_count
&& !found
;
873 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
882 for (cluster
= 2; cluster
< sb
->next_cluster
&& !found
;
885 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
894 D(bug("[fat] no more free clusters, we're out of space\n"));
895 return ERROR_DISK_FULL
;
898 sb
->next_cluster
= *rcluster
;
900 D(bug("[fat] found free cluster %ld\n", *rcluster
));
905 void FreeFATSuper(struct FSSuper
*sb
) {
906 struct Globals
*glob
= sb
->glob
;
907 D(bug("\tRemoving Super Block from memory\n"));
908 Cache_DestroyCache(sb
->cache
);
909 FreeVecPooled(glob
->mempool
, sb
->fat_buffers
);
910 sb
->fat_buffers
= NULL
;
911 FreeVecPooled(glob
->mempool
, sb
->fat_blocks
);
912 sb
->fat_blocks
= NULL
;
915 /* see how many unused clusters are available */
916 void CountFreeClusters(struct FSSuper
*sb
) {
920 /* loop over all the data clusters */
921 for (cluster
= 2; cluster
< sb
->clusters_count
+ 2; cluster
++)
923 /* record the free ones */
924 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
928 /* put the value away for later */
929 sb
->free_clusters
= free
;
931 D(bug("\tfree clusters: %ld\n", free
));
934 void AllocCluster(struct FSSuper
*sb
, ULONG cluster
) {
935 SET_NEXT_CLUSTER(sb
, cluster
, sb
->eoc_mark
);
937 if (sb
->fsinfo_buffer
!= NULL
) {
938 sb
->fsinfo_buffer
->free_count
= AROS_LONG2LE(sb
->free_clusters
);
939 sb
->fsinfo_buffer
->next_free
= AROS_LONG2LE(sb
->next_cluster
);
940 Cache_MarkBlockDirty(sb
->cache
, sb
->fsinfo_block
);
944 void FreeCluster(struct FSSuper
*sb
, ULONG cluster
) {
945 SET_NEXT_CLUSTER(sb
, cluster
, 0);
947 if (sb
->fsinfo_buffer
!= NULL
) {
948 sb
->fsinfo_buffer
->free_count
= AROS_LONG2LE(sb
->free_clusters
);
949 Cache_MarkBlockDirty(sb
->cache
, sb
->fsinfo_block
);
953 void ConvertFATDate(UWORD date
, UWORD time
, struct DateStamp
*ds
, struct Globals
*glob
) {
954 ULONG year
, month
, day
, hours
, mins
, secs
;
955 struct ClockData clock_data
;
957 /* date bits: yyyy yyym mmmd dddd */
958 year
= (date
& 0xfe00) >> 9; /* bits 15-9 */
959 month
= (date
& 0x01e0) >> 5; /* bits 8-5 */
960 day
= date
& 0x001f; /* bits 4-0 */
962 /* time bits: hhhh hmmm mmms ssss */
963 hours
= (time
& 0xf800) >> 11; /* bits 15-11 */
964 mins
= (time
& 0x07e0) >> 5; /* bits 10-5 */
965 secs
= time
& 0x001f; /* bits 4-0 */
967 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
));
969 clock_data
.year
= 1980 + year
;
970 clock_data
.month
= month
;
971 clock_data
.mday
= day
;
972 clock_data
.hour
= hours
;
973 clock_data
.min
= mins
;
974 clock_data
.sec
= secs
<< 1;
975 secs
= Date2Amiga(&clock_data
);
977 /* calculate days since 1978-01-01 (DOS epoch) */
978 ds
->ds_Days
= secs
/ (60 * 60 * 24);
980 /* minutes since midnight */
981 ds
->ds_Minute
= secs
/ 60 % (24 * 60);
983 /* 1/50 sec ticks since last minute */
984 ds
->ds_Tick
= secs
% 60 * TICKS_PER_SECOND
;
986 D(bug("[fat] converted fat date: days %ld minutes %ld ticks %ld\n", ds
->ds_Days
, ds
->ds_Minute
, ds
->ds_Tick
));
989 void ConvertDOSDate(struct DateStamp
*ds
, UWORD
*date
, UWORD
*time
, struct Globals
*glob
) {
992 /* convert datestamp to seconds since 1978 */
993 secs
= ds
->ds_Days
* 60 * 60 * 24 + ds
->ds_Minute
* 60
994 + ds
->ds_Tick
/ TICKS_PER_SECOND
;
996 ConvertSysDate(secs
, date
, time
, glob
);
999 void ConvertSysDate(ULONG secs
, UWORD
*date
, UWORD
*time
, struct Globals
*glob
) {
1000 ULONG year
, month
, day
, hours
, mins
;
1001 struct ClockData clock_data
;
1003 /* Round up to next even second because of FAT's two-second granularity */
1004 secs
= (secs
& ~1) + 2;
1006 /* convert seconds since 1978 to calendar/time data */
1007 Amiga2Date(secs
, &clock_data
);
1009 /* get values used in FAT dates */
1010 year
= clock_data
.year
- 1980;
1011 month
= clock_data
.month
- 0;
1012 day
= clock_data
.mday
;
1013 hours
= clock_data
.hour
;
1014 mins
= clock_data
.min
;
1015 secs
= clock_data
.sec
>> 1;
1017 /* all that remains is to bit-encode the whole lot */
1019 /* date bits: yyyy yyym mmmd dddd */
1020 *date
= (((ULONG
) year
) << 9) | (((ULONG
) month
) << 5) | day
;
1022 /* time bits: hhhh hmmm mmms ssss */
1023 *time
= (((ULONG
) hours
) << 11) | (((ULONG
) mins
) << 5) | secs
;