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>
24 #include <clib/macros.h>
30 #include "fat_protos.h"
32 #define DEBUG DEBUG_MISC
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
,
39 ULONG entry_cache_block
= offset
>> sb
->fat_cachesize_bits
;
40 ULONG entry_cache_offset
= offset
& (sb
->fat_cachesize
- 1);
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
]);
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
++)
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) */
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;
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);
105 val
= AROS_LE2WORD(*((UWORD
*) GetFatEntryPtr(sb
, offset
, NULL
, 0)));
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)))
129 static void SetFat12Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
) {
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) {
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
);
146 fat
= (UWORD
*) GetFatEntryPtr(sb
, offset
, &b
, i
);
147 newval
= AROS_LE2WORD(*fat
);
152 newval
= (newval
& 0xf) | val
;
155 newval
= (newval
& 0xf000) | val
;
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
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
);
170 *fat
= AROS_WORD2LE(newval
);
171 Cache_MarkBlockDirty(sb
->cache
, b
);
176 static void SetFat16Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
) {
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
) {
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
);
206 ULONG bsize
= de
->de_SizeBlock
* 4;
207 struct FATBootSector
*boot
;
208 struct FATFSInfo
*fsinfo
;
209 BOOL invalid
= FALSE
;
212 D(bug("[fat] reading boot sector\n"));
214 boot
= AllocMem(bsize
, MEMF_ANY
);
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
);
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
);
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
);
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)
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)
295 /* valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
296 if (sb
->clustersize
> 64 * 1024)
299 if (sb
->first_fat_sector
== 0)
302 if (sb
->fat_count
== 0)
305 if (boot
->bpb_media
< 0xF0)
308 /* FAT "signature" */
309 if (boot
->bpb_signature
[0] != 0x55 || boot
->bpb_signature
[1] != 0xaa)
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"));
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"));
336 sb
->eoc_mark
= 0xFFFF;
337 sb
->func_get_fat_entry
= GetFat16Entry
;
338 sb
->func_set_fat_entry
= SetFat16Entry
;
341 D(bug("\tFAT32 filesystem detected\n"));
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
;
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) {
381 UBYTE
*uu
= (void *)&sb
->volume_id
;
387 sb
->volume
.name
[i
++]='-';
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
;
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
;
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
);
432 LONG
GetVolumeIdentity(struct FSSuper
*sb
, struct VolumeIdentity
*volume
) {
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
];
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])));
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
;
483 ReleaseDirHandle(&dh
);
487 LONG
SetVolumeName(struct FSSuper
*sb
, UBYTE
*name
) {
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
);
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
);
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
));
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
;
532 /* create a new volume id entry if there wasn't one */
534 err
= AllocDirEntry(&dh
, 0, &de
);
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 */
543 de
.e
.entry
.name
[0] = name
[1];
544 for (i
= 0; i
< 11; i
++)
546 de
.e
.entry
.name
[i
] = toupper(name
[i
+1]);
548 de
.e
.entry
.name
[i
] = ' ';
550 if ((err
= UpdateDirEntry(&de
)) != 0) {
551 D(bug("[fat] couldn't change volume name\n"));
556 /* copy name to boot block as well, and save */
558 CopyMem(de
.e
.entry
.name
, boot
->type
.fat32
.bs_vollab
, 11);
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
);
578 LONG
FindFreeCluster(struct FSSuper
*sb
, ULONG
*rcluster
) {
582 for (cluster
= sb
->next_cluster
;
583 cluster
< 2 + sb
->clusters_count
&& !found
;
586 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
595 for (cluster
= 2; cluster
< sb
->next_cluster
&& !found
;
598 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
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
));
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
) {
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)
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
);
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);
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
;