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>
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 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
] : '.'));
69 LONG
ReadFileChunk(struct IOHandle
*ioh
, ULONG file_pos
, ULONG nwant
,
70 UBYTE
*data
, ULONG
*nread
) {
71 struct Globals
*glob
= ioh
->sb
->glob
;
72 ULONG sector_offset
, byte_offset
, cluster_offset
, old_sector
;
77 /* files with no data can't be read from */
78 if (ioh
->first_cluster
== 0xffffffff && nwant
> 0) {
79 D(bug("[fat] file has no first cluster, so nothing to read!\n"));
80 return ERROR_OBJECT_NOT_FOUND
;
83 /* figure out how far into the file to look for the requested data */
84 sector_offset
= file_pos
>> ioh
->sb
->sectorsize_bits
;
85 byte_offset
= file_pos
& (ioh
->sb
->sectorsize
-1);
87 /* loop until we get all we want */
91 D(bug("[fat] trying to read %ld bytes (%ld sectors + %ld bytes into the file)\n", nwant
, sector_offset
, byte_offset
));
93 /* move clusters if necessary */
94 cluster_offset
= sector_offset
>> ioh
->sb
->cluster_sectors_bits
;
95 if (ioh
->cluster_offset
!= cluster_offset
&& ioh
->first_cluster
!= 0) {
98 /* if we're already ahead of the wanted cluster, then we need to
99 * go back to the start of the cluster list */
100 if (ioh
->cluster_offset
> cluster_offset
) {
101 ioh
->cur_cluster
= ioh
->first_cluster
;
102 ioh
->cluster_offset
= 0;
105 D(bug("[fat] moving forward %ld clusters from cluster %ld\n",
106 cluster_offset
- ioh
->cluster_offset
, ioh
->cur_cluster
));
109 for (i
= 0; i
< cluster_offset
- ioh
->cluster_offset
; i
++) {
110 /* get the next one */
111 ioh
->cur_cluster
= GET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
);
113 /* if it was free (shouldn't happen) or we hit the end of the
114 * chain, the requested data isn't here */
115 if (ioh
->cur_cluster
== 0
116 || ioh
->cur_cluster
>= ioh
->sb
->eoc_mark
- 7) {
117 D(bug("[fat] hit empty or eoc cluster, no more file left\n"));
121 return ERROR_OBJECT_NOT_FOUND
;
125 /* remember how far in we are now */
126 ioh
->cluster_offset
= cluster_offset
;
128 D(bug("[fat] moved to cluster %ld\n", ioh
->cur_cluster
));
130 /* reset the sector offset so the sector recalc gets triggered */
131 ioh
->sector_offset
= 0xffffffff;
134 /* recalculate the sector location if we moved */
135 old_sector
= ioh
->cur_sector
;
136 if (ioh
->sector_offset
!= (sector_offset
& (ioh
->sb
->cluster_sectors
-1))
137 || ioh
->first_cluster
== 0) {
139 /* work out how many sectors in we should be looking */
140 ioh
->sector_offset
= sector_offset
& (ioh
->sb
->cluster_sectors
-1);
142 /* simple math to find the absolute sector number */
143 ioh
->cur_sector
= SECTOR_FROM_CLUSTER(ioh
->sb
, ioh
->cur_cluster
) + ioh
->sector_offset
;
145 /* if the first cluster is zero, we use sector addressing instead
146 * of clusters. this is a hack to support fat12/16 root dirs, which
147 * live before the data region */
148 if (ioh
->first_cluster
== 0) {
149 ioh
->sector_offset
= sector_offset
- ioh
->first_sector
;
150 ioh
->cur_sector
= ioh
->first_sector
+ sector_offset
;
152 /* Stop if we've reached the end of the root dir */
153 if (ioh
->cur_sector
>= ioh
->sb
->first_rootdir_sector
154 + ioh
->sb
->rootdir_sectors
) {
156 return ERROR_OBJECT_NOT_FOUND
;
159 D(bug("[fat] adjusted for cluster 0, chunk starts in sector %ld\n", ioh
->cur_sector
));
162 D(bug("[fat] chunk starts %ld sectors into the cluster, which is sector %ld\n", ioh
->sector_offset
, ioh
->cur_sector
));
165 /* if we don't have the wanted block kicking around, we need to bring it
166 * in from the cache */
167 if (ioh
->block
== NULL
|| ioh
->cur_sector
!= old_sector
) {
168 if (ioh
->block
!= NULL
) {
169 Cache_FreeBlock(ioh
->sb
->cache
, ioh
->block
);
173 D(bug("[fat] requesting sector %ld from cache\n", ioh
->cur_sector
));
175 b
= Cache_GetBlock(ioh
->sb
->cache
,
176 ioh
->sb
->first_device_sector
+ ioh
->cur_sector
, &p
);
180 D(bug("[fat] couldn't load sector, returning error %ld\n",
191 D(bug("[fat] using cached sector %ld\n", ioh
->cur_sector
));
193 /* now copy in the data */
194 ncopy
= ioh
->sb
->sectorsize
- byte_offset
;
195 if (ncopy
> nwant
) ncopy
= nwant
;
196 CopyMem(ioh
->data
+ byte_offset
, data
+ pos
, ncopy
);
198 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
199 D(bug("[fat] dump of last read, %ld bytes:\n", ncopy
));
200 HexDump(&(data
[pos
]), ncopy
);
206 D(bug("[fat] copied %ld bytes, want %ld more\n", ncopy
, nwant
));
219 LONG
WriteFileChunk(struct IOHandle
*ioh
, ULONG file_pos
, ULONG nwant
,
220 UBYTE
*data
, ULONG
*nwritten
) {
221 struct Globals
*glob
= ioh
->sb
->glob
;
223 ULONG sector_offset
, byte_offset
, cluster_offset
, old_sector
;
224 struct cache_block
*b
;
228 /* figure out how far into the file to start */
229 sector_offset
= file_pos
>> ioh
->sb
->sectorsize_bits
;
230 byte_offset
= file_pos
& (ioh
->sb
->sectorsize
-1);
232 /* loop until we've finished writing */
236 D(bug("[fat] trying to write %ld bytes (%ld sectors + %ld bytes into the file)\n", nwant
, sector_offset
, byte_offset
));
238 /* move clusters if necessary */
239 cluster_offset
= sector_offset
>> ioh
->sb
->cluster_sectors_bits
;
240 if (ioh
->cluster_offset
!= cluster_offset
&& ioh
->first_cluster
!= 0) {
243 /* if we have no first cluster, this is a new file. we allocate
244 * the first cluster and then update the ioh */
245 if (ioh
->first_cluster
== 0xffffffff) {
248 D(bug("[fat] no first cluster, allocating one\n"));
250 /* allocate a cluster */
251 if ((err
= FindFreeCluster(ioh
->sb
, &cluster
)) != 0) {
256 /* mark the cluster used */
257 AllocCluster(ioh
->sb
, cluster
);
259 /* now setup the ioh */
260 ioh
->first_cluster
= cluster
;
264 /* if we're already ahead of the wanted cluster, then we need to
265 * go back to the start of the cluster list */
266 if (ioh
->cluster_offset
> cluster_offset
) {
267 ioh
->cur_cluster
= ioh
->first_cluster
;
268 ioh
->cluster_offset
= 0;
271 D(bug("[fat] moving forward %ld clusters from cluster %ld\n", cluster_offset
- ioh
->cluster_offset
, ioh
->cur_cluster
));
274 for (i
= 0; i
< cluster_offset
- ioh
->cluster_offset
; i
++) {
275 /* get the next one */
276 ULONG next_cluster
= GET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
);
278 /* if it was free (shouldn't happen) or we hit the end of the
279 * chain, there is no next cluster, so we have to allocate a
281 if (next_cluster
== 0
282 || next_cluster
>= ioh
->sb
->eoc_mark
- 7) {
283 D(bug("[fat] hit empty or eoc cluster, allocating another\n"));
285 if ((err
= FindFreeCluster(ioh
->sb
, &next_cluster
)) != 0) {
290 /* link the current cluster to the new one */
291 SET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
, next_cluster
);
293 /* and mark the new one used */
294 AllocCluster(ioh
->sb
, next_cluster
);
296 ioh
->cur_cluster
= next_cluster
;
298 D(bug("[fat] allocated cluster %d\n", next_cluster
));
302 ioh
->cur_cluster
= next_cluster
;
305 /* remember how far in we are now */
306 ioh
->cluster_offset
= cluster_offset
;
308 D(bug("[fat] moved to cluster %ld\n", ioh
->cur_cluster
));
310 /* reset the sector offset so the sector recalc gets triggered */
311 ioh
->sector_offset
= 0xffffffff;
314 /* recalculate the sector location if we moved */
315 old_sector
= ioh
->cur_sector
;
316 if (ioh
->sector_offset
!= (sector_offset
& (ioh
->sb
->cluster_sectors
-1))
317 || ioh
->first_cluster
== 0) {
319 /* work out how many sectors in we should be looking */
320 ioh
->sector_offset
= sector_offset
& (ioh
->sb
->cluster_sectors
-1);
322 /* simple math to find the absolute sector number */
323 ioh
->cur_sector
= SECTOR_FROM_CLUSTER(ioh
->sb
, ioh
->cur_cluster
) + ioh
->sector_offset
;
325 /* if the first cluster is zero, we use sector addressing instead
326 * of clusters. this is a hack to support fat12/16 root dirs, which
327 * live before the data region */
328 if (ioh
->first_cluster
== 0) {
329 ioh
->sector_offset
= sector_offset
- ioh
->first_sector
;
330 ioh
->cur_sector
= ioh
->first_sector
+ sector_offset
;
332 D(bug("[fat] adjusted for cluster 0, chunk starts in sector %ld\n", ioh
->cur_sector
));
335 D(bug("[fat] chunk starts %ld sectors into the cluster, which is sector %ld\n", ioh
->sector_offset
, ioh
->cur_sector
));
338 /* if we don't have the wanted block kicking around, we need to bring it
339 * in from the cache */
340 if (ioh
->block
== NULL
|| ioh
->cur_sector
!= old_sector
) {
341 if (ioh
->block
!= NULL
) {
342 Cache_FreeBlock(ioh
->sb
->cache
, ioh
->block
);
346 D(bug("[fat] requesting sector %ld from cache\n", ioh
->cur_sector
));
348 b
= Cache_GetBlock(ioh
->sb
->cache
, ioh
->sb
->first_device_sector
349 + ioh
->cur_sector
, &p
);
353 D(bug("[fat] couldn't load sector, returning error %ld\n", IoErr()));
362 D(bug("[fat] using cached sector %ld\n", ioh
->cur_sector
));
364 /* copy our data into the block */
365 ncopy
= ioh
->sb
->sectorsize
- byte_offset
;
366 if (ncopy
> nwant
) ncopy
= nwant
;
367 CopyMem(data
+ pos
, ioh
->data
+ byte_offset
, ncopy
);
369 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
370 D(bug("[fat] dump of last write, %ld bytes:\n", ncopy
));
371 HexDump(&(ioh
->data
[byte_offset
]), ncopy
);
374 Cache_MarkBlockDirty(ioh
->sb
->cache
, ioh
->block
);
379 D(bug("[fat] wrote %ld bytes, want %ld more\n", ncopy
, nwant
));