revert between 56095 -> 55830 in arch
[AROS.git] / rom / filesys / fat / fat.c
blob2bff6b3b51b71687cbd3b69c099d26968dd32c27
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 <exec/types.h>
15 #include "fat_fs.h"
16 #include "fat_protos.h"
18 #define DEBUG DEBUG_MISC
19 #include "debug.h"
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,
24 UWORD fat_no)
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);
29 ULONG num;
30 UWORD i;
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",
38 sb->fat_blocks_count,
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;
50 /* Load some more */
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++)
56 sb->fat_blocks[i] =
57 Cache_GetBlock(sb->cache, num + i, &sb->fat_buffers[i]);
59 if (sb->fat_blocks[i] == NULL)
61 while (i-- != 0)
62 Cache_FreeBlock(sb->cache, sb->fat_blocks[i]);
63 return NULL;
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) */
74 if (rb != NULL)
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;
103 UBYTE *p1;
104 UWORD val = 0, *p2;
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);
111 if (p1 != NULL)
113 val = *p1 << 8;
114 p1 = GetFatEntryPtr(sb, offset, NULL, 0);
115 if (p1 != NULL)
116 val |= *p1;
117 else
118 val = 0;
121 else
123 p2 = GetFatEntryPtr(sb, offset, NULL, 0);
124 if (p2 != NULL)
125 val = AROS_LE2WORD(*p2);
128 if (n & 1)
129 val >>= 4;
130 else
131 val &= 0xfff;
133 return val;
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)
143 UWORD val = 0, *p;
145 p = GetFatEntryPtr(sb, n << 1, NULL, 0);
146 if (p != NULL)
147 val = AROS_LE2WORD(*p);
149 return val;
152 ULONG GetFat32Entry(struct FSSuper *sb, ULONG n)
154 ULONG val = 0, *p;
156 p = GetFatEntryPtr(sb, n << 2, NULL, 0);
157 if (p != NULL)
158 val = AROS_LE2LONG(*p) & 0x0fffffff;
160 return val;
163 BOOL SetFat12Entry(struct FSSuper *sb, ULONG n, ULONG val)
165 D(struct Globals *glob = sb->glob);
166 APTR b;
167 ULONG offset = n + n / 2;
168 BOOL success = TRUE, boundary = FALSE;
169 UBYTE *p1;
170 UWORD *p2, newval, i;
172 for (i = 0; i < sb->fat_count; i++)
174 if ((offset & (sb->sectorsize - 1)) == sb->sectorsize - 1)
176 boundary = TRUE;
178 D(bug(
179 "[fat] fat12 cluster pair on block boundary, compensating\n"));
181 p1 = GetFatEntryPtr(sb, offset + 1, NULL, i);
182 if (p1 != NULL)
184 newval = *p1 << 8;
185 p1 = GetFatEntryPtr(sb, offset, NULL, i);
186 if (p1 != NULL)
187 newval |= *p1;
188 else
189 success = FALSE;
191 else
192 success = FALSE;
194 else
196 p2 = (UWORD *) GetFatEntryPtr(sb, offset, &b, i);
197 if (p2 != NULL)
198 newval = AROS_LE2WORD(*p2);
199 else
200 success = FALSE;
203 if (success)
205 if (n & 1)
206 newval = (newval & 0xf) | val << 4;
207 else
208 newval = (newval & 0xf000) | val;
210 if (boundary)
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
216 * safe enough. */
217 p1 = GetFatEntryPtr(sb, offset + 1, &b, i);
218 if (p1 != NULL)
220 *p1 = newval >> 8;
221 Cache_MarkBlockDirty(sb->cache, b);
222 p1 = GetFatEntryPtr(sb, offset, &b, i);
223 if (p1 != NULL)
225 *p1 = newval & 0xff;
226 Cache_MarkBlockDirty(sb->cache, b);
228 else
229 success = FALSE;
231 else
232 success = FALSE;
234 else
236 *p2 = AROS_WORD2LE(newval);
237 Cache_MarkBlockDirty(sb->cache, b);
242 return success;
245 BOOL SetFat16Entry(struct FSSuper *sb, ULONG n, ULONG val)
247 BOOL success = TRUE;
248 APTR b;
249 UWORD i, *p;
251 for (i = 0; i < sb->fat_count; i++)
253 p = GetFatEntryPtr(sb, n << 1, &b, i);
254 if (p != NULL)
256 *p = AROS_WORD2LE((UWORD) val);
257 Cache_MarkBlockDirty(sb->cache, b);
259 else
260 success = FALSE;
263 return success;
266 BOOL SetFat32Entry(struct FSSuper *sb, ULONG n, ULONG val)
268 BOOL success = TRUE;
269 APTR b;
270 ULONG *p;
271 UWORD i;
273 for (i = 0; i < sb->fat_count; i++)
275 p = (ULONG *) GetFatEntryPtr(sb, n << 2, &b, i);
276 if (p != NULL)
278 *p = AROS_LONG2LE((AROS_LE2LONG(*p) & 0xf0000000) | val);
279 Cache_MarkBlockDirty(sb->cache, b);
281 else
282 success = FALSE;
285 return success;
288 LONG FindFreeCluster(struct FSSuper *sb, ULONG *rcluster)
290 D(struct Globals *glob = sb->glob);
291 ULONG cluster = 0;
292 BOOL found = FALSE;
294 for (cluster = sb->next_cluster;
295 cluster < 2 + sb->clusters_count && !found; cluster++)
297 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
299 *rcluster = cluster;
300 found = TRUE;
304 if (!found)
306 for (cluster = 2; cluster < sb->next_cluster && !found; cluster++)
308 if (GET_NEXT_CLUSTER(sb, cluster) == 0)
310 *rcluster = cluster;
311 found = TRUE;
316 if (!found)
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));
326 return 0;
329 /* See how many unused clusters are available */
330 void CountFreeClusters(struct FSSuper *sb)
332 D(struct Globals *glob = sb->glob);
333 ULONG cluster = 0;
334 ULONG free = 0;
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)
341 free++;
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);
353 sb->free_clusters--;
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);
365 sb->free_clusters++;
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);