add remtask before addtask
[AROS.git] / rom / filesys / fat / file.c
blob0b81724e7dcf790f8db56e576b48aa53551ad7d8
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) {
33 int i,j;
34 int count;
36 /* do this in chunks of CHUNK bytes */
37 for (i=0; i<bufsz; i+=CHUNK) {
38 /* show the offset */
39 bug("0x%06x ", i);
41 /* max of CHUNK or remaining bytes */
42 count = ((bufsz-i) > CHUNK ? CHUNK : bufsz-i);
44 /* show the bytes */
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(" ");
53 bug(" ");
56 /* divider between hex and ascii */
57 bug(" ");
59 for (j=0; j<count; j++)
60 bug("%c",(isprint(buf[i+j]) ? buf[i+j] : '.'));
62 bug("\n");
65 #else
66 #define HexDump(b,c)
67 #endif
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;
73 APTR b;
74 ULONG pos, ncopy;
75 UBYTE *p;
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 */
88 pos = 0;
89 while (nwant > 0) {
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) {
96 ULONG i;
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));
108 /* find it */
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"));
119 RESET_HANDLE(ioh);
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) {
155 RESET_HANDLE(ioh);
156 return ERROR_OBJECT_NOT_FOUND;
159 D(bug("[fat] adjusted for cluster 0, chunk starts in sector %ld\n", ioh->cur_sector));
161 else
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);
170 ioh->block = NULL;
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);
177 if (b == NULL) {
178 RESET_HANDLE(ioh);
180 D(bug("[fat] couldn't load sector, returning error %ld\n",
181 IoErr()));
183 return IoErr();
186 ioh->block = b;
187 ioh->data = p;
190 else
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);
201 #endif
203 pos += ncopy;
204 nwant -= ncopy;
206 D(bug("[fat] copied %ld bytes, want %ld more\n", ncopy, nwant));
208 if (nwant > 0) {
209 sector_offset++;
210 byte_offset = 0;
214 *nread = pos;
216 return 0;
219 LONG WriteFileChunk(struct IOHandle *ioh, ULONG file_pos, ULONG nwant,
220 UBYTE *data, ULONG *nwritten) {
221 struct Globals *glob = ioh->sb->glob;
222 LONG err = 0;
223 ULONG sector_offset, byte_offset, cluster_offset, old_sector;
224 struct cache_block *b;
225 ULONG pos, ncopy;
226 UBYTE *p;
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 */
233 pos = 0;
234 while (nwant > 0) {
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) {
241 ULONG i;
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) {
246 ULONG cluster;
248 D(bug("[fat] no first cluster, allocating one\n"));
250 /* allocate a cluster */
251 if ((err = FindFreeCluster(ioh->sb, &cluster)) != 0) {
252 RESET_HANDLE(ioh);
253 return err;
256 /* mark the cluster used */
257 AllocCluster(ioh->sb, cluster);
259 /* now setup the ioh */
260 ioh->first_cluster = cluster;
261 RESET_HANDLE(ioh);
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));
273 /* find it */
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
280 * new one */
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) {
286 RESET_HANDLE(ioh);
287 return err;
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));
301 else
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));
334 else
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);
343 ioh->block = NULL;
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);
350 if (b == NULL) {
351 RESET_HANDLE(ioh);
353 D(bug("[fat] couldn't load sector, returning error %ld\n", IoErr()));
355 return IoErr();
358 ioh->block = b;
359 ioh->data = p;
361 else
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);
372 #endif
374 Cache_MarkBlockDirty(ioh->sb->cache, ioh->block);
376 pos += ncopy;
377 nwant -= ncopy;
379 D(bug("[fat] wrote %ld bytes, want %ld more\n", ncopy, nwant));
381 if (nwant > 0) {
382 sector_offset++;
383 byte_offset = 0;
387 *nwritten = pos;
389 return 0;