r26035@plastic: rob | 2007-04-16 16:19:31 +1000
[cake.git] / workbench / fs / fat / fat.c
blob6ec0fedeb9907d269c54ca6ddcca5b0f49b2ef71
1 /*
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.
10 * $Id$
13 #include <exec/types.h>
14 #include <dos/dos.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>
23 #include <string.h>
24 #include <ctype.h>
26 #include "fat_fs.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);
43 /* load some more */
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)),
49 sb->fat_blocks_count,
51 sb->fat_blocks);
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 */
59 if (rb != NULL)
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;
86 UWORD val;
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);
96 else
97 val = AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, n + n/2, NULL)));
99 if (n & 1)
100 val >>= 4;
101 else
102 val &= 0xfff;
104 return val;
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;
124 UWORD *fat, newval;
126 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
127 boundary = TRUE;
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);
136 else {
137 fat = (UWORD *) GetFatEntryPtr(sb, offset, &b);
138 newval = AROS_LE2WORD(*fat);
141 if (n & 1) {
142 n <<= 4;
143 newval = (newval & 0xf) | val;
145 else {
146 n &= 0xfff;
147 newval = (newval & 0xf000) | val;
150 if (boundary) {
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);
162 else {
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);
187 LONG err;
188 UBYTE raw[512];
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 *
203 de->de_LowCyl;
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));
215 return 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);
240 else
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);
246 else
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)
271 invalid = TRUE;
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)
275 invalid = TRUE;
277 /* valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
278 if (sb->clustersize > 64 * 1024)
279 invalid = TRUE;
281 if (sb->first_fat_sector == 0)
282 invalid = TRUE;
284 if (boot->bpb_num_fats == 0)
285 invalid = TRUE;
287 if (boot->bpb_media < 0xF0)
288 invalid = TRUE;
290 /* FAT "signature" */
291 if (raw[510] != 0x55 || raw[511] != 0xaa)
292 invalid = TRUE;
294 if (invalid) {
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");
303 sb->type = 12;
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");
310 sb->type = 16;
311 sb->eoc_mark = 0xFFF8;
312 sb->func_get_fat_entry = GetFat16Entry;
313 sb->func_set_fat_entry = SetFat16Entry;
315 else {
316 kprintf("\tFAT32 filesystem detected\n");
317 sb->type = 32;
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;
339 else {
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) {
351 LONG i;
352 UBYTE *uu = (void *)&sb->volume_id;
354 for (i=1; i<10;) {
355 int d;
357 if (i==5)
358 sb->volume.name[i++]='-';
360 d = (*uu) & 0x0f;
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;
365 uu++;
368 sb->volume.name[i] = '\0';
369 sb->volume.name[0] = 9;
372 kprintf("\tFAT Filesystem succesfully detected.\n");
374 return 0;
377 LONG GetVolumeInfo(struct FSSuper *sb, struct VolumeInfo *volume) {
378 struct DirHandle dh;
379 struct DirEntry de;
380 LONG err;
381 int i;
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])));
415 break;
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;
422 break;
426 ReleaseDirHandle(&dh);
427 return err;
430 LONG SetVolumeName(struct FSSuper *sb, UBYTE *name) {
431 struct DirHandle dh;
432 struct DirEntry de;
433 LONG err;
434 int i;
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++)
452 if (i < name[0])
453 de.e.entry.name[i] = tolower(name[i+1]);
454 else
455 de.e.entry.name[i] = ' ';
457 if ((err = UpdateDirEntry(&de)) != 0) {
458 D(bug("[fat] couldn't change volume name\n"));
459 return err;
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])));
468 break;
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;
475 break;
479 ReleaseDirHandle(&dh);
480 return err;
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) {
490 LONG res;
492 if ((res = memcmp(s1->volume.name, s2->volume.name, s1->volume.name[0])) != 0)
493 return res;
495 return s1->volume_id - s2->volume_id;
499 /* see how many unused clusters are available */
500 void CountFreeClusters(struct FSSuper *sb) {
501 ULONG cluster = 0;
502 ULONG free = 0;
504 /* don't calculate this if we already have it */
505 if (sb->free_clusters != 0xffffffff)
506 return;
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)
513 free++;
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;
525 UBYTE nleap;
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 :) */
543 nleap = year >> 2;
545 /* if this year is a leap year and its March or later, adjust for this
546 * year too */
547 if (year & 0x03 && month >= 3)
548 nleap++;
550 /* calculate days since 1978-01-01 (DOS epoch):
551 * 730 days in 1978+1979, getting us to the FAT epoch 1980-01-01
552 * years * 365 days
553 * leap days
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
562 * as well */
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));