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
[] =
59 static const struct DateStamp unset_date_limit
=
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
,
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);
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",
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;
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
++)
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
)
111 Cache_FreeBlock(sb
->cache
, sb
->fat_blocks
[i
]);
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) */
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;
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);
162 val
= AROS_LE2WORD(*((UWORD
*) GetFatEntryPtr(sb
, offset
, NULL
, 0)));
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)))
188 static void SetFat12Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
)
190 D(struct Globals
*glob
= sb
->glob
);
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)
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
);
210 fat
= (UWORD
*) GetFatEntryPtr(sb
, offset
, &b
, i
);
211 newval
= AROS_LE2WORD(*fat
);
217 newval
= (newval
& 0xf) | val
;
221 newval
= (newval
& 0xf000) | val
;
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
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
);
238 *fat
= AROS_WORD2LE(newval
);
239 Cache_MarkBlockDirty(sb
->cache
, b
);
244 static void SetFat16Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
)
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
)
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
);
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
;
286 struct DirEntry dir_entry
;
290 D(bug("[fat] reading boot sector\n"));
292 boot
= AllocMem(bsize
, MEMF_ANY
);
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 */
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
);
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
);
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
);
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
)
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)
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)
395 /* Valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
396 if (sb
->clustersize
> 64 * 1024)
399 if (sb
->first_fat_sector
== 0)
402 if (sb
->fat_count
== 0)
405 if (boot
->bpb_media
< 0xF0)
408 /* FAT "signature" */
409 if (boot
->bpb_signature
[0] != 0x55 || boot
->bpb_signature
[1] != 0xaa)
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
,
430 if (sb
->clusters_count
< 4085)
432 D(bug("\tFAT12 filesystem detected\n"));
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"));
442 sb
->eoc_mark
= 0xFFFF;
443 sb
->func_get_fat_entry
= GetFat16Entry
;
444 sb
->func_set_fat_entry
= SetFat16Entry
;
448 D(bug("\tFAT32 filesystem detected\n"));
450 sb
->eoc_mark
= 0x0FFFFFFF;
451 sb
->func_get_fat_entry
= GetFat32Entry
;
452 sb
->func_set_fat_entry
= SetFat32Entry
;
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
);
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
;
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
,
502 /* FIXME: Handle IO errors on cache read! */
503 memset(fat_block
, 0, bsize
);
506 /* The first two entries are special */
508 *(UQUAD
*) fat_block
= AROS_QUAD2LE(0x0FFFFFFF0FFFFFF8);
509 else if (sb
->type
== 16)
510 *(ULONG
*) fat_block
= AROS_LONG2LE(0xFFFFFFF8);
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 */
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)
542 UBYTE
*uu
= (void *)&sb
->volume_id
;
544 /* No volume name entry, so construct name from serial number */
550 sb
->volume
.name
[i
++] = '-';
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
;
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)
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;
586 sb
->fsinfo_block
= Cache_GetBlock(sb
->cache
, sb
->first_device_sector
587 + AROS_LE2WORD(boot
->ebpbs
.ebpb32
.bpb_fs_info
),
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
;
601 Cache_FreeBlock(sb
->cache
, sb
->fsinfo_block
);
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
);
620 static LONG
GetVolumeIdentity(struct FSSuper
*sb
,
621 struct VolumeIdentity
*volume
)
623 struct Globals
*glob
= sb
->glob
;
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
];
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])));
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
;
678 ReleaseDirHandle(&dh
, glob
);
682 LONG
FormatFATVolume(const UBYTE
*name
, UWORD len
, struct Globals
*glob
)
684 struct DosEnvec
*de
= BADDR(glob
->fssm
->fssm_Environ
);
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)
700 else if (sector_count
< 1024 * 1024)
705 D(bug("[fat] writing boot sector\n"));
707 /* Decide on cluster size and root dir entries */
708 first_fat_sector
= 1;
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;
728 /* We only support some common 3.5" floppy formats */
729 return ERROR_NOT_IMPLEMENTED
;
734 for (i
= 0; fat16_cluster_thresholds
[i
] < sector_count
; i
++);
735 sectors_per_cluster
= 1 << i
;
736 root_entries_count
= 512;
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;
754 fat_size
= (temp1
+ temp2
- 1) / temp2
;
756 boot
= AllocMem(bsize
, MEMF_CLEAR
);
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
);
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
);
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
;
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 */
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
++)
812 ebpb
->bs_vollab
[i
] = toupper(name
[i
]);
814 ebpb
->bs_vollab
[i
] = ' ';
816 CopyMem(default_filsystype
, ebpb
->bs_filsystype
, 8);
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
);
841 /* Write back-up boot sector and FS info sector */
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
);
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
);
870 FreeMem(boot
, bsize
);
872 glob
->formatting
= TRUE
;
877 LONG
SetVolumeName(struct FSSuper
*sb
, UBYTE
*name
, UWORD len
)
879 struct Globals
*glob
= sb
->glob
;
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
);
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
);
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
));
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
;
934 /* Create a new volume ID entry if there wasn't one */
937 err
= AllocDirEntry(&dh
, 0, &de
, glob
);
939 FillDirEntry(&de
, ATTR_VOLUME_ID
, 0, glob
);
942 /* Copy the name in */
945 for (i
= 0; i
< FAT_MAX_SHORT_NAME
; i
++)
947 de
.e
.entry
.name
[i
] = toupper(name
[i
]);
949 de
.e
.entry
.name
[i
] = ' ';
951 if ((err
= UpdateDirEntry(&de
, glob
)) != 0)
953 D(bug("[fat] couldn't change volume name\n"));
958 /* Copy name to boot block as well, and save */
960 CopyMem(de
.e
.entry
.name
, boot
->ebpbs
.ebpb32
.ebpb
.bs_vollab
,
963 CopyMem(de
.e
.entry
.name
, boot
->ebpbs
.ebpb
.bs_vollab
,
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
);
984 LONG
FindFreeCluster(struct FSSuper
*sb
, ULONG
*rcluster
)
986 D(struct Globals
*glob
= sb
->glob
);
990 for (cluster
= sb
->next_cluster
;
991 cluster
< 2 + sb
->clusters_count
&& !found
; cluster
++)
993 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
1002 for (cluster
= 2; cluster
< sb
->next_cluster
&& !found
; cluster
++)
1004 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
1006 *rcluster
= cluster
;
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
));
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
);
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)
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"));
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
;