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
, struct Globals
*glob
)
37 /* Do this in chunks of CHUNK bytes */
38 for (i
= 0; i
< bufsz
; i
+= CHUNK
)
43 /* Max of CHUNK or remaining bytes */
44 count
= ((bufsz
- i
) > CHUNK
? CHUNK
: bufsz
- i
);
47 for (j
= 0; j
< count
; j
++)
51 bug("%02x ", buf
[i
+ j
]);
54 /* Pad with spaces if less than CHUNK */
55 for (j
= count
; j
< CHUNK
; j
++)
62 /* Divider between hex and ASCII */
65 for (j
= 0; j
< count
; j
++)
66 bug("%c", (isprint(buf
[i
+ j
]) ? buf
[i
+ j
] : '.'));
72 #define HexDump(b, c, g)
75 LONG
ReadFileChunk(struct IOHandle
*ioh
, ULONG file_pos
, ULONG nwant
,
76 UBYTE
*data
, ULONG
*nread
)
78 struct Globals
*glob
= ioh
->sb
->glob
;
79 ULONG sector_offset
, byte_offset
, cluster_offset
, old_sector
;
84 /* Files with no data can't be read from */
85 if (ioh
->first_cluster
== 0xffffffff && nwant
> 0)
87 D(bug("[fat] file has no first cluster, so nothing to read!\n"));
88 return ERROR_OBJECT_NOT_FOUND
;
91 /* Figure out how far into the file to look for the requested data */
92 sector_offset
= file_pos
>> ioh
->sb
->sectorsize_bits
;
93 byte_offset
= file_pos
& (ioh
->sb
->sectorsize
- 1);
95 /* Loop until we get all we want */
100 D(bug("[fat] trying to read %ld bytes"
101 " (%ld sectors + %ld bytes into the file)\n",
102 nwant
, sector_offset
, byte_offset
));
104 /* Move clusters if necessary */
105 cluster_offset
= sector_offset
>> ioh
->sb
->cluster_sectors_bits
;
106 if (ioh
->cluster_offset
!= cluster_offset
107 && ioh
->first_cluster
!= 0)
111 /* If we're already ahead of the wanted cluster, then we need to
112 * go back to the start of the cluster list */
113 if (ioh
->cluster_offset
> cluster_offset
)
115 ioh
->cur_cluster
= ioh
->first_cluster
;
116 ioh
->cluster_offset
= 0;
119 D(bug("[fat] moving forward %ld clusters from cluster %ld\n",
120 cluster_offset
- ioh
->cluster_offset
, ioh
->cur_cluster
));
123 for (i
= 0; i
< cluster_offset
- ioh
->cluster_offset
; i
++)
125 /* Get the next one */
127 GET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
);
129 /* If it was free (shouldn't happen) or we hit the end of the
130 * chain, the requested data isn't here */
131 if (ioh
->cur_cluster
== 0
132 || ioh
->cur_cluster
>= ioh
->sb
->eoc_mark
- 7)
134 D(bug("[fat] hit empty or eoc cluster,"
135 " no more file left\n"));
139 return ERROR_OBJECT_NOT_FOUND
;
143 /* Remember how far in we are now */
144 ioh
->cluster_offset
= cluster_offset
;
146 D(bug("[fat] moved to cluster %ld\n", ioh
->cur_cluster
));
148 /* Reset the sector offset so the sector recalc gets triggered */
149 ioh
->sector_offset
= 0xffffffff;
152 /* Recalculate the sector location if we moved */
153 old_sector
= ioh
->cur_sector
;
154 if (ioh
->sector_offset
!=
155 (sector_offset
& (ioh
->sb
->cluster_sectors
- 1))
156 || ioh
->first_cluster
== 0)
159 /* Work out how many sectors in we should be looking */
161 sector_offset
& (ioh
->sb
->cluster_sectors
- 1);
163 /* Simple math to find the absolute sector number */
164 ioh
->cur_sector
= SECTOR_FROM_CLUSTER(ioh
->sb
, ioh
->cur_cluster
)
165 + ioh
->sector_offset
;
167 /* If the first cluster is zero, we use sector addressing instead
168 * of clusters. this is a hack to support FAT12/16 root dirs,
169 * which live before the data region */
170 if (ioh
->first_cluster
== 0)
172 ioh
->sector_offset
= sector_offset
- ioh
->first_sector
;
173 ioh
->cur_sector
= ioh
->first_sector
+ sector_offset
;
175 /* Stop if we've reached the end of the root dir */
176 if (ioh
->cur_sector
>= ioh
->sb
->first_rootdir_sector
177 + ioh
->sb
->rootdir_sectors
)
180 return ERROR_OBJECT_NOT_FOUND
;
183 D(bug("[fat] adjusted for cluster 0,"
184 " chunk starts in sector %ld\n",
188 D(bug("[fat] chunk starts %ld sectors into the cluster,"
189 " which is sector %ld\n",
190 ioh
->sector_offset
, ioh
->cur_sector
));
193 /* If we don't have the wanted block kicking around, we need to bring
194 * it in from the cache */
195 if (ioh
->block
== NULL
|| ioh
->cur_sector
!= old_sector
)
197 if (ioh
->block
!= NULL
)
199 Cache_FreeBlock(ioh
->sb
->cache
, ioh
->block
);
203 D(bug("[fat] requesting sector %ld from cache\n",
206 b
= Cache_GetBlock(ioh
->sb
->cache
,
207 ioh
->sb
->first_device_sector
+ ioh
->cur_sector
, &p
);
212 D(bug("[fat] couldn't load sector, returning error %ld\n",
223 D(bug("[fat] using cached sector %ld\n", ioh
->cur_sector
));
225 /* Now copy in the data */
226 ncopy
= ioh
->sb
->sectorsize
- byte_offset
;
229 CopyMem(ioh
->data
+ byte_offset
, data
+ pos
, ncopy
);
231 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
232 D(bug("[fat] dump of last read, %ld bytes:\n", ncopy
));
233 HexDump(&(data
[pos
]), ncopy
, glob
);
239 D(bug("[fat] copied %ld bytes, want %ld more\n", ncopy
, nwant
));
253 LONG
WriteFileChunk(struct IOHandle
*ioh
, ULONG file_pos
, ULONG nwant
,
254 UBYTE
*data
, ULONG
*nwritten
)
256 struct Globals
*glob
= ioh
->sb
->glob
;
258 ULONG sector_offset
, byte_offset
, cluster_offset
, old_sector
;
259 struct cache_block
*b
;
263 /* Figure out how far into the file to start */
264 sector_offset
= file_pos
>> ioh
->sb
->sectorsize_bits
;
265 byte_offset
= file_pos
& (ioh
->sb
->sectorsize
- 1);
267 /* Loop until we've finished writing */
272 D(bug("[fat] trying to write %ld bytes"
273 " (%ld sectors + %ld bytes into the file)\n",
274 nwant
, sector_offset
, byte_offset
));
276 /* Move clusters if necessary */
277 cluster_offset
= sector_offset
>> ioh
->sb
->cluster_sectors_bits
;
278 if (ioh
->cluster_offset
!= cluster_offset
279 && ioh
->first_cluster
!= 0)
283 /* If we have no first cluster, this is a new file. We allocate
284 * the first cluster and then update the ioh */
285 if (ioh
->first_cluster
== 0xffffffff)
289 D(bug("[fat] no first cluster, allocating one\n"));
291 /* Allocate a cluster */
292 if ((err
= FindFreeCluster(ioh
->sb
, &cluster
)) != 0)
298 /* Mark the cluster used */
299 AllocCluster(ioh
->sb
, cluster
);
301 /* Now setup the ioh */
302 ioh
->first_cluster
= cluster
;
306 /* If we're already ahead of the wanted cluster, then we need to
307 * go back to the start of the cluster list */
308 if (ioh
->cluster_offset
> cluster_offset
)
310 ioh
->cur_cluster
= ioh
->first_cluster
;
311 ioh
->cluster_offset
= 0;
314 D(bug("[fat] moving forward %ld clusters from cluster %ld\n",
315 cluster_offset
- ioh
->cluster_offset
, ioh
->cur_cluster
));
318 for (i
= 0; i
< cluster_offset
- ioh
->cluster_offset
; i
++)
320 /* Get the next one */
322 GET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
);
324 /* If it was free (shouldn't happen) or we hit the end of the
325 * chain, there is no next cluster, so we have to allocate a
327 if (next_cluster
== 0
328 || next_cluster
>= ioh
->sb
->eoc_mark
- 7)
330 D(bug("[fat] hit empty or eoc cluster,"
331 " allocating another\n"));
333 if ((err
= FindFreeCluster(ioh
->sb
, &next_cluster
)) != 0)
339 /* Link the current cluster to the new one */
340 SET_NEXT_CLUSTER(ioh
->sb
, ioh
->cur_cluster
,
343 /* And mark the new one used */
344 AllocCluster(ioh
->sb
, next_cluster
);
346 ioh
->cur_cluster
= next_cluster
;
348 D(bug("[fat] allocated cluster %d\n", next_cluster
));
351 ioh
->cur_cluster
= next_cluster
;
354 /* Remember how far in we are now */
355 ioh
->cluster_offset
= cluster_offset
;
357 D(bug("[fat] moved to cluster %ld\n", ioh
->cur_cluster
));
359 /* Reset the sector offset so the sector recalc gets triggered */
360 ioh
->sector_offset
= 0xffffffff;
363 /* Recalculate the sector location if we moved */
364 old_sector
= ioh
->cur_sector
;
365 if (ioh
->sector_offset
!=
366 (sector_offset
& (ioh
->sb
->cluster_sectors
- 1))
367 || ioh
->first_cluster
== 0)
370 /* Work out how many sectors in we should be looking */
372 sector_offset
& (ioh
->sb
->cluster_sectors
- 1);
374 /* Simple math to find the absolute sector number */
375 ioh
->cur_sector
= SECTOR_FROM_CLUSTER(ioh
->sb
, ioh
->cur_cluster
)
376 + ioh
->sector_offset
;
378 /* If the first cluster is zero, we use sector addressing instead
379 * of clusters. this is a hack to support FAT12/16 root dirs,
380 * which live before the data region */
381 if (ioh
->first_cluster
== 0)
383 ioh
->sector_offset
= sector_offset
- ioh
->first_sector
;
384 ioh
->cur_sector
= ioh
->first_sector
+ sector_offset
;
386 D(bug("[fat] adjusted for cluster 0,"
387 " chunk starts in sector %ld\n", ioh
->cur_sector
));
390 D(bug("[fat] chunk starts %ld sectors into the cluster,"
391 " which is sector %ld\n",
392 ioh
->sector_offset
, ioh
->cur_sector
));
395 /* If we don't have the wanted block kicking around, we need to bring
396 * it in from the cache */
397 if (ioh
->block
== NULL
|| ioh
->cur_sector
!= old_sector
)
399 if (ioh
->block
!= NULL
)
401 Cache_FreeBlock(ioh
->sb
->cache
, ioh
->block
);
405 D(bug("[fat] requesting sector %ld from cache\n",
408 b
= Cache_GetBlock(ioh
->sb
->cache
, ioh
->sb
->first_device_sector
409 + ioh
->cur_sector
, &p
);
414 D(bug("[fat] couldn't load sector, returning error %ld\n",
424 D(bug("[fat] using cached sector %ld\n", ioh
->cur_sector
));
426 /* Copy our data into the block */
427 ncopy
= ioh
->sb
->sectorsize
- byte_offset
;
430 CopyMem(data
+ pos
, ioh
->data
+ byte_offset
, ncopy
);
432 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
433 D(bug("[fat] dump of last write, %ld bytes:\n", ncopy
));
434 HexDump(&(ioh
->data
[byte_offset
]), ncopy
, glob
);
437 Cache_MarkBlockDirty(ioh
->sb
->cache
, ioh
->block
);
442 D(bug("[fat] wrote %ld bytes, want %ld more\n", ncopy
, nwant
));