2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007 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 <exec/types.h>
15 #include <dos/dosextens.h>
16 #include <dos/filehandler.h>
18 #include <proto/exec.h>
19 #include <proto/dos.h>
21 #include <clib/macros.h>
27 #include "fat_protos.h"
29 /* helper function to get the location of a fat entry for a cluster. it used
30 * to be a define until it got too crazy */
31 static UBYTE
*GetFatEntryPtr(struct FSSuper
*sb
, ULONG offset
, struct cache_block
**rb
) {
32 ULONG entry_cache_block
= offset
>> sb
->fat_cachesize_bits
;
33 ULONG entry_cache_offset
= offset
& (sb
->fat_cachesize
- 1);
35 /* if the target cluster is not within the currently loaded chunk of fat,
36 * we need to get the right data in */
37 if (sb
->fat_cache_block
!= entry_cache_block
) {
38 D(bug("[fat] loading %ld FAT sectors starting at sector %ld\n", sb
->fat_blocks_count
, entry_cache_block
));
39 /* put the old ones back */
40 if (sb
->fat_cache_block
!= 0xffffffff)
41 cache_put_blocks(sb
->cache
, sb
->fat_blocks
, sb
->fat_blocks_count
, 0);
44 cache_get_blocks(sb
->cache
,
45 glob
->diskioreq
->iotd_Req
.io_Device
,
46 glob
->diskioreq
->iotd_Req
.io_Unit
,
47 sb
->first_device_sector
+ sb
->first_fat_sector
+
48 (entry_cache_block
<< (sb
->fat_cachesize_bits
- sb
->sectorsize_bits
)),
53 /* remember where we are for next time */
54 sb
->fat_cache_block
= entry_cache_block
;
57 /* give the block back if they asked for it (needed to mark the block
58 * dirty if they're writing */
60 *rb
= sb
->fat_blocks
[entry_cache_offset
>> sb
->sectorsize_bits
];
62 /* compute the pointer location and return it */
63 return sb
->fat_blocks
[entry_cache_offset
>> sb
->sectorsize_bits
]->data
+
64 (entry_cache_offset
& (sb
->sectorsize
-1));
67 /* FAT12 has, as the name suggests, 12-bit FAT entries. This means that two
68 * entries are condensed into three bytes, like so:
70 * entry: aaaaaaaa aaaabbbb bbbbbbbb
71 * bytes: xxxxxxxx xxxxxxxx xxxxxxxx
73 * To get at the entry we want, we find and grab the word starting at either
74 * byte 0 or 1 of the three-byte set, then shift up or down as needed. FATdoc
75 * 1.03 p16-17 describes the method
77 * The only tricky bit is if the word falls such that the first byte is the
78 * last byte of the block and the second byte is the first byte of the next
79 * block. Since our block data are stored within cache block structures, a
80 * simple cast won't do (hell, the second block may not even be in memory if
81 * we're at the end of the FAT cache). So we get it a byte at a time, and
82 * build the word ourselves.
84 static ULONG
GetFat12Entry(struct FSSuper
*sb
, ULONG n
) {
85 ULONG offset
= n
+ n
/2;
88 if ((offset
& (sb
->sectorsize
-1)) == sb
->sectorsize
-1) {
89 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
91 val
= ((UWORD
) *GetFatEntryPtr(sb
, offset
, NULL
)) << 8;
92 val
|= ((UWORD
) *GetFatEntryPtr(sb
, offset
+1, NULL
));
94 val
= AROS_LE2WORD(val
);
97 val
= AROS_LE2WORD(*((UWORD
*) GetFatEntryPtr(sb
, n
+ n
/2, NULL
)));
108 * FAT16 and FAT32, on the other hand, have nice neat entry widths, so simple
109 * word/long casts are fine. There's also no chance that the entry can be
110 * split across blocks. Why can't everything be this simple?
112 static ULONG
GetFat16Entry(struct FSSuper
*sb
, ULONG n
) {
113 return AROS_LE2WORD(*((UWORD
*) GetFatEntryPtr(sb
, n
<< 1, NULL
)));
116 static ULONG
GetFat32Entry(struct FSSuper
*sb
, ULONG n
) {
117 return AROS_LE2LONG(*((ULONG
*) GetFatEntryPtr(sb
, n
<< 2, NULL
)));
120 static void SetFat12Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
) {
121 struct cache_block
*b
;
122 ULONG offset
= n
+ n
/2;
123 BOOL boundary
= FALSE
;
126 if ((offset
& (sb
->sectorsize
-1)) == sb
->sectorsize
-1) {
129 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
131 newval
= ((UWORD
) *GetFatEntryPtr(sb
, offset
, NULL
)) << 8;
132 newval
|= ((UWORD
) *GetFatEntryPtr(sb
, offset
+1, NULL
));
134 newval
= AROS_LE2WORD(newval
);
137 fat
= (UWORD
*) GetFatEntryPtr(sb
, offset
, &b
);
138 newval
= AROS_LE2WORD(*fat
);
143 newval
= (newval
& 0xf) | val
;
147 newval
= (newval
& 0xf000) | val
;
151 newval
= AROS_WORD2LE(newval
);
153 /* XXX ideally we'd mark both blocks dirty at the same time or only do
154 * it once if they're same block. unfortunately b is essentially
155 * invalid after a call to GetFatEntryPtr, as it may have swapped the
156 * previous cache out. This is probably safe enough. */
157 *GetFatEntryPtr(sb
, offset
+1, &b
) = newval
>> 8;
158 cache_mark_block_dirty(sb
->cache
, b
);
159 *GetFatEntryPtr(sb
, offset
, &b
) = newval
& 0xff;
160 cache_mark_block_dirty(sb
->cache
, b
);
163 *fat
= AROS_WORD2LE(newval
);
164 cache_mark_block_dirty(sb
->cache
, b
);
168 static void SetFat16Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
) {
169 struct cache_block
*b
;
171 *((UWORD
*) GetFatEntryPtr(sb
, n
<< 1, &b
)) = AROS_WORD2LE((UWORD
) val
);
173 cache_mark_block_dirty(sb
->cache
, b
);
176 static void SetFat32Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
) {
177 struct cache_block
*b
;
178 ULONG
*fat
= (ULONG
*) GetFatEntryPtr(sb
, n
<< 2, &b
);
180 *fat
= (*fat
& 0xf0000000) | val
;
182 cache_mark_block_dirty(sb
->cache
, b
);
185 LONG
ReadFATSuper(struct FSSuper
*sb
) {
186 struct DosEnvec
*de
= BADDR(glob
->fssm
->fssm_Environ
);
189 struct FATBootSector
*boot
= (struct FATBootSector
*) &raw
;
190 BOOL invalid
= FALSE
;
192 D(bug("[fat] reading boot sector\n"));
196 * Read the boot sector. We go direct because we don't have a cache yet,
197 * and can't create one until we know the sector size, which is held in
198 * the boot sector. In practice it doesn't matter - we're going use this
199 * once and once only.
202 sb
->first_device_sector
= de
->de_BlocksPerTrack
*
205 D(bug("[fat] boot sector at sector %ld\n", sb
->first_device_sector
));
207 glob
->diskioreq
->iotd_Req
.io_Command
= CMD_READ
;
208 glob
->diskioreq
->iotd_Req
.io_Offset
= sb
->first_device_sector
* de
->de_SizeBlock
* 4;
209 glob
->diskioreq
->iotd_Req
.io_Length
= 512;
210 glob
->diskioreq
->iotd_Req
.io_Data
= &raw
;
211 glob
->diskioreq
->iotd_Req
.io_Flags
= IOF_QUICK
;
213 if ((err
= DoIO((struct IORequest
*) glob
->diskioreq
)) != 0) {
214 D(bug("[fat] couldn't read boot block (%ld)\n", err
));
218 kprintf("\tBoot sector:\n");
220 sb
->sectorsize
= AROS_LE2WORD(boot
->bpb_bytes_per_sect
);
221 sb
->sectorsize_bits
= log2(sb
->sectorsize
);
222 kprintf("\tSectorSize = %ld\n", sb
->sectorsize
);
223 kprintf("\tSectorSize Bits = %ld\n", sb
->sectorsize_bits
);
225 sb
->cluster_sectors
= boot
->bpb_sect_per_clust
;
226 sb
->clustersize
= sb
->sectorsize
* boot
->bpb_sect_per_clust
;
227 sb
->clustersize_bits
= log2(sb
->clustersize
);
228 sb
->cluster_sectors_bits
= sb
->clustersize_bits
- sb
->sectorsize_bits
;
230 kprintf("\tSectorsPerCluster = %ld\n", (ULONG
)boot
->bpb_sect_per_clust
);
231 kprintf("\tClusterSize = %ld\n", sb
->clustersize
);
232 kprintf("\tClusterSize Bits = %ld\n", sb
->clustersize_bits
);
233 kprintf("\tCluster Sectors Bits = %ld\n", sb
->cluster_sectors_bits
);
235 sb
->first_fat_sector
= AROS_LE2WORD(boot
->bpb_rsvd_sect_count
);
236 kprintf("\tFirst FAT Sector = %ld\n", sb
->first_fat_sector
);
238 if (boot
->bpb_fat_size_16
!= 0)
239 sb
->fat_size
= AROS_LE2WORD(boot
->bpb_fat_size_16
);
241 sb
->fat_size
= AROS_LE2LONG(boot
->type
.fat32
.bpb_fat_size_32
);
242 kprintf("\tFAT Size = %ld\n", sb
->fat_size
);
244 if (boot
->bpb_total_sectors_16
!= 0)
245 sb
->total_sectors
= AROS_LE2WORD(boot
->bpb_total_sectors_16
);
247 sb
->total_sectors
= AROS_LE2LONG(boot
->bpb_total_sectors_32
);
248 kprintf("\tTotal Sectors = %ld\n", sb
->total_sectors
);
250 sb
->rootdir_sectors
= ((AROS_LE2WORD(boot
->bpb_root_entries_count
) * sizeof(struct FATDirEntry
)) + (sb
->sectorsize
- 1)) >> sb
->sectorsize_bits
;
251 kprintf("\tRootDir Sectors = %ld\n", sb
->rootdir_sectors
);
253 sb
->data_sectors
= sb
->total_sectors
- (sb
->first_fat_sector
+ (boot
->bpb_num_fats
* sb
->fat_size
) + sb
->rootdir_sectors
);
254 kprintf("\tData Sectors = %ld\n", sb
->data_sectors
);
256 sb
->clusters_count
= sb
->data_sectors
>> sb
->cluster_sectors_bits
;
257 kprintf("\tClusters Count = %ld\n", sb
->clusters_count
);
259 sb
->first_rootdir_sector
= sb
->first_fat_sector
+ (boot
->bpb_num_fats
* sb
->fat_size
);
260 kprintf("\tFirst RootDir Sector = %ld\n", sb
->first_rootdir_sector
);
262 sb
->first_data_sector
= sb
->first_fat_sector
+ (boot
->bpb_num_fats
* sb
->fat_size
) + sb
->rootdir_sectors
;
263 kprintf("\tFirst Data Sector = %ld\n", sb
->first_data_sector
);
265 sb
->free_clusters
= 0xffffffff;
267 /* check if disk is in fact FAT filesystem */
269 /* valid sector size: 512, 1024, 2048, 4096 */
270 if (sb
->sectorsize
!= 512 && sb
->sectorsize
!= 1024 && sb
->sectorsize
!= 2048 && sb
->sectorsize
!= 4096)
273 /* valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
274 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)
277 /* valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
278 if (sb
->clustersize
> 64 * 1024)
281 if (sb
->first_fat_sector
== 0)
284 if (boot
->bpb_num_fats
== 0)
287 if (boot
->bpb_media
< 0xF0)
290 /* FAT "signature" */
291 if (raw
[510] != 0x55 || raw
[511] != 0xaa)
295 kprintf("\tInvalid FAT Boot Sector\n");
296 return ERROR_NOT_A_DOS_DISK
;
299 sb
->cache
= cache_new(64, 256, sb
->sectorsize
, CACHE_WRITETHROUGH
);
301 if (sb
->clusters_count
< 4085) {
302 kprintf("\tFAT12 filesystem detected\n");
304 sb
->eoc_mark
= 0x0FF8;
305 sb
->func_get_fat_entry
= GetFat12Entry
;
306 sb
->func_set_fat_entry
= SetFat12Entry
;
308 else if (sb
->clusters_count
< 65525) {
309 kprintf("\tFAT16 filesystem detected\n");
311 sb
->eoc_mark
= 0xFFF8;
312 sb
->func_get_fat_entry
= GetFat16Entry
;
313 sb
->func_set_fat_entry
= SetFat16Entry
;
316 kprintf("\tFAT32 filesystem detected\n");
318 sb
->eoc_mark
= 0x0FFFFFF8;
319 sb
->func_get_fat_entry
= GetFat32Entry
;
320 sb
->func_set_fat_entry
= SetFat32Entry
;
323 /* setup the FAT cache and load the first blocks */
324 sb
->fat_cachesize
= 4096;
325 sb
->fat_cachesize_bits
= log2(sb
->fat_cachesize
);
326 sb
->fat_cache_block
= 0xffffffff;
328 sb
->fat_blocks_count
= MIN(sb
->fat_size
, sb
->fat_cachesize
>> sb
->sectorsize_bits
);
329 sb
->fat_blocks
= AllocVecPooled(glob
->mempool
, sizeof(struct cache_block
*) * sb
->fat_blocks_count
);
331 if (sb
->type
!= 32) { /* FAT 12/16 */
332 /* setup volume id */
333 sb
->volume_id
= AROS_LE2LONG(boot
->type
.fat16
.bs_volid
);
335 /* location of root directory */
336 sb
->rootdir_cluster
= 0;
337 sb
->rootdir_sector
= sb
->first_rootdir_sector
;
340 /* setup volume id */
341 sb
->volume_id
= AROS_LE2LONG(boot
->type
.fat32
.bs_volid
);
343 /* location of root directory */
344 sb
->rootdir_cluster
= AROS_LE2LONG(boot
->type
.fat32
.bpb_root_cluster
);
345 sb
->rootdir_sector
= 0;
348 D(bug("[fat] rootdir at cluster %ld sector %ld\n", sb
->rootdir_cluster
, sb
->rootdir_sector
));
350 if (GetVolumeInfo(sb
, &(sb
->volume
)) != 0) {
352 UBYTE
*uu
= (void *)&sb
->volume_id
;
358 sb
->volume
.name
[i
++]='-';
361 sb
->volume
.name
[i
++] = (d
< 10) ? '0' + d
: 'A' - 10 + d
;
362 d
= ((*uu
) & 0xf0)>>4;
363 sb
->volume
.name
[i
++] = (d
< 10) ? '0' + d
: 'A' - 10 + d
;
368 sb
->volume
.name
[i
] = '\0';
369 sb
->volume
.name
[0] = 9;
372 kprintf("\tFAT Filesystem succesfully detected.\n");
377 LONG
GetVolumeInfo(struct FSSuper
*sb
, struct VolumeInfo
*volume
) {
383 D(bug("[fat] searching root directory for volume name\n"));
385 /* search the directory for the volume id entry. it would've been nice to
386 * just use GetNextDirEntry but I didn't want a flag or something to tell
387 * it not to skip the volume name */
388 InitDirHandle(sb
, 0, &dh
);
390 while ((err
= GetDirEntry(&dh
, dh
.cur_index
+ 1, &de
)) == 0) {
392 /* match the volume id entry */
393 if ((de
.e
.entry
.attr
& ATTR_LONG_NAME_MASK
) == ATTR_VOLUME_ID
) {
394 D(bug("[fat] found volume id entry %ld\n", dh
.cur_index
));
396 /* copy the name in. volume->name is a BSTR */
398 volume
->name
[1] = de
.e
.entry
.name
[0];
399 volume
->name
[12] = '\0';
401 for (i
= 1; i
< 11; i
++)
402 volume
->name
[i
+1] = tolower(de
.e
.entry
.name
[i
]);
404 for (i
= 10; i
> 1; i
--)
405 if (volume
->name
[i
+1] == ' ')
406 volume
->name
[i
+1] = '\0';
408 volume
->name
[0] = strlen(&(volume
->name
[1]));
410 /* get the volume creation date date too */
411 ConvertDate(de
.e
.entry
.create_date
, de
.e
.entry
.create_time
, &volume
->create_time
);
413 D(bug("[fat] volume name is '%s'\n", &(volume
->name
[1])));
418 /* bail out if we hit the end of the dir */
419 if (de
.e
.entry
.name
[0] == 0x00) {
420 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
421 err
= ERROR_OBJECT_NOT_FOUND
;
426 ReleaseDirHandle(&dh
);
430 LONG
SetVolumeName(struct FSSuper
*sb
, UBYTE
*name
) {
436 D(bug("[fat] searching root directory for volume name\n"));
438 /* search the directory for the volume id entry. it would've been nice to
439 * just use GetNextDirEntry but I didn't want a flag or something to tell
440 * it not to skip the volume name */
441 InitDirHandle(sb
, 0, &dh
);
443 while ((err
= GetDirEntry(&dh
, dh
.cur_index
+ 1, &de
)) == 0) {
445 /* match the volume id entry */
446 if ((de
.e
.entry
.attr
& ATTR_LONG_NAME_MASK
) == ATTR_VOLUME_ID
) {
447 D(bug("[fat] found volume id entry %ld\n", dh
.cur_index
));
449 /* copy the name in. name is a BSTR */
450 de
.e
.entry
.name
[0] = name
[1];
451 for (i
= 1; i
< 11; i
++)
453 de
.e
.entry
.name
[i
] = tolower(name
[i
+1]);
455 de
.e
.entry
.name
[i
] = ' ';
457 if ((err
= UpdateDirEntry(&de
)) != 0) {
458 D(bug("[fat] couldn't change volume name\n"));
462 sb
->volume
.name
[0] = name
[0] < 10 ? name
[0] : 10;
463 CopyMem(&name
[1], &(sb
->volume
.name
[1]), sb
->volume
.name
[0]);
464 sb
->volume
.name
[sb
->volume
.name
[0]+1] = '\0';
466 D(bug("[fat] new volume name is '%s'\n", &(sb
->volume
.name
[1])));
471 /* bail out if we hit the end of the dir */
472 if (de
.e
.entry
.name
[0] == 0x00) {
473 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
474 err
= ERROR_OBJECT_NOT_FOUND
;
479 ReleaseDirHandle(&dh
);
483 void FreeFATSuper(struct FSSuper
*sb
) {
484 kprintf("\tRemoving Super Block from memory\n");
485 FreeVecPooled(glob
->mempool
, sb
->fat_blocks
);
486 sb
->fat_blocks
= NULL
;
489 LONG
CompareFATSuper(struct FSSuper
*s1
, struct FSSuper
*s2
) {
492 if ((res
= memcmp(s1
->volume
.name
, s2
->volume
.name
, s1
->volume
.name
[0])) != 0)
495 return s1
->volume_id
- s2
->volume_id
;
499 /* see how many unused clusters are available */
500 void CountFreeClusters(struct FSSuper
*sb
) {
504 /* don't calculate this if we already have it */
505 if (sb
->free_clusters
!= 0xffffffff)
508 /* loop over all the data clusters */
509 for (cluster
= 2; cluster
< sb
->clusters_count
+ 2; cluster
++)
511 /* record the free ones */
512 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
515 /* put the value away for later */
516 sb
->free_clusters
= free
;
518 kprintf("\tfree clusters: %ld\n", free
);
521 static const UWORD mdays
[] = { 0, 31, 59, 90, 120, 151, 181, 212, 143, 273, 304, 334 };
523 void ConvertDate(UWORD date
, UWORD time
, struct DateStamp
*ds
) {
524 UBYTE year
, month
, day
, hours
, mins
, secs
;
527 /* date bits: yyyy yyym mmmd dddd */
528 year
= (date
& 0xfe00) >> 9; /* bits 15-9 */
529 month
= (date
& 0x01e0) >> 5; /* bits 8-5 */
530 day
= date
& 0x001f; /* bits 4-0 */
532 /* time bits: hhhh hmmm mmms ssss */
533 hours
= (time
& 0xf800) >> 11; /* bits 15-11 */
534 mins
= (time
& 0x07e0) >> 5; /* bits 8-5 */
535 secs
= time
& 0x001f; /* bits 4-0 */
537 D(bug("[fat] convert date: year %d month %d day %d hours %d mins %d secs %d\n", year
, month
, day
, hours
, mins
, secs
));
539 /* number of leap years in before this year. note this is only dividing by
540 * four, which is fine because FAT dates range 1980-2107. The only year in
541 * that range that is divisible by four but not a leap year is 2100. If
542 * this code is still being used then, feel free to fix it :) */
545 /* if this year is a leap year and its March or later, adjust for this
547 if (year
& 0x03 && month
>= 3)
550 /* calculate days since 1978-01-01 (DOS epoch):
551 * 730 days in 1978+1979, getting us to the FAT epoch 1980-01-01
554 * days in all the months before this one
555 * day of this month */
556 ds
->ds_Days
= 730 + year
* 365 + nleap
+ mdays
[month
-1] + day
-1;
558 /* minutes since midnight */
559 ds
->ds_Minute
= hours
* 60 + mins
;
561 /* 1/50 sec ticks. FAT dates are 0-29, so we have to multiply them by two
563 ds
->ds_Tick
= (secs
<< 1) * 50;
565 D(bug("[fat] convert date: days %ld minutes %ld ticks %ld\n", ds
->ds_Days
, ds
->ds_Minute
, ds
->ds_Tick
));