2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2011 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 ULONG sector_offset
, byte_offset
, cluster_offset
, old_sector
;
76 /* files with no data can't be read from */
77 if (ioh
->first_cluster
== 0xffffffff && nwant
> 0) {
78 D(bug("[fat] file has no first cluster, so nothing to read!\n"));
79 return ERROR_OBJECT_NOT_FOUND
;
82 /* figure out how far into the file to look for the requested data */
83 sector_offset
= file_pos
>> ioh
->sb
->sectorsize_bits
;
84 byte_offset
= file_pos
& (ioh
->sb
->sectorsize
-1);
86 /* loop until we get all we want */
90 D(bug("[fat] trying to read %ld bytes (%ld sectors + %ld bytes into the file)\n", nwant
, sector_offset
, byte_offset
));
92 /* move clusters if necessary */
93 cluster_offset
= sector_offset
>> ioh
->sb
->cluster_sectors_bits
;
94 if (ioh
->cluster_offset
!= cluster_offset
&& ioh
->first_cluster
!= 0) {
97 /* if we're already ahead of the wanted cluster, then we need to
98 * go back to the start of the cluster list */
99 if (ioh
->cluster_offset
> cluster_offset
) {
100 ioh
->cur_cluster
= ioh
->first_cluster
;
101 ioh
->cluster_offset
= 0;
104 D(bug("[fat] moving forward %ld clusters from cluster %ld\n",
105 cluster_offset
- ioh
->cluster_offset
, ioh
->cur_cluster
));
108 for (i
= 0; i
< cluster_offset
- ioh
->cluster_offset
; i
++) {
109 /* get the next one */
110 ioh
->cur_cluster
= GET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
);
112 /* if it was free (shouldn't happen) or we hit the end of the
113 * chain, the requested data isn't here */
114 if (ioh
->cur_cluster
== 0
115 || ioh
->cur_cluster
>= ioh
->sb
->eoc_mark
- 7) {
116 D(bug("[fat] hit empty or eoc cluster, no more file left\n"));
120 return ERROR_OBJECT_NOT_FOUND
;
124 /* remember how far in we are now */
125 ioh
->cluster_offset
= cluster_offset
;
127 D(bug("[fat] moved to cluster %ld\n", ioh
->cur_cluster
));
129 /* reset the sector offset so the sector recalc gets triggered */
130 ioh
->sector_offset
= 0xffffffff;
133 /* recalculate the sector location if we moved */
134 old_sector
= ioh
->cur_sector
;
135 if (ioh
->sector_offset
!= (sector_offset
& (ioh
->sb
->cluster_sectors
-1))
136 || ioh
->first_cluster
== 0) {
138 /* work out how many sectors in we should be looking */
139 ioh
->sector_offset
= sector_offset
& (ioh
->sb
->cluster_sectors
-1);
141 /* simple math to find the absolute sector number */
142 ioh
->cur_sector
= SECTOR_FROM_CLUSTER(ioh
->sb
, ioh
->cur_cluster
) + ioh
->sector_offset
;
144 /* if the first cluster is zero, we use sector addressing instead
145 * of clusters. this is a hack to support fat12/16 root dirs, which
146 * live before the data region */
147 if (ioh
->first_cluster
== 0) {
148 ioh
->sector_offset
= sector_offset
- ioh
->first_sector
;
149 ioh
->cur_sector
= ioh
->first_sector
+ sector_offset
;
151 D(bug("[fat] adjusted for cluster 0, chunk starts in sector %ld\n", ioh
->cur_sector
));
154 D(bug("[fat] chunk starts %ld sectors into the cluster, which is sector %ld\n", ioh
->sector_offset
, ioh
->cur_sector
));
157 /* if we don't have the wanted block kicking around, we need to bring it
158 * in from the cache */
159 if (ioh
->block
== NULL
|| ioh
->cur_sector
!= old_sector
) {
160 if (ioh
->block
!= NULL
) {
161 Cache_FreeBlock(ioh
->sb
->cache
, ioh
->block
);
165 D(bug("[fat] requesting sector %ld from cache\n", ioh
->cur_sector
));
167 b
= Cache_GetBlock(ioh
->sb
->cache
,
168 ioh
->sb
->first_device_sector
+ ioh
->cur_sector
, &p
);
172 D(bug("[fat] couldn't load sector, returning error %ld\n",
183 D(bug("[fat] using cached sector %ld\n", ioh
->cur_sector
));
185 /* now copy in the data */
186 ncopy
= ioh
->sb
->sectorsize
- byte_offset
;
187 if (ncopy
> nwant
) ncopy
= nwant
;
188 CopyMem(ioh
->data
+ byte_offset
, data
+ pos
, ncopy
);
190 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
191 D(bug("[fat] dump of last read, %ld bytes:\n", ncopy
));
192 HexDump(&(data
[pos
]), ncopy
);
198 D(bug("[fat] copied %ld bytes, want %ld more\n", ncopy
, nwant
));
211 LONG
WriteFileChunk(struct IOHandle
*ioh
, ULONG file_pos
, ULONG nwant
,
212 UBYTE
*data
, ULONG
*nwritten
) {
214 ULONG sector_offset
, byte_offset
, cluster_offset
, old_sector
;
215 struct cache_block
*b
;
219 /* figure out how far into the file to start */
220 sector_offset
= file_pos
>> ioh
->sb
->sectorsize_bits
;
221 byte_offset
= file_pos
& (ioh
->sb
->sectorsize
-1);
223 /* loop until we've finished writing */
227 D(bug("[fat] trying to write %ld bytes (%ld sectors + %ld bytes into the file)\n", nwant
, sector_offset
, byte_offset
));
229 /* move clusters if necessary */
230 cluster_offset
= sector_offset
>> ioh
->sb
->cluster_sectors_bits
;
231 if (ioh
->cluster_offset
!= cluster_offset
&& ioh
->first_cluster
!= 0) {
234 /* if we have no first cluster, this is a new file. we allocate
235 * the first cluster and then update the ioh */
236 if (ioh
->first_cluster
== 0xffffffff) {
239 D(bug("[fat] no first cluster, allocating one\n"));
241 /* allocate a cluster */
242 if ((err
= FindFreeCluster(ioh
->sb
, &cluster
)) != 0) {
247 /* mark the cluster used */
248 AllocCluster(ioh
->sb
, cluster
);
250 /* now setup the ioh */
251 ioh
->first_cluster
= cluster
;
255 /* if we're already ahead of the wanted cluster, then we need to
256 * go back to the start of the cluster list */
257 if (ioh
->cluster_offset
> cluster_offset
) {
258 ioh
->cur_cluster
= ioh
->first_cluster
;
259 ioh
->cluster_offset
= 0;
262 D(bug("[fat] moving forward %ld clusters from cluster %ld\n", cluster_offset
- ioh
->cluster_offset
, ioh
->cur_cluster
));
265 for (i
= 0; i
< cluster_offset
- ioh
->cluster_offset
; i
++) {
266 /* get the next one */
267 ULONG next_cluster
= GET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
);
269 /* if it was free (shouldn't happen) or we hit the end of the
270 * chain, there is no next cluster, so we have to allocate a
272 if (next_cluster
== 0
273 || next_cluster
>= ioh
->sb
->eoc_mark
- 7) {
274 D(bug("[fat] hit empty or eoc cluster, allocating another\n"));
276 if ((err
= FindFreeCluster(ioh
->sb
, &next_cluster
)) != 0) {
281 /* link the current cluster to the new one */
282 SET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
, next_cluster
);
284 /* and mark the new one used */
285 AllocCluster(ioh
->sb
, next_cluster
);
287 ioh
->cur_cluster
= next_cluster
;
289 D(bug("[fat] allocated cluster %d\n", next_cluster
));
293 ioh
->cur_cluster
= next_cluster
;
296 /* remember how far in we are now */
297 ioh
->cluster_offset
= cluster_offset
;
299 D(bug("[fat] moved to cluster %ld\n", ioh
->cur_cluster
));
301 /* reset the sector offset so the sector recalc gets triggered */
302 ioh
->sector_offset
= 0xffffffff;
305 /* recalculate the sector location if we moved */
306 old_sector
= ioh
->cur_sector
;
307 if (ioh
->sector_offset
!= (sector_offset
& (ioh
->sb
->cluster_sectors
-1))
308 || ioh
->first_cluster
== 0) {
310 /* work out how many sectors in we should be looking */
311 ioh
->sector_offset
= sector_offset
& (ioh
->sb
->cluster_sectors
-1);
313 /* simple math to find the absolute sector number */
314 ioh
->cur_sector
= SECTOR_FROM_CLUSTER(ioh
->sb
, ioh
->cur_cluster
) + ioh
->sector_offset
;
316 /* if the first cluster is zero, we use sector addressing instead
317 * of clusters. this is a hack to support fat12/16 root dirs, which
318 * live before the data region */
319 if (ioh
->first_cluster
== 0) {
320 ioh
->sector_offset
= sector_offset
- ioh
->first_sector
;
321 ioh
->cur_sector
= ioh
->first_sector
+ sector_offset
;
323 D(bug("[fat] adjusted for cluster 0, chunk starts in sector %ld\n", ioh
->cur_sector
));
326 D(bug("[fat] chunk starts %ld sectors into the cluster, which is sector %ld\n", ioh
->sector_offset
, ioh
->cur_sector
));
329 /* if we don't have the wanted block kicking around, we need to bring it
330 * in from the cache */
331 if (ioh
->block
== NULL
|| ioh
->cur_sector
!= old_sector
) {
332 if (ioh
->block
!= NULL
) {
333 Cache_FreeBlock(ioh
->sb
->cache
, ioh
->block
);
337 D(bug("[fat] requesting sector %ld from cache\n", ioh
->cur_sector
));
339 b
= Cache_GetBlock(ioh
->sb
->cache
, ioh
->sb
->first_device_sector
340 + ioh
->cur_sector
, &p
);
344 D(bug("[fat] couldn't load sector, returning error %ld\n", err
));
353 D(bug("[fat] using cached sector %ld\n", ioh
->cur_sector
));
355 /* copy our data into the block */
356 ncopy
= ioh
->sb
->sectorsize
- byte_offset
;
357 if (ncopy
> nwant
) ncopy
= nwant
;
358 CopyMem(data
+ pos
, ioh
->data
+ byte_offset
, ncopy
);
360 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
361 D(bug("[fat] dump of last write, %ld bytes:\n", ncopy
));
362 HexDump(&(ioh
->data
[byte_offset
]), ncopy
);
365 Cache_MarkBlockDirty(ioh
->sb
->cache
, ioh
->block
);
370 D(bug("[fat] wrote %ld bytes, want %ld more\n", ncopy
, nwant
));