revert between 56095 -> 55830 in arch
[AROS.git] / rom / filesys / fat / file.c
blobc5d815f50fdc4bc4ec5742d4d08918ebdad5b3ec
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>
14 #include <dos/dos.h>
15 #include <dos/dosextens.h>
16 #include <dos/filehandler.h>
18 #include <proto/exec.h>
19 #include <proto/dos.h>
21 #include "fat_fs.h"
22 #include "fat_protos.h"
24 #define DEBUG DEBUG_FILE
25 #include "debug.h"
27 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
28 #include <ctype.h>
30 #define CHUNK 16
32 static void HexDump(unsigned char *buf, int bufsz, struct Globals *glob)
34 int i, j;
35 int count;
37 /* Do this in chunks of CHUNK bytes */
38 for (i = 0; i < bufsz; i += CHUNK)
40 /* Show the offset */
41 bug("0x%06x ", i);
43 /* Max of CHUNK or remaining bytes */
44 count = ((bufsz - i) > CHUNK ? CHUNK : bufsz - i);
46 /* Show the bytes */
47 for (j = 0; j < count; j++)
49 if (j == CHUNK / 2)
50 bug(" ");
51 bug("%02x ", buf[i + j]);
54 /* Pad with spaces if less than CHUNK */
55 for (j = count; j < CHUNK; j++)
57 if (j == CHUNK / 2)
58 bug(" ");
59 bug(" ");
62 /* Divider between hex and ASCII */
63 bug(" ");
65 for (j = 0; j < count; j++)
66 bug("%c", (isprint(buf[i + j]) ? buf[i + j] : '.'));
68 bug("\n");
71 #else
72 #define HexDump(b, c, g)
73 #endif
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;
80 APTR b;
81 ULONG pos, ncopy;
82 UBYTE *p;
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 */
96 pos = 0;
97 while (nwant > 0)
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)
109 ULONG i;
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));
122 /* Find it */
123 for (i = 0; i < cluster_offset - ioh->cluster_offset; i++)
125 /* Get the next one */
126 ioh->cur_cluster =
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"));
137 RESET_HANDLE(ioh);
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 */
160 ioh->sector_offset =
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)
179 RESET_HANDLE(ioh);
180 return ERROR_OBJECT_NOT_FOUND;
183 D(bug("[fat] adjusted for cluster 0,"
184 " chunk starts in sector %ld\n",
185 ioh->cur_sector));
187 else
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);
200 ioh->block = NULL;
203 D(bug("[fat] requesting sector %ld from cache\n",
204 ioh->cur_sector));
206 b = Cache_GetBlock(ioh->sb->cache,
207 ioh->sb->first_device_sector + ioh->cur_sector, &p);
208 if (b == NULL)
210 RESET_HANDLE(ioh);
212 D(bug("[fat] couldn't load sector, returning error %ld\n",
213 IoErr()));
215 return IoErr();
218 ioh->block = b;
219 ioh->data = p;
222 else
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;
227 if (ncopy > nwant)
228 ncopy = nwant;
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);
234 #endif
236 pos += ncopy;
237 nwant -= ncopy;
239 D(bug("[fat] copied %ld bytes, want %ld more\n", ncopy, nwant));
241 if (nwant > 0)
243 sector_offset++;
244 byte_offset = 0;
248 *nread = pos;
250 return 0;
253 LONG WriteFileChunk(struct IOHandle *ioh, ULONG file_pos, ULONG nwant,
254 UBYTE *data, ULONG *nwritten)
256 struct Globals *glob = ioh->sb->glob;
257 LONG err = 0;
258 ULONG sector_offset, byte_offset, cluster_offset, old_sector;
259 struct cache_block *b;
260 ULONG pos, ncopy;
261 UBYTE *p;
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 */
268 pos = 0;
269 while (nwant > 0)
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)
281 ULONG i;
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)
287 ULONG cluster;
289 D(bug("[fat] no first cluster, allocating one\n"));
291 /* Allocate a cluster */
292 if ((err = FindFreeCluster(ioh->sb, &cluster)) != 0)
294 RESET_HANDLE(ioh);
295 return err;
298 /* Mark the cluster used */
299 AllocCluster(ioh->sb, cluster);
301 /* Now setup the ioh */
302 ioh->first_cluster = cluster;
303 RESET_HANDLE(ioh);
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));
317 /* Find it */
318 for (i = 0; i < cluster_offset - ioh->cluster_offset; i++)
320 /* Get the next one */
321 ULONG next_cluster =
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
326 * new one */
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)
335 RESET_HANDLE(ioh);
336 return err;
339 /* Link the current cluster to the new one */
340 SET_NEXT_CLUSTER(ioh->sb, ioh->cur_cluster,
341 next_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));
350 else
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 */
371 ioh->sector_offset =
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));
389 else
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);
402 ioh->block = NULL;
405 D(bug("[fat] requesting sector %ld from cache\n",
406 ioh->cur_sector));
408 b = Cache_GetBlock(ioh->sb->cache, ioh->sb->first_device_sector
409 + ioh->cur_sector, &p);
410 if (b == NULL)
412 RESET_HANDLE(ioh);
414 D(bug("[fat] couldn't load sector, returning error %ld\n",
415 IoErr()));
417 return IoErr();
420 ioh->block = b;
421 ioh->data = p;
423 else
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;
428 if (ncopy > nwant)
429 ncopy = nwant;
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);
435 #endif
437 Cache_MarkBlockDirty(ioh->sb->cache, ioh->block);
439 pos += ncopy;
440 nwant -= ncopy;
442 D(bug("[fat] wrote %ld bytes, want %ld more\n", ncopy, nwant));
444 if (nwant > 0)
446 sector_offset++;
447 byte_offset = 0;
451 *nwritten = pos;
453 return 0;