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 <exec/types.h>
16 #include "fat_protos.h"
18 #define DEBUG DEBUG_MISC
21 /* Helper function to get the location of a fat entry for a cluster. It used
22 * to be a define until it got too crazy */
23 static APTR
GetFatEntryPtr(struct FSSuper
*sb
, ULONG offset
, APTR
*rb
,
26 D(struct Globals
*glob
= sb
->glob
);
27 ULONG entry_cache_block
= offset
>> sb
->fat_cachesize_bits
;
28 ULONG entry_cache_offset
= offset
& (sb
->fat_cachesize
- 1);
32 /* If the target cluster is not within the currently loaded chunk of fat,
33 * we need to get the right data in */
34 if (sb
->fat_cache_block
!= entry_cache_block
35 || sb
->fat_cache_no
!= fat_no
)
37 D(bug("[fat] loading %ld FAT sectors starting at sector %ld\n",
39 entry_cache_block
<< (sb
->fat_cachesize_bits
-
40 sb
->sectorsize_bits
)));
42 /* Put the old ones back */
43 if (sb
->fat_cache_block
!= 0xffffffff)
45 for (i
= 0; i
< sb
->fat_blocks_count
; i
++)
46 Cache_FreeBlock(sb
->cache
, sb
->fat_blocks
[i
]);
47 sb
->fat_cache_block
= 0xffffffff;
51 num
= sb
->first_device_sector
+ sb
->first_fat_sector
52 + sb
->fat_size
* fat_no
+ (entry_cache_block
53 << (sb
->fat_cachesize_bits
- sb
->sectorsize_bits
));
54 for (i
= 0; i
< sb
->fat_blocks_count
; i
++)
57 Cache_GetBlock(sb
->cache
, num
+ i
, &sb
->fat_buffers
[i
]);
59 if (sb
->fat_blocks
[i
] == NULL
)
62 Cache_FreeBlock(sb
->cache
, sb
->fat_blocks
[i
]);
67 /* Remember where we are for next time */
68 sb
->fat_cache_block
= entry_cache_block
;
69 sb
->fat_cache_no
= fat_no
;
72 /* Give the block back if they asked for it (needed to mark the block
73 * dirty if they're writing) */
75 *rb
= sb
->fat_blocks
[entry_cache_offset
>> sb
->sectorsize_bits
];
77 /* Compute the pointer location and return it */
78 return sb
->fat_buffers
[entry_cache_offset
>> sb
->sectorsize_bits
] +
79 (entry_cache_offset
& (sb
->sectorsize
- 1));
82 /* FAT12 has, as the name suggests, 12-bit FAT entries. This means that two
83 * entries are condensed into three bytes, like so:
85 * entry: aaaaaaaa aaaabbbb bbbbbbbb
86 * bytes: xxxxxxxx xxxxxxxx xxxxxxxx
88 * To get at the entry we want, we find and grab the word starting at either
89 * byte 0 or 1 of the three-byte set, then shift up or down as needed. FATdoc
90 * 1.03 p16-17 describes the method
92 * The only tricky bit is if the word falls such that the first byte is the
93 * last byte of the block and the second byte is the first byte of the next
94 * block. Since our block data are stored within cache block structures, a
95 * simple cast won't do (hell, the second block may not even be in memory if
96 * we're at the end of the FAT cache). So we get it a byte at a time, and
97 * build the word ourselves.
99 ULONG
GetFat12Entry(struct FSSuper
*sb
, ULONG n
)
101 D(struct Globals
*glob
= sb
->glob
);
102 ULONG offset
= n
+ n
/ 2;
106 if ((offset
& (sb
->sectorsize
- 1)) == sb
->sectorsize
- 1)
108 D(bug("[fat] fat12 cluster pair on block boundary, compensating\n"));
110 p1
= GetFatEntryPtr(sb
, offset
+ 1, NULL
, 0);
114 p1
= GetFatEntryPtr(sb
, offset
, NULL
, 0);
123 p2
= GetFatEntryPtr(sb
, offset
, NULL
, 0);
125 val
= AROS_LE2WORD(*p2
);
137 * FAT16 and FAT32, on the other hand, have nice neat entry widths, so simple
138 * word/long casts are fine. There's also no chance that the entry can be
139 * split across blocks. Why can't everything be this simple?
141 ULONG
GetFat16Entry(struct FSSuper
*sb
, ULONG n
)
145 p
= GetFatEntryPtr(sb
, n
<< 1, NULL
, 0);
147 val
= AROS_LE2WORD(*p
);
152 ULONG
GetFat32Entry(struct FSSuper
*sb
, ULONG n
)
156 p
= GetFatEntryPtr(sb
, n
<< 2, NULL
, 0);
158 val
= AROS_LE2LONG(*p
) & 0x0fffffff;
163 BOOL
SetFat12Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
)
165 D(struct Globals
*glob
= sb
->glob
);
167 ULONG offset
= n
+ n
/ 2;
168 BOOL success
= TRUE
, boundary
= FALSE
;
170 UWORD
*p2
, newval
, i
;
172 for (i
= 0; i
< sb
->fat_count
; i
++)
174 if ((offset
& (sb
->sectorsize
- 1)) == sb
->sectorsize
- 1)
179 "[fat] fat12 cluster pair on block boundary, compensating\n"));
181 p1
= GetFatEntryPtr(sb
, offset
+ 1, NULL
, i
);
185 p1
= GetFatEntryPtr(sb
, offset
, NULL
, i
);
196 p2
= (UWORD
*) GetFatEntryPtr(sb
, offset
, &b
, i
);
198 newval
= AROS_LE2WORD(*p2
);
206 newval
= (newval
& 0xf) | val
<< 4;
208 newval
= (newval
& 0xf000) | val
;
212 /* XXX: Ideally we'd mark both blocks dirty at the same time or
213 * only do it once if they're the same block. Unfortunately any
214 * old value of b is invalid after a call to GetFatEntryPtr, as
215 * it may have swapped the previous cache out. This is probably
217 p1
= GetFatEntryPtr(sb
, offset
+ 1, &b
, i
);
221 Cache_MarkBlockDirty(sb
->cache
, b
);
222 p1
= GetFatEntryPtr(sb
, offset
, &b
, i
);
226 Cache_MarkBlockDirty(sb
->cache
, b
);
236 *p2
= AROS_WORD2LE(newval
);
237 Cache_MarkBlockDirty(sb
->cache
, b
);
245 BOOL
SetFat16Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
)
251 for (i
= 0; i
< sb
->fat_count
; i
++)
253 p
= GetFatEntryPtr(sb
, n
<< 1, &b
, i
);
256 *p
= AROS_WORD2LE((UWORD
) val
);
257 Cache_MarkBlockDirty(sb
->cache
, b
);
266 BOOL
SetFat32Entry(struct FSSuper
*sb
, ULONG n
, ULONG val
)
273 for (i
= 0; i
< sb
->fat_count
; i
++)
275 p
= (ULONG
*) GetFatEntryPtr(sb
, n
<< 2, &b
, i
);
278 *p
= (*p
& 0xf0000000) | val
;
279 Cache_MarkBlockDirty(sb
->cache
, b
);
288 LONG
FindFreeCluster(struct FSSuper
*sb
, ULONG
*rcluster
)
290 D(struct Globals
*glob
= sb
->glob
);
294 for (cluster
= sb
->next_cluster
;
295 cluster
< 2 + sb
->clusters_count
&& !found
; cluster
++)
297 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
306 for (cluster
= 2; cluster
< sb
->next_cluster
&& !found
; cluster
++)
308 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
318 D(bug("[fat] no more free clusters, we're out of space\n"));
319 return ERROR_DISK_FULL
;
322 sb
->next_cluster
= *rcluster
;
324 D(bug("[fat] found free cluster %ld\n", *rcluster
));
329 /* See how many unused clusters are available */
330 void CountFreeClusters(struct FSSuper
*sb
)
332 D(struct Globals
*glob
= sb
->glob
);
336 /* Loop over all the data clusters */
337 for (cluster
= 2; cluster
< sb
->clusters_count
+ 2; cluster
++)
339 /* Record the free ones */
340 if (GET_NEXT_CLUSTER(sb
, cluster
) == 0)
344 /* Put the value away for later */
345 sb
->free_clusters
= free
;
347 D(bug("\tfree clusters: %ld\n", free
));
350 void AllocCluster(struct FSSuper
*sb
, ULONG cluster
)
352 SET_NEXT_CLUSTER(sb
, cluster
, sb
->eoc_mark
);
354 if (sb
->fsinfo_buffer
!= NULL
)
356 sb
->fsinfo_buffer
->free_count
= AROS_LONG2LE(sb
->free_clusters
);
357 sb
->fsinfo_buffer
->next_free
= AROS_LONG2LE(sb
->next_cluster
);
358 Cache_MarkBlockDirty(sb
->cache
, sb
->fsinfo_block
);
362 void FreeCluster(struct FSSuper
*sb
, ULONG cluster
)
364 SET_NEXT_CLUSTER(sb
, cluster
, 0);
366 if (sb
->fsinfo_buffer
!= NULL
)
368 sb
->fsinfo_buffer
->free_count
= AROS_LONG2LE(sb
->free_clusters
);
369 Cache_MarkBlockDirty(sb
->cache
, sb
->fsinfo_block
);