2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2008 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>
15 #include <dos/dosextens.h>
16 #include <dos/filehandler.h>
18 #include <proto/exec.h>
19 #include <proto/dos.h>
22 #include "fat_protos.h"
24 #define DEBUG DEBUG_FILE
27 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
32 static void fat_hexdump(unsigned char *buf
, int bufsz
) {
36 /* do this in chunks of CHUNK bytes */
37 for (i
=0; i
<bufsz
; i
+=CHUNK
) {
41 /* max of CHUNK or remaining bytes */
42 count
= ((bufsz
-i
) > CHUNK
? CHUNK
: bufsz
-i
);
45 for (j
=0; j
<count
; j
++) {
46 if (j
==CHUNK
/2) bug(" ");
47 bug("%02x ",buf
[i
+j
]);
50 /* pad with spaces if less than CHUNK */
51 for (j
=count
; j
<CHUNK
; j
++) {
52 if (j
==CHUNK
/2) bug(" ");
56 /* divider between hex and ascii */
59 for (j
=0; j
<count
; j
++)
60 bug("%c",(isprint(buf
[i
+j
]) ? buf
[i
+j
] : '.'));
66 #define fat_hexdump(b,c)
69 LONG
ReadFileChunk(struct IOHandle
*ioh
, ULONG file_pos
, ULONG nwant
, UBYTE
*data
, ULONG
*nread
) {
71 ULONG sector_offset
, byte_offset
, cluster_offset
;
72 struct cache_block
*b
;
75 /* files with no data can't be read from */
76 if (ioh
->first_cluster
== 0xffffffff) {
77 D(bug("[fat] file has no first cluster, so nothing to read!\n"));
78 return ERROR_OBJECT_NOT_FOUND
;
81 /* figure out how far into the file to look for the requested data */
82 sector_offset
= file_pos
>> ioh
->sb
->sectorsize_bits
;
83 byte_offset
= file_pos
& (ioh
->sb
->sectorsize
-1);
85 /* loop until we get all we want */
89 D(bug("[fat] trying to read %ld bytes (%ld sectors + %ld bytes into the file)\n", nwant
, sector_offset
, byte_offset
));
91 /* move clusters if necessary */
92 cluster_offset
= sector_offset
>> ioh
->sb
->cluster_sectors_bits
;
93 if (ioh
->cluster_offset
!= cluster_offset
&& ioh
->first_cluster
!= 0) {
96 /* if we're already ahead of the wanted cluster, then we need to
97 * go back to the start of the cluster list */
98 if (ioh
->cluster_offset
> cluster_offset
) {
99 ioh
->cur_cluster
= ioh
->first_cluster
;
100 ioh
->cluster_offset
= 0;
103 D(bug("[fat] moving forward %ld clusters from cluster %ld\n", cluster_offset
- ioh
->cluster_offset
, ioh
->cur_cluster
));
106 for (i
= 0; i
< cluster_offset
- ioh
->cluster_offset
; i
++) {
107 /* get the next one */
108 ioh
->cur_cluster
= GET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
);
110 /* if it was free (shouldn't happen) or we hit the end of the
111 * chain, the requested data isn't here */
112 if (ioh
->cur_cluster
== 0 || ioh
->cur_cluster
>= ioh
->sb
->eoc_mark
) {
113 D(bug("[fat] hit empty or eoc cluster, no more file left\n"));
117 return ERROR_OBJECT_NOT_FOUND
;
121 /* remember how far in we are now */
122 ioh
->cluster_offset
= cluster_offset
;
124 D(bug("[fat] moved to cluster %ld\n", ioh
->cur_cluster
));
126 /* reset the sector offset so the sector recalc gets triggered */
127 ioh
->sector_offset
= 0xffffffff;
130 /* recalculate the sector location if we moved */
131 if (ioh
->sector_offset
!= (sector_offset
& (ioh
->sb
->cluster_sectors
-1))
132 || ioh
->first_cluster
== 0) {
134 /* work out how many sectors in we should be looking */
135 ioh
->sector_offset
= sector_offset
& (ioh
->sb
->cluster_sectors
-1);
137 /* simple math to find the absolute sector number */
138 ioh
->cur_sector
= SECTOR_FROM_CLUSTER(ioh
->sb
, ioh
->cur_cluster
) + ioh
->sector_offset
;
140 /* if the first cluster is zero, we use sector addressing instead
141 * of clusters. this is a hack to support fat12/16 root dirs, which
142 * live before the data region */
143 if (ioh
->first_cluster
== 0) {
144 ioh
->sector_offset
= sector_offset
- ioh
->first_sector
;
145 ioh
->cur_sector
= ioh
->first_sector
+ sector_offset
;
147 D(bug("[fat] adjusted for cluster 0, chunk starts in sector %ld\n", ioh
->cur_sector
));
151 D(bug("[fat] chunk starts %ld sectors into the cluster, which is sector %ld\n", ioh
->sector_offset
, ioh
->cur_sector
));
154 /* if we don't have the wanted block kicking around, we need to bring it
155 * in from the cache */
156 if (ioh
->block
== NULL
|| ioh
->cur_sector
!= ioh
->block
->num
) {
157 if (ioh
->block
!= NULL
) {
158 cache_put_block(ioh
->sb
->cache
, ioh
->block
, 0);
162 D(bug("[fat] requesting sector %ld from cache\n", ioh
->cur_sector
));
164 err
= cache_get_block(ioh
->sb
->cache
, ioh
->sb
->first_device_sector
+ ioh
->cur_sector
, 0, &b
);
168 D(bug("[fat] couldn't load sector, returning error %ld\n", err
));
177 D(bug("[fat] using cached sector %ld\n", ioh
->cur_sector
));
179 /* now copy in the data */
180 ncopy
= ioh
->sb
->sectorsize
- byte_offset
;
181 if (ncopy
> nwant
) ncopy
= nwant
;
182 CopyMem(&(ioh
->block
->data
[byte_offset
]), &(data
[pos
]), ncopy
);
184 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
185 D(bug("[fat] dump of last read, %ld bytes:\n", ncopy
));
186 fat_hexdump(&(data
[pos
]), ncopy
);
192 D(bug("[fat] copied %ld bytes, want %ld more\n", ncopy
, nwant
));
205 LONG
WriteFileChunk(struct IOHandle
*ioh
, ULONG file_pos
, ULONG nwant
, UBYTE
*data
, ULONG
*nwritten
) {
207 ULONG sector_offset
, byte_offset
, cluster_offset
;
208 struct cache_block
*b
;
211 /* figure out how far into the file to start */
212 sector_offset
= file_pos
>> ioh
->sb
->sectorsize_bits
;
213 byte_offset
= file_pos
& (ioh
->sb
->sectorsize
-1);
215 /* loop until we've finished writing */
219 D(bug("[fat] trying to write %ld bytes (%ld sectors + %ld bytes into the file)\n", nwant
, sector_offset
, byte_offset
));
221 /* move clusters if necessary */
222 cluster_offset
= sector_offset
>> ioh
->sb
->cluster_sectors_bits
;
223 if (ioh
->cluster_offset
!= cluster_offset
&& ioh
->first_cluster
!= 0) {
226 /* if we have no first cluster, this is a new file. we allocate
227 * the first cluster and then update the ioh */
228 if (ioh
->first_cluster
== 0xffffffff) {
231 D(bug("[fat] no first cluster, allocating one\n"));
233 /* allocate a cluster */
234 if ((err
= FindFreeCluster(ioh
->sb
, &cluster
)) != 0) {
239 /* mark the cluster used */
240 SET_NEXT_CLUSTER(ioh
->sb
, cluster
, ioh
->sb
->eoc_mark
);
242 /* now setup the ioh */
243 ioh
->first_cluster
= cluster
;
247 /* if we're already ahead of the wanted cluster, then we need to
248 * go back to the start of the cluster list */
249 if (ioh
->cluster_offset
> cluster_offset
) {
250 ioh
->cur_cluster
= ioh
->first_cluster
;
251 ioh
->cluster_offset
= 0;
254 D(bug("[fat] moving forward %ld clusters from cluster %ld\n", cluster_offset
- ioh
->cluster_offset
, ioh
->cur_cluster
));
257 for (i
= 0; i
< cluster_offset
- ioh
->cluster_offset
; i
++) {
258 /* get the next one */
259 ULONG next_cluster
= GET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
);
261 /* if it was free (shouldn't happen) or we hit the end of the
262 * chain, there is no next cluster, so we have to allocate a
264 if (next_cluster
== 0 || next_cluster
>= ioh
->sb
->eoc_mark
) {
265 D(bug("[fat] hit empty or eoc cluster, allocating another\n"));
267 if ((err
= FindFreeCluster(ioh
->sb
, &next_cluster
)) != 0) {
272 /* link the current cluster to the new one */
273 SET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
, next_cluster
);
275 /* and mark the new one used */
276 SET_NEXT_CLUSTER(ioh
->sb
, next_cluster
, ioh
->sb
->eoc_mark
);
278 ioh
->cur_cluster
= next_cluster
;
280 D(bug("[fat] allocated cluster %d\n", next_cluster
));
284 ioh
->cur_cluster
= next_cluster
;
287 /* remember how far in we are now */
288 ioh
->cluster_offset
= cluster_offset
;
290 D(bug("[fat] moved to cluster %ld\n", ioh
->cur_cluster
));
292 /* reset the sector offset so the sector recalc gets triggered */
293 ioh
->sector_offset
= 0xffffffff;
296 /* recalculate the sector location if we moved */
297 if (ioh
->sector_offset
!= (sector_offset
& (ioh
->sb
->cluster_sectors
-1))
298 || ioh
->first_cluster
== 0) {
300 /* work out how many sectors in we should be looking */
301 ioh
->sector_offset
= sector_offset
& (ioh
->sb
->cluster_sectors
-1);
303 /* simple math to find the absolute sector number */
304 ioh
->cur_sector
= SECTOR_FROM_CLUSTER(ioh
->sb
, ioh
->cur_cluster
) + ioh
->sector_offset
;
306 /* if the first cluster is zero, we use sector addressing instead
307 * of clusters. this is a hack to support fat12/16 root dirs, which
308 * live before the data region */
309 if (ioh
->first_cluster
== 0) {
310 ioh
->sector_offset
= sector_offset
- ioh
->first_sector
;
311 ioh
->cur_sector
= ioh
->first_sector
+ sector_offset
;
313 D(bug("[fat] adjusted for cluster 0, chunk starts in sector %ld\n", ioh
->cur_sector
));
317 D(bug("[fat] chunk starts %ld sectors into the cluster, which is sector %ld\n", ioh
->sector_offset
, ioh
->cur_sector
));
320 /* if we don't have the wanted block kicking around, we need to bring it
321 * in from the cache */
322 if (ioh
->block
== NULL
|| ioh
->cur_sector
!= ioh
->block
->num
) {
323 if (ioh
->block
!= NULL
) {
324 cache_put_block(ioh
->sb
->cache
, ioh
->block
, 0);
328 D(bug("[fat] requesting sector %ld from cache\n", ioh
->cur_sector
));
330 err
= cache_get_block(ioh
->sb
->cache
, ioh
->sb
->first_device_sector
+ ioh
->cur_sector
, 0, &b
);
334 D(bug("[fat] couldn't load sector, returning error %ld\n", err
));
343 D(bug("[fat] using cached sector %ld\n", ioh
->cur_sector
));
345 /* copy our data into the block */
346 ncopy
= ioh
->sb
->sectorsize
- byte_offset
;
347 if (ncopy
> nwant
) ncopy
= nwant
;
348 CopyMem(&(data
[pos
]), &(ioh
->block
->data
[byte_offset
]), ncopy
);
350 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
351 D(bug("[fat] dump of last write, %ld bytes:\n", ncopy
));
352 fat_hexdump(&(ioh
->block
->data
[byte_offset
]), ncopy
);
355 cache_mark_block_dirty(ioh
->sb
->cache
, ioh
->block
);
360 D(bug("[fat] wrote %ld bytes, want %ld more\n", ncopy
, nwant
));