Corrected module name.
[AROS.git] / rom / filesys / fat / file.c
blob4f3df73ff6dc4d483948dc3734c9c9233c14ee67
1 /*
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.
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 ULONG sector_offset, byte_offset, cluster_offset, old_sector;
72 APTR b;
73 ULONG pos, ncopy;
74 UBYTE *p;
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 */
87 pos = 0;
88 while (nwant > 0) {
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) {
95 ULONG i;
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));
107 /* find it */
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"));
118 RESET_HANDLE(ioh);
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));
153 else
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);
162 ioh->block = NULL;
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);
169 if (b == NULL) {
170 RESET_HANDLE(ioh);
172 D(bug("[fat] couldn't load sector, returning error %ld\n",
173 IoErr()));
175 return IoErr();
178 ioh->block = b;
179 ioh->data = p;
182 else
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);
193 #endif
195 pos += ncopy;
196 nwant -= ncopy;
198 D(bug("[fat] copied %ld bytes, want %ld more\n", ncopy, nwant));
200 if (nwant > 0) {
201 sector_offset++;
202 byte_offset = 0;
206 *nread = pos;
208 return 0;
211 LONG WriteFileChunk(struct IOHandle *ioh, ULONG file_pos, ULONG nwant,
212 UBYTE *data, ULONG *nwritten) {
213 LONG err = 0;
214 ULONG sector_offset, byte_offset, cluster_offset, old_sector;
215 struct cache_block *b;
216 ULONG pos, ncopy;
217 UBYTE *p;
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 */
224 pos = 0;
225 while (nwant > 0) {
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) {
232 ULONG i;
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) {
237 ULONG cluster;
239 D(bug("[fat] no first cluster, allocating one\n"));
241 /* allocate a cluster */
242 if ((err = FindFreeCluster(ioh->sb, &cluster)) != 0) {
243 RESET_HANDLE(ioh);
244 return err;
247 /* mark the cluster used */
248 AllocCluster(ioh->sb, cluster);
250 /* now setup the ioh */
251 ioh->first_cluster = cluster;
252 RESET_HANDLE(ioh);
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));
264 /* find it */
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
271 * new one */
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) {
277 RESET_HANDLE(ioh);
278 return err;
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));
292 else
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));
325 else
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);
334 ioh->block = NULL;
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);
341 if (b == NULL) {
342 RESET_HANDLE(ioh);
344 D(bug("[fat] couldn't load sector, returning error %ld\n", err));
346 return IoErr();
349 ioh->block = b;
350 ioh->data = p;
352 else
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);
363 #endif
365 Cache_MarkBlockDirty(ioh->sb->cache, ioh->block);
367 pos += ncopy;
368 nwant -= ncopy;
370 D(bug("[fat] wrote %ld bytes, want %ld more\n", ncopy, nwant));
372 if (nwant > 0) {
373 sector_offset++;
374 byte_offset = 0;
378 *nwritten = pos;
380 return 0;