Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / fs / fat / file.c
blob4385fbb859ef37776fb9a273dd87ccd22b520cfb
1 /*
2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2008 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 fat_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 fat_hexdump(b,c)
67 #endif
69 LONG ReadFileChunk(struct IOHandle *ioh, ULONG file_pos, ULONG nwant, UBYTE *data, ULONG *nread) {
70 LONG err = 0;
71 ULONG sector_offset, byte_offset, cluster_offset;
72 struct cache_block *b;
73 ULONG pos, ncopy;
75 /* files with no data can't be read from */
76 if (ioh->first_cluster == 0xffffffff) {
77 D(bug("[fat] file has no first cluster, so nothing to read!\n"));
78 return ERROR_OBJECT_NOT_FOUND;
81 /* figure out how far into the file to look for the requested data */
82 sector_offset = file_pos >> ioh->sb->sectorsize_bits;
83 byte_offset = file_pos & (ioh->sb->sectorsize-1);
85 /* loop until we get all we want */
86 pos = 0;
87 while (nwant > 0) {
89 D(bug("[fat] trying to read %ld bytes (%ld sectors + %ld bytes into the file)\n", nwant, sector_offset, byte_offset));
91 /* move clusters if necessary */
92 cluster_offset = sector_offset >> ioh->sb->cluster_sectors_bits;
93 if (ioh->cluster_offset != cluster_offset && ioh->first_cluster != 0) {
94 ULONG i;
96 /* if we're already ahead of the wanted cluster, then we need to
97 * go back to the start of the cluster list */
98 if (ioh->cluster_offset > cluster_offset) {
99 ioh->cur_cluster = ioh->first_cluster;
100 ioh->cluster_offset = 0;
103 D(bug("[fat] moving forward %ld clusters from cluster %ld\n", cluster_offset - ioh->cluster_offset, ioh->cur_cluster));
105 /* find it */
106 for (i = 0; i < cluster_offset - ioh->cluster_offset; i++) {
107 /* get the next one */
108 ioh->cur_cluster = GET_NEXT_CLUSTER(ioh->sb, ioh->cur_cluster);
110 /* if it was free (shouldn't happen) or we hit the end of the
111 * chain, the requested data isn't here */
112 if (ioh->cur_cluster == 0 || ioh->cur_cluster >= ioh->sb->eoc_mark) {
113 D(bug("[fat] hit empty or eoc cluster, no more file left\n"));
115 RESET_HANDLE(ioh);
117 return ERROR_OBJECT_NOT_FOUND;
121 /* remember how far in we are now */
122 ioh->cluster_offset = cluster_offset;
124 D(bug("[fat] moved to cluster %ld\n", ioh->cur_cluster));
126 /* reset the sector offset so the sector recalc gets triggered */
127 ioh->sector_offset = 0xffffffff;
130 /* recalculate the sector location if we moved */
131 if (ioh->sector_offset != (sector_offset & (ioh->sb->cluster_sectors-1))
132 || ioh->first_cluster == 0) {
134 /* work out how many sectors in we should be looking */
135 ioh->sector_offset = sector_offset & (ioh->sb->cluster_sectors-1);
137 /* simple math to find the absolute sector number */
138 ioh->cur_sector = SECTOR_FROM_CLUSTER(ioh->sb, ioh->cur_cluster) + ioh->sector_offset;
140 /* if the first cluster is zero, we use sector addressing instead
141 * of clusters. this is a hack to support fat12/16 root dirs, which
142 * live before the data region */
143 if (ioh->first_cluster == 0) {
144 ioh->sector_offset = sector_offset - ioh->first_sector;
145 ioh->cur_sector = ioh->first_sector + sector_offset;
147 D(bug("[fat] adjusted for cluster 0, chunk starts in sector %ld\n", ioh->cur_sector));
150 else
151 D(bug("[fat] chunk starts %ld sectors into the cluster, which is sector %ld\n", ioh->sector_offset, ioh->cur_sector));
154 /* if we don't have the wanted block kicking around, we need to bring it
155 * in from the cache */
156 if (ioh->block == NULL || ioh->cur_sector != ioh->block->num) {
157 if (ioh->block != NULL) {
158 cache_put_block(ioh->sb->cache, ioh->block, 0);
159 ioh->block = NULL;
162 D(bug("[fat] requesting sector %ld from cache\n", ioh->cur_sector));
164 err = cache_get_block(ioh->sb->cache, ioh->sb->first_device_sector + ioh->cur_sector, 0, &b);
165 if (err > 0) {
166 RESET_HANDLE(ioh);
168 D(bug("[fat] couldn't load sector, returning error %ld\n", err));
170 return err;
173 ioh->block = b;
176 else
177 D(bug("[fat] using cached sector %ld\n", ioh->cur_sector));
179 /* now copy in the data */
180 ncopy = ioh->sb->sectorsize - byte_offset;
181 if (ncopy > nwant) ncopy = nwant;
182 CopyMem(&(ioh->block->data[byte_offset]), &(data[pos]), ncopy);
184 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
185 D(bug("[fat] dump of last read, %ld bytes:\n", ncopy));
186 fat_hexdump(&(data[pos]), ncopy);
187 #endif
189 pos += ncopy;
190 nwant -= ncopy;
192 D(bug("[fat] copied %ld bytes, want %ld more\n", ncopy, nwant));
194 if (nwant > 0) {
195 sector_offset++;
196 byte_offset = 0;
200 *nread = pos;
202 return 0;
205 LONG WriteFileChunk(struct IOHandle *ioh, ULONG file_pos, ULONG nwant, UBYTE *data, ULONG *nwritten) {
206 LONG err = 0;
207 ULONG sector_offset, byte_offset, cluster_offset;
208 struct cache_block *b;
209 ULONG pos, ncopy;
211 /* figure out how far into the file to start */
212 sector_offset = file_pos >> ioh->sb->sectorsize_bits;
213 byte_offset = file_pos & (ioh->sb->sectorsize-1);
215 /* loop until we've finished writing */
216 pos = 0;
217 while (nwant > 0) {
219 D(bug("[fat] trying to write %ld bytes (%ld sectors + %ld bytes into the file)\n", nwant, sector_offset, byte_offset));
221 /* move clusters if necessary */
222 cluster_offset = sector_offset >> ioh->sb->cluster_sectors_bits;
223 if (ioh->cluster_offset != cluster_offset && ioh->first_cluster != 0) {
224 ULONG i;
226 /* if we have no first cluster, this is a new file. we allocate
227 * the first cluster and then update the ioh */
228 if (ioh->first_cluster == 0xffffffff) {
229 ULONG cluster;
231 D(bug("[fat] no first cluster, allocating one\n"));
233 /* allocate a cluster */
234 if ((err = FindFreeCluster(ioh->sb, &cluster)) != 0) {
235 RESET_HANDLE(ioh);
236 return err;
239 /* mark the cluster used */
240 SET_NEXT_CLUSTER(ioh->sb, cluster, ioh->sb->eoc_mark);
242 /* now setup the ioh */
243 ioh->first_cluster = cluster;
244 RESET_HANDLE(ioh);
247 /* if we're already ahead of the wanted cluster, then we need to
248 * go back to the start of the cluster list */
249 if (ioh->cluster_offset > cluster_offset) {
250 ioh->cur_cluster = ioh->first_cluster;
251 ioh->cluster_offset = 0;
254 D(bug("[fat] moving forward %ld clusters from cluster %ld\n", cluster_offset - ioh->cluster_offset, ioh->cur_cluster));
256 /* find it */
257 for (i = 0; i < cluster_offset - ioh->cluster_offset; i++) {
258 /* get the next one */
259 ULONG next_cluster = GET_NEXT_CLUSTER(ioh->sb, ioh->cur_cluster);
261 /* if it was free (shouldn't happen) or we hit the end of the
262 * chain, there is no next cluster, so we have to allocate a
263 * new one */
264 if (next_cluster == 0 || next_cluster >= ioh->sb->eoc_mark) {
265 D(bug("[fat] hit empty or eoc cluster, allocating another\n"));
267 if ((err = FindFreeCluster(ioh->sb, &next_cluster)) != 0) {
268 RESET_HANDLE(ioh);
269 return err;
272 /* link the current cluster to the new one */
273 SET_NEXT_CLUSTER(ioh->sb, ioh->cur_cluster, next_cluster);
275 /* and mark the new one used */
276 SET_NEXT_CLUSTER(ioh->sb, next_cluster, ioh->sb->eoc_mark);
278 ioh->cur_cluster = next_cluster;
280 D(bug("[fat] allocated cluster %d\n", next_cluster));
283 else
284 ioh->cur_cluster = next_cluster;
287 /* remember how far in we are now */
288 ioh->cluster_offset = cluster_offset;
290 D(bug("[fat] moved to cluster %ld\n", ioh->cur_cluster));
292 /* reset the sector offset so the sector recalc gets triggered */
293 ioh->sector_offset = 0xffffffff;
296 /* recalculate the sector location if we moved */
297 if (ioh->sector_offset != (sector_offset & (ioh->sb->cluster_sectors-1))
298 || ioh->first_cluster == 0) {
300 /* work out how many sectors in we should be looking */
301 ioh->sector_offset = sector_offset & (ioh->sb->cluster_sectors-1);
303 /* simple math to find the absolute sector number */
304 ioh->cur_sector = SECTOR_FROM_CLUSTER(ioh->sb, ioh->cur_cluster) + ioh->sector_offset;
306 /* if the first cluster is zero, we use sector addressing instead
307 * of clusters. this is a hack to support fat12/16 root dirs, which
308 * live before the data region */
309 if (ioh->first_cluster == 0) {
310 ioh->sector_offset = sector_offset - ioh->first_sector;
311 ioh->cur_sector = ioh->first_sector + sector_offset;
313 D(bug("[fat] adjusted for cluster 0, chunk starts in sector %ld\n", ioh->cur_sector));
316 else
317 D(bug("[fat] chunk starts %ld sectors into the cluster, which is sector %ld\n", ioh->sector_offset, ioh->cur_sector));
320 /* if we don't have the wanted block kicking around, we need to bring it
321 * in from the cache */
322 if (ioh->block == NULL || ioh->cur_sector != ioh->block->num) {
323 if (ioh->block != NULL) {
324 cache_put_block(ioh->sb->cache, ioh->block, 0);
325 ioh->block = NULL;
328 D(bug("[fat] requesting sector %ld from cache\n", ioh->cur_sector));
330 err = cache_get_block(ioh->sb->cache, ioh->sb->first_device_sector + ioh->cur_sector, 0, &b);
331 if (err > 0) {
332 RESET_HANDLE(ioh);
334 D(bug("[fat] couldn't load sector, returning error %ld\n", err));
336 return err;
339 ioh->block = b;
342 else
343 D(bug("[fat] using cached sector %ld\n", ioh->cur_sector));
345 /* copy our data into the block */
346 ncopy = ioh->sb->sectorsize - byte_offset;
347 if (ncopy > nwant) ncopy = nwant;
348 CopyMem(&(data[pos]), &(ioh->block->data[byte_offset]), ncopy);
350 #if defined(DEBUG_DUMP) && DEBUG_DUMP != 0
351 D(bug("[fat] dump of last write, %ld bytes:\n", ncopy));
352 fat_hexdump(&(ioh->block->data[byte_offset]), ncopy);
353 #endif
355 cache_mark_block_dirty(ioh->sb->cache, ioh->block);
357 pos += ncopy;
358 nwant -= ncopy;
360 D(bug("[fat] wrote %ld bytes, want %ld more\n", ncopy, nwant));
362 if (nwant > 0) {
363 sector_offset++;
364 byte_offset = 0;
368 *nwritten = pos;
370 return 0;