- Round up modification time to the next even second because of FAT's
[AROS.git] / rom / filesys / fat / fat.c
bloba64cf58ab4394b7f85094d5ebe60dabbd374a58e
1 /*
2 * fat-handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2015 The AROS Development Team
7 * This program is free software; you can redistribute it and/or modify it
8 * under the same terms as AROS itself.
10 * $Id$
13 #include <aros/macros.h>
14 #include <exec/errors.h>
15 #include <exec/types.h>
16 #include <dos/dos.h>
17 #include <dos/dosextens.h>
18 #include <dos/filehandler.h>
20 #include <proto/exec.h>
21 #include <proto/dos.h>
22 #include <proto/utility.h>
24 #include <clib/macros.h>
26 #include <string.h>
27 #include <ctype.h>
29 #include "fat_fs.h"
30 #include "fat_protos.h"
32 #define DEBUG DEBUG_MISC
33 #include "debug.h"
35 /* helper function to get the location of a fat entry for a cluster. it used
36 * to be a define until it got too crazy */
37 static UBYTE *GetFatEntryPtr(struct FSSuper *sb, ULONG offset, APTR *rb,
38 UWORD fat_no) {
39 ULONG entry_cache_block = offset >> sb->fat_cachesize_bits;
40 ULONG entry_cache_offset = offset & (sb->fat_cachesize - 1);
41 ULONG num;
42 UWORD i;
44 /* if the target cluster is not within the currently loaded chunk of fat,
45 * we need to get the right data in */
46 if (sb->fat_cache_block != entry_cache_block
47 || sb->fat_cache_no != fat_no) {
48 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)));
49 /* put the old ones back */
50 if (sb->fat_cache_block != 0xffffffff)
51 for (i = 0; i < sb->fat_blocks_count; i++)
52 Cache_FreeBlock(sb->cache, sb->fat_blocks[i]);
54 /* load some more */
55 num = sb->first_device_sector + sb->first_fat_sector
56 + sb->fat_size * fat_no + (entry_cache_block
57 << (sb->fat_cachesize_bits - sb->sectorsize_bits));
58 for (i = 0; i < sb->fat_blocks_count; i++)
59 sb->fat_blocks[i] =
60 Cache_GetBlock(sb->cache, num + i, &sb->fat_buffers[i]);
62 /* remember where we are for next time */
63 sb->fat_cache_block = entry_cache_block;
64 sb->fat_cache_no = fat_no;
67 /* give the block back if they asked for it (needed to mark the block
68 * dirty if they're writing) */
69 if (rb != NULL)
70 *rb = sb->fat_blocks[entry_cache_offset >> sb->sectorsize_bits];
72 /* compute the pointer location and return it */
73 return sb->fat_buffers[entry_cache_offset >> sb->sectorsize_bits] +
74 (entry_cache_offset & (sb->sectorsize - 1));
77 /* FAT12 has, as the name suggests, 12-bit FAT entries. This means that two
78 * entries are condensed into three bytes, like so:
80 * entry: aaaaaaaa aaaabbbb bbbbbbbb
81 * bytes: xxxxxxxx xxxxxxxx xxxxxxxx
83 * To get at the entry we want, we find and grab the word starting at either
84 * byte 0 or 1 of the three-byte set, then shift up or down as needed. FATdoc
85 * 1.03 p16-17 describes the method
87 * The only tricky bit is if the word falls such that the first byte is the
88 * last byte of the block and the second byte is the first byte of the next
89 * block. Since our block data are stored within cache block structures, a
90 * simple cast won't do (hell, the second block may not even be in memory if
91 * we're at the end of the FAT cache). So we get it a byte at a time, and
92 * build the word ourselves.
94 static ULONG GetFat12Entry(struct FSSuper *sb, ULONG n) {
95 ULONG offset = n + n/2;
96 UWORD val;
98 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
99 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
101 val = *GetFatEntryPtr(sb, offset + 1, NULL, 0) << 8;
102 val |= *GetFatEntryPtr(sb, offset, NULL, 0);
104 else
105 val = AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, offset, NULL, 0)));
107 if (n & 1)
108 val >>= 4;
109 else
110 val &= 0xfff;
112 return val;
116 * FAT16 and FAT32, on the other hand, have nice neat entry widths, so simple
117 * word/long casts are fine. There's also no chance that the entry can be
118 * split across blocks. Why can't everything be this simple?
120 static ULONG GetFat16Entry(struct FSSuper *sb, ULONG n) {
121 return AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, n << 1, NULL, 0)));
124 static ULONG GetFat32Entry(struct FSSuper *sb, ULONG n) {
125 return AROS_LE2LONG(*((ULONG *) GetFatEntryPtr(sb, n << 2, NULL, 0)))
126 & 0x0fffffff;
129 static void SetFat12Entry(struct FSSuper *sb, ULONG n, ULONG val) {
130 APTR b;
131 ULONG offset = n + n/2;
132 BOOL boundary = FALSE;
133 UWORD *fat = NULL, newval, i;
135 for (i = 0; i < sb->fat_count; i++)
137 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
138 boundary = TRUE;
140 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
142 newval = *GetFatEntryPtr(sb, offset + 1, NULL, i) << 8;
143 newval |= *GetFatEntryPtr(sb, offset, NULL, i);
145 else {
146 fat = (UWORD *) GetFatEntryPtr(sb, offset, &b, i);
147 newval = AROS_LE2WORD(*fat);
150 if (n & 1) {
151 val <<= 4;
152 newval = (newval & 0xf) | val;
154 else {
155 newval = (newval & 0xf000) | val;
158 if (boundary) {
159 /* XXX ideally we'd mark both blocks dirty at the same time or
160 * only do it once if they're the same block. unfortunately any
161 * old value of b is invalid after a call to GetFatEntryPtr, as
162 * it may have swapped the previous cache out. This is probably
163 * safe enough. */
164 *GetFatEntryPtr(sb, offset+1, &b, i) = newval >> 8;
165 Cache_MarkBlockDirty(sb->cache, b);
166 *GetFatEntryPtr(sb, offset, &b, i) = newval & 0xff;
167 Cache_MarkBlockDirty(sb->cache, b);
169 else {
170 *fat = AROS_WORD2LE(newval);
171 Cache_MarkBlockDirty(sb->cache, b);
176 static void SetFat16Entry(struct FSSuper *sb, ULONG n, ULONG val) {
177 APTR b;
178 UWORD i;
180 for (i = 0; i < sb->fat_count; i++)
182 *((UWORD *) GetFatEntryPtr(sb, n << 1, &b, i)) =
183 AROS_WORD2LE((UWORD) val);
184 Cache_MarkBlockDirty(sb->cache, b);
188 static void SetFat32Entry(struct FSSuper *sb, ULONG n, ULONG val) {
189 APTR b;
190 ULONG *fat;
191 UWORD i;
193 for (i = 0; i < sb->fat_count; i++)
195 fat = (ULONG *) GetFatEntryPtr(sb, n << 2, &b, i);
197 *fat = (*fat & 0xf0000000) | val;
199 Cache_MarkBlockDirty(sb->cache, b);
203 LONG ReadFATSuper(struct FSSuper *sb ) {
204 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
205 LONG err;
206 ULONG bsize = de->de_SizeBlock * 4;
207 struct FATBootSector *boot;
208 struct FATFSInfo *fsinfo;
209 BOOL invalid = FALSE;
210 ULONG end;
212 D(bug("[fat] reading boot sector\n"));
214 boot = AllocMem(bsize, MEMF_ANY);
215 if (!boot)
216 return ERROR_NO_FREE_STORE;
219 * Read the boot sector. We go direct because we don't have a cache yet,
220 * and can't create one until we know the sector size, which is held in
221 * the boot sector. In practice it doesn't matter - we're going to use
222 * this once and once only.
224 sb->first_device_sector =
225 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
227 D(bug("[fat] boot sector at sector %ld\n", sb->first_device_sector));
229 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize, (UBYTE *)boot)) != 0) {
230 D(bug("[fat] couldn't read boot block (%ld)\n", err));
231 FreeMem(boot, bsize);
232 return err;
235 D(bug("\tBoot sector:\n"));
237 sb->sectorsize = AROS_LE2WORD(boot->bpb_bytes_per_sect);
238 sb->sectorsize_bits = log2(sb->sectorsize);
239 D(bug("\tSectorSize = %ld\n", sb->sectorsize));
240 D(bug("\tSectorSize Bits = %ld\n", sb->sectorsize_bits));
242 sb->cluster_sectors = boot->bpb_sect_per_clust;
243 sb->clustersize = sb->sectorsize * boot->bpb_sect_per_clust;
244 sb->clustersize_bits = log2(sb->clustersize);
245 sb->cluster_sectors_bits = sb->clustersize_bits - sb->sectorsize_bits;
247 D(bug("\tSectorsPerCluster = %ld\n", (ULONG)boot->bpb_sect_per_clust));
248 D(bug("\tClusterSize = %ld\n", sb->clustersize));
249 D(bug("\tClusterSize Bits = %ld\n", sb->clustersize_bits));
250 D(bug("\tCluster Sectors Bits = %ld\n", sb->cluster_sectors_bits));
252 sb->first_fat_sector = AROS_LE2WORD(boot->bpb_rsvd_sect_count);
253 D(bug("\tFirst FAT Sector = %ld\n", sb->first_fat_sector));
255 sb->fat_count = boot->bpb_num_fats;
256 D(bug("\tNumber of FATs = %d\n", sb->fat_count));
258 if (boot->bpb_fat_size_16 != 0)
259 sb->fat_size = AROS_LE2WORD(boot->bpb_fat_size_16);
260 else
261 sb->fat_size = AROS_LE2LONG(boot->type.fat32.bpb_fat_size_32);
262 D(bug("\tFAT Size = %ld\n", sb->fat_size));
264 if (boot->bpb_total_sectors_16 != 0)
265 sb->total_sectors = AROS_LE2WORD(boot->bpb_total_sectors_16);
266 else
267 sb->total_sectors = AROS_LE2LONG(boot->bpb_total_sectors_32);
268 D(bug("\tTotal Sectors = %ld\n", sb->total_sectors));
270 sb->rootdir_sectors = ((AROS_LE2WORD(boot->bpb_root_entries_count) * sizeof(struct FATDirEntry)) + (sb->sectorsize - 1)) >> sb->sectorsize_bits;
271 D(bug("\tRootDir Sectors = %ld\n", sb->rootdir_sectors));
273 sb->data_sectors = sb->total_sectors - (sb->first_fat_sector + (sb->fat_count * sb->fat_size) + sb->rootdir_sectors);
274 D(bug("\tData Sectors = %ld\n", sb->data_sectors));
276 sb->clusters_count = sb->data_sectors >> sb->cluster_sectors_bits;
277 D(bug("\tClusters Count = %ld\n", sb->clusters_count));
279 sb->first_rootdir_sector = sb->first_fat_sector + (sb->fat_count * sb->fat_size);
280 D(bug("\tFirst RootDir Sector = %ld\n", sb->first_rootdir_sector));
282 sb->first_data_sector = sb->first_fat_sector + (sb->fat_count * sb->fat_size) + sb->rootdir_sectors;
283 D(bug("\tFirst Data Sector = %ld\n", sb->first_data_sector));
285 /* check if disk is in fact a FAT filesystem */
287 /* valid sector size: 512, 1024, 2048, 4096 */
288 if (sb->sectorsize != 512 && sb->sectorsize != 1024 && sb->sectorsize != 2048 && sb->sectorsize != 4096)
289 invalid = TRUE;
291 /* valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
292 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)
293 invalid = TRUE;
295 /* valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
296 if (sb->clustersize > 64 * 1024)
297 invalid = TRUE;
299 if (sb->first_fat_sector == 0)
300 invalid = TRUE;
302 if (sb->fat_count == 0)
303 invalid = TRUE;
305 if (boot->bpb_media < 0xF0)
306 invalid = TRUE;
308 /* FAT "signature" */
309 if (boot->bpb_signature[0] != 0x55 || boot->bpb_signature[1] != 0xaa)
310 invalid = TRUE;
312 if (invalid) {
313 D(bug("\tInvalid FAT Boot Sector\n"));
314 FreeMem(boot, bsize);
315 return ERROR_NOT_A_DOS_DISK;
317 end = 0xFFFFFFFF / sb->sectorsize;
318 if ((sb->first_device_sector + sb->total_sectors - 1 > end) && (glob->readcmd == CMD_READ)) {
319 D(bug("\tDevice is too large\n"));
320 FreeMem(boot, bsize);
321 return IOERR_BADADDRESS;
324 sb->cache = Cache_CreateCache(64, 64, sb->sectorsize);
326 if (sb->clusters_count < 4085) {
327 D(bug("\tFAT12 filesystem detected\n"));
328 sb->type = 12;
329 sb->eoc_mark = 0x0FFF;
330 sb->func_get_fat_entry = GetFat12Entry;
331 sb->func_set_fat_entry = SetFat12Entry;
333 else if (sb->clusters_count < 65525) {
334 D(bug("\tFAT16 filesystem detected\n"));
335 sb->type = 16;
336 sb->eoc_mark = 0xFFFF;
337 sb->func_get_fat_entry = GetFat16Entry;
338 sb->func_set_fat_entry = SetFat16Entry;
340 else {
341 D(bug("\tFAT32 filesystem detected\n"));
342 sb->type = 32;
343 sb->eoc_mark = 0x0FFFFFFF;
344 sb->func_get_fat_entry = GetFat32Entry;
345 sb->func_set_fat_entry = SetFat32Entry;
348 /* setup the FAT cache and load the first blocks */
349 sb->fat_cachesize = 4096;
350 sb->fat_cachesize_bits = log2(sb->fat_cachesize);
351 sb->fat_cache_block = 0xffffffff;
353 sb->fat_blocks_count =
354 MIN(sb->fat_size, sb->fat_cachesize >> sb->sectorsize_bits);
355 sb->fat_blocks = AllocVecPooled(glob->mempool,
356 sizeof(APTR) * sb->fat_blocks_count);
357 sb->fat_buffers = AllocVecPooled(glob->mempool,
358 sizeof(APTR) * sb->fat_blocks_count);
360 if (sb->type != 32) { /* FAT 12/16 */
361 /* setup volume id */
362 sb->volume_id = AROS_LE2LONG(boot->type.fat16.bs_volid);
364 /* location of root directory */
365 sb->rootdir_cluster = 0;
366 sb->rootdir_sector = sb->first_rootdir_sector;
368 else {
369 /* setup volume id */
370 sb->volume_id = AROS_LE2LONG(boot->type.fat32.bs_volid);
372 /* location of root directory */
373 sb->rootdir_cluster = AROS_LE2LONG(boot->type.fat32.bpb_root_cluster);
374 sb->rootdir_sector = 0;
377 D(bug("[fat] rootdir at cluster %ld sector %ld\n", sb->rootdir_cluster, sb->rootdir_sector));
379 if (GetVolumeIdentity(sb, &(sb->volume)) != 0) {
380 LONG i;
381 UBYTE *uu = (void *)&sb->volume_id;
383 for (i=1; i<10;) {
384 int d;
386 if (i==5)
387 sb->volume.name[i++]='-';
389 d = (*uu) & 0x0f;
390 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
391 d = ((*uu) & 0xf0)>>4;
392 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
394 uu++;
397 sb->volume.name[i] = '\0';
398 sb->volume.name[0] = 9;
401 /* get initial number of free clusters */
402 sb->free_clusters = -1;
403 sb->next_cluster = -1;
404 if (sb->type == 32) {
405 sb->fsinfo_block = Cache_GetBlock(sb->cache, sb->first_device_sector
406 + AROS_LE2WORD(boot->type.fat32.bpb_fs_info), (UBYTE **)&fsinfo);
407 if (sb->fsinfo_block != NULL) {
408 if (fsinfo->lead_sig == AROS_LONG2LE(FSI_LEAD_SIG)
409 && fsinfo->struct_sig == AROS_LONG2LE(FSI_STRUCT_SIG)
410 && fsinfo->trail_sig == AROS_LONG2LE(FSI_TRAIL_SIG)) {
411 sb->free_clusters = AROS_LE2LONG(fsinfo->free_count);
412 sb->next_cluster = AROS_LE2LONG(fsinfo->next_free);
413 D(bug("[fat] valid FATFSInfo block found\n"));
414 sb->fsinfo_buffer = fsinfo;
416 else
417 Cache_FreeBlock(sb->cache, sb->fsinfo_block);
420 if (sb->free_clusters == -1)
421 CountFreeClusters(sb);
422 if (sb->next_cluster == -1)
423 sb->next_cluster = 2;
425 D(bug("\tFAT Filesystem successfully detected.\n"));
426 D(bug("\tFree Clusters = %ld\n", sb->free_clusters));
427 D(bug("\tNext Free Cluster = %ld\n", sb->next_cluster));
428 FreeMem(boot, bsize);
429 return 0;
432 LONG GetVolumeIdentity(struct FSSuper *sb, struct VolumeIdentity *volume) {
433 struct DirHandle dh;
434 struct DirEntry de;
435 LONG err;
436 int i;
438 D(bug("[fat] searching root directory for volume name\n"));
440 /* search the directory for the volume id entry. it would've been nice to
441 * just use GetNextDirEntry but I didn't want a flag or something to tell
442 * it not to skip the volume name */
443 InitDirHandle(sb, sb->rootdir_cluster, &dh, FALSE);
445 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
447 /* match the volume id entry */
448 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
449 && de.e.entry.name[0] != 0xe5) {
450 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
452 /* copy the name in. volume->name is a BSTR */
454 volume->name[1] = de.e.entry.name[0];
456 for (i = 1; i < 11; i++) {
457 if (volume->name[i] == ' ')
458 volume->name[i+1] = de.e.entry.name[i];
459 else
460 volume->name[i+1] = tolower(de.e.entry.name[i]);
463 for (i = 10; volume->name[i+1] == ' '; i--);
464 volume->name[i+2] = '\0';
465 volume->name[0] = strlen(&(volume->name[1]));
467 /* get the volume creation date too */
468 ConvertFATDate(de.e.entry.create_date, de.e.entry.create_time, &volume->create_time);
470 D(bug("[fat] volume name is '%s'\n", &(volume->name[1])));
472 break;
475 /* bail out if we hit the end of the dir */
476 if (de.e.entry.name[0] == 0x00) {
477 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
478 err = ERROR_OBJECT_NOT_FOUND;
479 break;
483 ReleaseDirHandle(&dh);
484 return err;
487 LONG SetVolumeName(struct FSSuper *sb, UBYTE *name) {
488 struct DirHandle dh;
489 struct DirEntry de;
490 LONG err;
491 int i;
492 struct DosEnvec *dosenv = BADDR(glob->fssm->fssm_Environ);
493 ULONG bsize = dosenv->de_SizeBlock * 4;
494 struct FATBootSector *boot;
496 /* read boot block */
497 boot = AllocMem(bsize, MEMF_ANY);
498 if (!boot)
499 return ERROR_NO_FREE_STORE;
501 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize, (UBYTE *)boot)) != 0) {
502 D(bug("[fat] couldn't read boot block (%ld)\n", err));
503 FreeMem(boot, bsize);
504 return err;
507 D(bug("[fat] searching root directory for volume name\n"));
509 /* search the directory for the volume id entry. it would've been nice to
510 * just use GetNextDirEntry but I didn't want a flag or something to tell
511 * it not to skip the volume name */
512 InitDirHandle(sb, 0, &dh, FALSE);
514 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
516 /* match the volume id entry */
517 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
518 && de.e.entry.name[0] != 0xe5) {
519 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
520 err = 0;
521 break;
524 /* bail out if we hit the end of the dir */
525 if (de.e.entry.name[0] == 0x00) {
526 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
527 err = ERROR_OBJECT_NOT_FOUND;
528 break;
532 /* create a new volume id entry if there wasn't one */
533 if (err != 0) {
534 err = AllocDirEntry(&dh, 0, &de);
535 if (err == 0) {
536 memset(&de.e.entry, 0, sizeof(struct FATDirEntry));
537 de.e.entry.attr = ATTR_VOLUME_ID;
541 /* copy the name in. name is a BSTR */
542 if (err == 0) {
543 de.e.entry.name[0] = name[1];
544 for (i = 0; i < 11; i++)
545 if (i < name[0])
546 de.e.entry.name[i] = toupper(name[i+1]);
547 else
548 de.e.entry.name[i] = ' ';
550 if ((err = UpdateDirEntry(&de)) != 0) {
551 D(bug("[fat] couldn't change volume name\n"));
552 return err;
556 /* copy name to boot block as well, and save */
557 if (sb->type == 32)
558 CopyMem(de.e.entry.name, boot->type.fat32.bs_vollab, 11);
559 else
560 CopyMem(de.e.entry.name, boot->type.fat16.bs_vollab, 11);
562 if ((err = AccessDisk(TRUE, sb->first_device_sector, 1, bsize,
563 (UBYTE *)boot)) != 0)
564 D(bug("[fat] couldn't write boot block (%ld)\n", err));
565 FreeMem(boot, bsize);
567 /* update name in sb */
568 sb->volume.name[0] = name[0] <= 11 ? name[0] : 11;
569 CopyMem(&name[1], &(sb->volume.name[1]), sb->volume.name[0]);
570 sb->volume.name[sb->volume.name[0]+1] = '\0';
572 D(bug("[fat] new volume name is '%s'\n", &(sb->volume.name[1])));
574 ReleaseDirHandle(&dh);
575 return err;
578 LONG FindFreeCluster(struct FSSuper *sb, ULONG *rcluster) {
579 ULONG cluster = 0;
580 BOOL found = FALSE;
582 for (cluster = sb->next_cluster;
583 cluster < 2 + sb->clusters_count && !found;
584 cluster++)
586 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
588 *rcluster = cluster;
589 found = TRUE;
593 if (!found)
595 for (cluster = 2; cluster < sb->next_cluster && !found;
596 cluster++)
598 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
600 *rcluster = cluster;
601 found = TRUE;
606 if (!found) {
607 D(bug("[fat] no more free clusters, we're out of space\n"));
608 return ERROR_DISK_FULL;
611 sb->next_cluster = *rcluster;
613 D(bug("[fat] found free cluster %ld\n", *rcluster));
615 return 0;
618 void FreeFATSuper(struct FSSuper *sb) {
619 D(bug("\tRemoving Super Block from memory\n"));
620 Cache_DestroyCache(sb->cache);
621 FreeVecPooled(glob->mempool, sb->fat_buffers);
622 sb->fat_buffers = NULL;
623 FreeVecPooled(glob->mempool, sb->fat_blocks);
624 sb->fat_blocks = NULL;
627 /* see how many unused clusters are available */
628 void CountFreeClusters(struct FSSuper *sb) {
629 ULONG cluster = 0;
630 ULONG free = 0;
632 /* loop over all the data clusters */
633 for (cluster = 2; cluster < sb->clusters_count + 2; cluster++)
635 /* record the free ones */
636 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
637 free++;
640 /* put the value away for later */
641 sb->free_clusters = free;
643 D(bug("\tfree clusters: %ld\n", free));
646 void AllocCluster(struct FSSuper *sb, ULONG cluster) {
647 SET_NEXT_CLUSTER(sb, cluster, sb->eoc_mark);
648 sb->free_clusters--;
649 if (sb->fsinfo_buffer != NULL) {
650 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
651 sb->fsinfo_buffer->next_free = AROS_LONG2LE(sb->next_cluster);
652 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
656 void FreeCluster(struct FSSuper *sb, ULONG cluster) {
657 SET_NEXT_CLUSTER(sb, cluster, 0);
658 sb->free_clusters++;
659 if (sb->fsinfo_buffer != NULL) {
660 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
661 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
665 void ConvertFATDate(UWORD date, UWORD time, struct DateStamp *ds) {
666 ULONG year, month, day, hours, mins, secs;
667 struct ClockData clock_data;
669 /* date bits: yyyy yyym mmmd dddd */
670 year = (date & 0xfe00) >> 9; /* bits 15-9 */
671 month = (date & 0x01e0) >> 5; /* bits 8-5 */
672 day = date & 0x001f; /* bits 4-0 */
674 /* time bits: hhhh hmmm mmms ssss */
675 hours = (time & 0xf800) >> 11; /* bits 15-11 */
676 mins = (time & 0x07e0) >> 5; /* bits 10-5 */
677 secs = time & 0x001f; /* bits 4-0 */
679 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));
681 clock_data.year = 1980 + year;
682 clock_data.month = month;
683 clock_data.mday = day;
684 clock_data.hour = hours;
685 clock_data.min = mins;
686 clock_data.sec = secs << 1;
687 secs = Date2Amiga(&clock_data);
689 /* calculate days since 1978-01-01 (DOS epoch) */
690 ds->ds_Days = secs / (60 * 60 * 24);
692 /* minutes since midnight */
693 ds->ds_Minute = secs / 60 % (24 * 60);
695 /* 1/50 sec ticks since last minute */
696 ds->ds_Tick = secs % 60 * TICKS_PER_SECOND;
698 D(bug("[fat] converted fat date: days %ld minutes %ld ticks %ld\n", ds->ds_Days, ds->ds_Minute, ds->ds_Tick));
701 void ConvertAROSDate(struct DateStamp *ds, UWORD *date, UWORD *time) {
702 ULONG year, month, day, hours, mins, secs;
703 struct ClockData clock_data;
705 /* convert datestamp to seconds since 1978 */
706 secs = ds->ds_Days * 60 * 60 * 24 + ds->ds_Minute * 60
707 + ds->ds_Tick / TICKS_PER_SECOND;
709 /* Round up to next even second because of FAT's two-second granularity */
710 secs = (secs & ~1) + 2;
712 /* convert seconds since 1978 to calendar/time data */
713 Amiga2Date(secs, &clock_data);
715 /* get values used in FAT dates */
716 year = clock_data.year - 1980;
717 month = clock_data.month - 0;
718 day = clock_data.mday;
719 hours = clock_data.hour;
720 mins = clock_data.min;
721 secs = clock_data.sec >> 1;
723 /* all that remains is to bit-encode the whole lot */
725 /* date bits: yyyy yyym mmmd dddd */
726 *date = (((ULONG) year) << 9) | (((ULONG) month) << 5) | day;
728 /* time bits: hhhh hmmm mmms ssss */
729 *time = (((ULONG) hours) << 11) | (((ULONG) mins) << 5) | secs;