includes: Add definitions for AROS_UFP2S to the generic asmcall.h
[AROS.git] / rom / filesys / fat / fat.c
blob9dcda6ed2c60c8a411293092d0a5ba02474a1999
1 /*
2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2011 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 ULONG entry_cache_block = offset >> sb->fat_cachesize_bits;
39 ULONG entry_cache_offset = offset & (sb->fat_cachesize - 1);
40 ULONG num;
41 UWORD i;
43 /* if the target cluster is not within the currently loaded chunk of fat,
44 * we need to get the right data in */
45 if (sb->fat_cache_block != entry_cache_block) {
46 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)));
47 /* put the old ones back */
48 if (sb->fat_cache_block != 0xffffffff)
49 for (i = 0; i < sb->fat_blocks_count; i++)
50 Cache_FreeBlock(sb->cache, sb->fat_blocks[i]);
52 /* load some more */
53 num = sb->first_device_sector + sb->first_fat_sector
54 + (entry_cache_block
55 << (sb->fat_cachesize_bits - sb->sectorsize_bits));
56 for (i = 0; i < sb->fat_blocks_count; i++)
57 sb->fat_blocks[i] =
58 Cache_GetBlock(sb->cache, num + i, &sb->fat_buffers[i]);
60 /* remember where we are for next time */
61 sb->fat_cache_block = entry_cache_block;
64 /* give the block back if they asked for it (needed to mark the block
65 * dirty if they're writing */
66 if (rb != NULL)
67 *rb = sb->fat_blocks[entry_cache_offset >> sb->sectorsize_bits];
69 /* compute the pointer location and return it */
70 return sb->fat_buffers[entry_cache_offset >> sb->sectorsize_bits] +
71 (entry_cache_offset & (sb->sectorsize - 1));
74 /* FAT12 has, as the name suggests, 12-bit FAT entries. This means that two
75 * entries are condensed into three bytes, like so:
77 * entry: aaaaaaaa aaaabbbb bbbbbbbb
78 * bytes: xxxxxxxx xxxxxxxx xxxxxxxx
80 * To get at the entry we want, we find and grab the word starting at either
81 * byte 0 or 1 of the three-byte set, then shift up or down as needed. FATdoc
82 * 1.03 p16-17 describes the method
84 * The only tricky bit is if the word falls such that the first byte is the
85 * last byte of the block and the second byte is the first byte of the next
86 * block. Since our block data are stored within cache block structures, a
87 * simple cast won't do (hell, the second block may not even be in memory if
88 * we're at the end of the FAT cache). So we get it a byte at a time, and
89 * build the word ourselves.
91 static ULONG GetFat12Entry(struct FSSuper *sb, ULONG n) {
92 ULONG offset = n + n/2;
93 UWORD val;
95 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
96 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
98 val = *GetFatEntryPtr(sb, offset + 1, NULL) << 8;
99 val |= *GetFatEntryPtr(sb, offset, NULL);
101 else
102 val = AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, offset, NULL)));
104 if (n & 1)
105 val >>= 4;
106 else
107 val &= 0xfff;
109 return val;
113 * FAT16 and FAT32, on the other hand, have nice neat entry widths, so simple
114 * word/long casts are fine. There's also no chance that the entry can be
115 * split across blocks. Why can't everything be this simple?
117 static ULONG GetFat16Entry(struct FSSuper *sb, ULONG n) {
118 return AROS_LE2WORD(*((UWORD *) GetFatEntryPtr(sb, n << 1, NULL)));
121 static ULONG GetFat32Entry(struct FSSuper *sb, ULONG n) {
122 return AROS_LE2LONG(*((ULONG *) GetFatEntryPtr(sb, n << 2, NULL)))
123 & 0x0fffffff;
126 static void SetFat12Entry(struct FSSuper *sb, ULONG n, ULONG val) {
127 APTR b;
128 ULONG offset = n + n/2;
129 BOOL boundary = FALSE;
130 UWORD *fat = NULL, newval;
132 if ((offset & (sb->sectorsize-1)) == sb->sectorsize-1) {
133 boundary = TRUE;
135 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
137 newval = *GetFatEntryPtr(sb, offset + 1, NULL) << 8;
138 newval |= *GetFatEntryPtr(sb, offset, NULL);
140 else {
141 fat = (UWORD *) GetFatEntryPtr(sb, offset, &b);
142 newval = AROS_LE2WORD(*fat);
145 if (n & 1) {
146 val <<= 4;
147 newval = (newval & 0xf) | val;
149 else {
150 newval = (newval & 0xf000) | val;
153 if (boundary) {
154 /* XXX ideally we'd mark both blocks dirty at the same time or only do
155 * it once if they're the same block. unfortunately b is essentially
156 * invalid after a call to GetFatEntryPtr, as it may have swapped the
157 * previous cache out. This is probably safe enough. */
158 *GetFatEntryPtr(sb, offset+1, &b) = newval >> 8;
159 Cache_MarkBlockDirty(sb->cache, b);
160 *GetFatEntryPtr(sb, offset, &b) = newval & 0xff;
161 Cache_MarkBlockDirty(sb->cache, b);
163 else {
164 *fat = AROS_WORD2LE(newval);
165 Cache_MarkBlockDirty(sb->cache, b);
169 static void SetFat16Entry(struct FSSuper *sb, ULONG n, ULONG val) {
170 APTR b;
172 *((UWORD *) GetFatEntryPtr(sb, n << 1, &b)) = AROS_WORD2LE((UWORD) val);
174 Cache_MarkBlockDirty(sb->cache, b);
177 static void SetFat32Entry(struct FSSuper *sb, ULONG n, ULONG val) {
178 APTR b;
179 ULONG *fat = (ULONG *) GetFatEntryPtr(sb, n << 2, &b);
181 *fat = (*fat & 0xf0000000) | val;
183 Cache_MarkBlockDirty(sb->cache, b);
186 LONG ReadFATSuper(struct FSSuper *sb ) {
187 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
188 LONG err;
189 ULONG bsize = de->de_SizeBlock * 4;
190 struct FATBootSector *boot;
191 struct FATFSInfo *fsinfo;
192 BOOL invalid = FALSE;
193 ULONG end;
195 D(bug("[fat] reading boot sector\n"));
197 boot = AllocMem(bsize, MEMF_ANY);
198 if (!boot)
199 return ERROR_NO_FREE_STORE;
202 * Read the boot sector. We go direct because we don't have a cache yet,
203 * and can't create one until we know the sector size, which is held in
204 * the boot sector. In practice it doesn't matter - we're going to use
205 * this once and once only.
207 sb->first_device_sector =
208 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
210 D(bug("[fat] boot sector at sector %ld\n", sb->first_device_sector));
212 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize, (UBYTE *)boot)) != 0) {
213 D(bug("[fat] couldn't read boot block (%ld)\n", err));
214 FreeMem(boot, bsize);
215 return err;
218 D(bug("\tBoot sector:\n"));
220 sb->sectorsize = AROS_LE2WORD(boot->bpb_bytes_per_sect);
221 sb->sectorsize_bits = log2(sb->sectorsize);
222 D(bug("\tSectorSize = %ld\n", sb->sectorsize));
223 D(bug("\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 D(bug("\tSectorsPerCluster = %ld\n", (ULONG)boot->bpb_sect_per_clust));
231 D(bug("\tClusterSize = %ld\n", sb->clustersize));
232 D(bug("\tClusterSize Bits = %ld\n", sb->clustersize_bits));
233 D(bug("\tCluster Sectors Bits = %ld\n", sb->cluster_sectors_bits));
235 sb->first_fat_sector = AROS_LE2WORD(boot->bpb_rsvd_sect_count);
236 D(bug("\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 D(bug("\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 D(bug("\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 D(bug("\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 D(bug("\tData Sectors = %ld\n", sb->data_sectors));
256 sb->clusters_count = sb->data_sectors >> sb->cluster_sectors_bits;
257 D(bug("\tClusters Count = %ld\n", sb->clusters_count));
259 sb->first_rootdir_sector = sb->first_fat_sector + (boot->bpb_num_fats * sb->fat_size);
260 D(bug("\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 D(bug("\tFirst Data Sector = %ld\n", sb->first_data_sector));
265 sb->free_clusters = 0xffffffff;
267 /* check if disk is in fact a 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 (boot->bpb_signature[0] != 0x55 || boot->bpb_signature[1] != 0xaa)
292 invalid = TRUE;
294 if (invalid) {
295 D(bug("\tInvalid FAT Boot Sector\n"));
296 FreeMem(boot, bsize);
297 return ERROR_NOT_A_DOS_DISK;
299 end = 0xFFFFFFFF / sb->sectorsize;
300 if ((sb->first_device_sector + sb->total_sectors - 1 > end) && (glob->readcmd == CMD_READ)) {
301 D(bug("\tDevice is too large\n"));
302 FreeMem(boot, bsize);
303 return IOERR_BADADDRESS;
306 sb->cache = Cache_CreateCache(64, 64, sb->sectorsize);
308 if (sb->clusters_count < 4085) {
309 D(bug("\tFAT12 filesystem detected\n"));
310 sb->type = 12;
311 sb->eoc_mark = 0x0FFF;
312 sb->func_get_fat_entry = GetFat12Entry;
313 sb->func_set_fat_entry = SetFat12Entry;
315 else if (sb->clusters_count < 65525) {
316 D(bug("\tFAT16 filesystem detected\n"));
317 sb->type = 16;
318 sb->eoc_mark = 0xFFFF;
319 sb->func_get_fat_entry = GetFat16Entry;
320 sb->func_set_fat_entry = SetFat16Entry;
322 else {
323 D(bug("\tFAT32 filesystem detected\n"));
324 sb->type = 32;
325 sb->eoc_mark = 0x0FFFFFFF;
326 sb->func_get_fat_entry = GetFat32Entry;
327 sb->func_set_fat_entry = SetFat32Entry;
330 /* setup the FAT cache and load the first blocks */
331 sb->fat_cachesize = 4096;
332 sb->fat_cachesize_bits = log2(sb->fat_cachesize);
333 sb->fat_cache_block = 0xffffffff;
335 sb->fat_blocks_count =
336 MIN(sb->fat_size, sb->fat_cachesize >> sb->sectorsize_bits);
337 sb->fat_blocks = AllocVecPooled(glob->mempool,
338 sizeof(APTR) * sb->fat_blocks_count);
339 sb->fat_buffers = AllocVecPooled(glob->mempool,
340 sizeof(APTR) * sb->fat_blocks_count);
342 if (sb->type != 32) { /* FAT 12/16 */
343 /* setup volume id */
344 sb->volume_id = AROS_LE2LONG(boot->type.fat16.bs_volid);
346 /* location of root directory */
347 sb->rootdir_cluster = 0;
348 sb->rootdir_sector = sb->first_rootdir_sector;
350 else {
351 /* setup volume id */
352 sb->volume_id = AROS_LE2LONG(boot->type.fat32.bs_volid);
354 /* location of root directory */
355 sb->rootdir_cluster = AROS_LE2LONG(boot->type.fat32.bpb_root_cluster);
356 sb->rootdir_sector = 0;
359 D(bug("[fat] rootdir at cluster %ld sector %ld\n", sb->rootdir_cluster, sb->rootdir_sector));
361 if (GetVolumeIdentity(sb, &(sb->volume)) != 0) {
362 LONG i;
363 UBYTE *uu = (void *)&sb->volume_id;
365 for (i=1; i<10;) {
366 int d;
368 if (i==5)
369 sb->volume.name[i++]='-';
371 d = (*uu) & 0x0f;
372 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
373 d = ((*uu) & 0xf0)>>4;
374 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
376 uu++;
379 sb->volume.name[i] = '\0';
380 sb->volume.name[0] = 9;
383 /* get initial number of free clusters */
384 sb->free_clusters = -1;
385 if (sb->type == 32) {
386 sb->fsinfo_block = Cache_GetBlock(sb->cache, sb->first_device_sector
387 + AROS_LE2WORD(boot->type.fat32.bpb_fs_info), (UBYTE **)&fsinfo);
388 if (sb->fsinfo_block != NULL) {
389 if (fsinfo->lead_sig == AROS_LONG2LE(FSI_LEAD_SIG)
390 && fsinfo->struct_sig == AROS_LONG2LE(FSI_STRUCT_SIG)
391 && fsinfo->trail_sig == AROS_LONG2LE(FSI_TRAIL_SIG)) {
392 sb->free_clusters = AROS_LE2LONG(fsinfo->free_count);
393 fsinfo->next_free = -1;
394 D(bug("[fat] valid FATFSInfo block found\n"));
395 sb->fsinfo_buffer = fsinfo;
397 else
398 Cache_FreeBlock(sb->cache, sb->fsinfo_block);
401 if (sb->free_clusters == -1)
402 CountFreeClusters(sb);
404 D(bug("\tFAT Filesystem successfully detected.\n"));
405 D(bug("\tFree Clusters = %ld\n", sb->free_clusters));
406 FreeMem(boot, bsize);
407 return 0;
410 LONG GetVolumeIdentity(struct FSSuper *sb, struct VolumeIdentity *volume) {
411 struct DirHandle dh;
412 struct DirEntry de;
413 LONG err;
414 int i;
416 D(bug("[fat] searching root directory for volume name\n"));
418 /* search the directory for the volume id entry. it would've been nice to
419 * just use GetNextDirEntry but I didn't want a flag or something to tell
420 * it not to skip the volume name */
421 InitDirHandle(sb, sb->rootdir_cluster, &dh, FALSE);
423 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
425 /* match the volume id entry */
426 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
427 && de.e.entry.name[0] != 0xe5) {
428 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
430 /* copy the name in. volume->name is a BSTR */
432 volume->name[1] = de.e.entry.name[0];
434 for (i = 1; i < 11; i++) {
435 if (volume->name[i] == ' ')
436 volume->name[i+1] = de.e.entry.name[i];
437 else
438 volume->name[i+1] = tolower(de.e.entry.name[i]);
441 for (i = 10; volume->name[i+1] == ' '; i--);
442 volume->name[i+2] = '\0';
443 volume->name[0] = strlen(&(volume->name[1]));
445 /* get the volume creation date too */
446 ConvertFATDate(de.e.entry.create_date, de.e.entry.create_time, &volume->create_time);
448 D(bug("[fat] volume name is '%s'\n", &(volume->name[1])));
450 break;
453 /* bail out if we hit the end of the dir */
454 if (de.e.entry.name[0] == 0x00) {
455 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
456 err = ERROR_OBJECT_NOT_FOUND;
457 break;
461 ReleaseDirHandle(&dh);
462 return err;
465 LONG SetVolumeName(struct FSSuper *sb, UBYTE *name) {
466 struct DirHandle dh;
467 struct DirEntry de;
468 LONG err;
469 int i;
470 struct DosEnvec *dosenv = BADDR(glob->fssm->fssm_Environ);
471 ULONG bsize = dosenv->de_SizeBlock * 4;
472 struct FATBootSector *boot;
474 /* read boot block */
475 boot = AllocMem(bsize, MEMF_ANY);
476 if (!boot)
477 return ERROR_NO_FREE_STORE;
479 if ((err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize, (UBYTE *)boot)) != 0) {
480 D(bug("[fat] couldn't read boot block (%ld)\n", err));
481 FreeMem(boot, bsize);
482 return err;
485 D(bug("[fat] searching root directory for volume name\n"));
487 /* search the directory for the volume id entry. it would've been nice to
488 * just use GetNextDirEntry but I didn't want a flag or something to tell
489 * it not to skip the volume name */
490 InitDirHandle(sb, 0, &dh, FALSE);
492 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de)) == 0) {
494 /* match the volume id entry */
495 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
496 && de.e.entry.name[0] != 0xe5) {
497 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
498 err = 0;
499 break;
502 /* bail out if we hit the end of the dir */
503 if (de.e.entry.name[0] == 0x00) {
504 D(bug("[fat] found end-of-directory marker, volume name entry not found\n"));
505 err = ERROR_OBJECT_NOT_FOUND;
506 break;
510 /* create a new volume id entry if there wasn't one */
511 if (err != 0) {
512 err = AllocDirEntry(&dh, 0, &de);
513 if (err == 0) {
514 memset(&de.e.entry, 0, sizeof(struct FATDirEntry));
515 de.e.entry.attr = ATTR_VOLUME_ID;
519 /* copy the name in. name is a BSTR */
520 if (err == 0) {
521 de.e.entry.name[0] = name[1];
522 for (i = 0; i < 11; i++)
523 if (i < name[0])
524 de.e.entry.name[i] = toupper(name[i+1]);
525 else
526 de.e.entry.name[i] = ' ';
528 if ((err = UpdateDirEntry(&de)) != 0) {
529 D(bug("[fat] couldn't change volume name\n"));
530 return err;
534 /* copy name to boot block as well, and save */
535 if (sb->type == 32)
536 CopyMem(de.e.entry.name, boot->type.fat32.bs_vollab, 11);
537 else
538 CopyMem(de.e.entry.name, boot->type.fat16.bs_vollab, 11);
540 if ((err = AccessDisk(TRUE, sb->first_device_sector, 1, bsize,
541 (UBYTE *)boot)) != 0)
542 D(bug("[fat] couldn't write boot block (%ld)\n", err));
543 FreeMem(boot, bsize);
545 /* update name in sb */
546 sb->volume.name[0] = name[0] <= 11 ? name[0] : 11;
547 CopyMem(&name[1], &(sb->volume.name[1]), sb->volume.name[0]);
548 sb->volume.name[sb->volume.name[0]+1] = '\0';
550 D(bug("[fat] new volume name is '%s'\n", &(sb->volume.name[1])));
552 ReleaseDirHandle(&dh);
553 return err;
556 LONG FindFreeCluster(struct FSSuper *sb, ULONG *rcluster) {
557 ULONG cluster = 0;
560 * XXX this implementation is extremely naive. things we
561 * could do to make it better:
563 * - don't start looking for a free cluster at the start
564 * each time. start from the current cluster and wrap
565 * around when we hit the end
566 * - track where we last found a free cluster and start
567 * from there
568 * - allocate several contiguous clusters at a time to
569 * reduce fragmentation
572 for (cluster = 2; cluster < sb->clusters_count && GET_NEXT_CLUSTER(sb, cluster) != 0; cluster++);
574 if (cluster == sb->clusters_count) {
575 D(bug("[fat] no more free clusters, we're out of space\n"));
576 return ERROR_DISK_FULL;
579 D(bug("[fat] found free cluster %ld\n", cluster));
581 *rcluster = cluster;
583 return 0;
586 void FreeFATSuper(struct FSSuper *sb) {
587 D(bug("\tRemoving Super Block from memory\n"));
588 Cache_DestroyCache(sb->cache);
589 FreeVecPooled(glob->mempool, sb->fat_buffers);
590 sb->fat_buffers = NULL;
591 FreeVecPooled(glob->mempool, sb->fat_blocks);
592 sb->fat_blocks = NULL;
595 /* see how many unused clusters are available */
596 void CountFreeClusters(struct FSSuper *sb) {
597 ULONG cluster = 0;
598 ULONG free = 0;
600 /* loop over all the data clusters */
601 for (cluster = 2; cluster < sb->clusters_count + 2; cluster++)
603 /* record the free ones */
604 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
605 free++;
607 /* put the value away for later */
608 sb->free_clusters = free;
610 D(bug("\tfree clusters: %ld\n", free));
613 void AllocCluster(struct FSSuper *sb, ULONG cluster) {
614 SET_NEXT_CLUSTER(sb, cluster, sb->eoc_mark);
615 sb->free_clusters--;
616 if (sb->fsinfo_buffer != NULL) {
617 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
618 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
622 void FreeCluster(struct FSSuper *sb, ULONG cluster) {
623 SET_NEXT_CLUSTER(sb, cluster, 0);
624 sb->free_clusters++;
625 if (sb->fsinfo_buffer != NULL) {
626 sb->fsinfo_buffer->free_count = AROS_LONG2LE(sb->free_clusters);
627 Cache_MarkBlockDirty(sb->cache, sb->fsinfo_block);
631 void ConvertFATDate(UWORD date, UWORD time, struct DateStamp *ds) {
632 ULONG year, month, day, hours, mins, secs;
633 struct ClockData clock_data;
635 /* date bits: yyyy yyym mmmd dddd */
636 year = (date & 0xfe00) >> 9; /* bits 15-9 */
637 month = (date & 0x01e0) >> 5; /* bits 8-5 */
638 day = date & 0x001f; /* bits 4-0 */
640 /* time bits: hhhh hmmm mmms ssss */
641 hours = (time & 0xf800) >> 11; /* bits 15-11 */
642 mins = (time & 0x07e0) >> 5; /* bits 8-5 */
643 secs = time & 0x001f; /* bits 4-0 */
645 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));
647 clock_data.year = 1980 + year;
648 clock_data.month = month;
649 clock_data.mday = day;
650 clock_data.hour = hours;
651 clock_data.min = mins;
652 clock_data.sec = secs << 1;
653 secs = Date2Amiga(&clock_data);
655 /* calculate days since 1978-01-01 (DOS epoch) */
656 ds->ds_Days = secs / (60 * 60 * 24);
658 /* minutes since midnight */
659 ds->ds_Minute = secs / 60 % (24 * 60);
661 /* 1/50 sec ticks since last minute */
662 ds->ds_Tick = secs % 60 * TICKS_PER_SECOND;
664 D(bug("[fat] converted fat date: days %ld minutes %ld ticks %ld\n", ds->ds_Days, ds->ds_Minute, ds->ds_Tick));
667 void ConvertAROSDate(struct DateStamp *ds, UWORD *date, UWORD *time) {
668 ULONG year, month, day, hours, mins, secs;
669 struct ClockData clock_data;
671 /* convert datestamp to seconds since 1978 */
672 secs = ds->ds_Days * 60 * 60 * 24 + ds->ds_Minute * 60
673 + ds->ds_Tick / TICKS_PER_SECOND;
675 /* convert seconds since 1978 to calendar/time data */
676 Amiga2Date(secs, &clock_data);
678 /* get values used in FAT dates */
679 year = clock_data.year - 1980;
680 month = clock_data.month - 0;
681 day = clock_data.mday;
682 hours = clock_data.hour;
683 mins = clock_data.min;
684 secs = clock_data.sec >> 1;
686 /* all that remains is to bit-encode the whole lot */
688 /* date bits: yyyy yyym mmmd dddd */
689 *date = (((ULONG) year) << 9) | (((ULONG) month) << 5) | day;
691 /* time bits: hhhh hmmm mmms ssss */
692 *time = (((ULONG) hours) << 11) | (((ULONG) mins) << 5) | secs;