freemyipod r46 - Code Review

Jump to: navigation, search
Repository:freemyipod
Revision:r45‎ | r46 | r47 >
Date:02:23, 7 August 2010
Author:theseven
Status:new
Tags:
Comment:
Import file system code from Rockbox. It still needs to be adapted and cleaned up.
Modified paths:
  • /embios/trunk/dir.c (added) (history)
  • /embios/trunk/dir.h (added) (history)
  • /embios/trunk/disk.c (added) (history)
  • /embios/trunk/fat.c (added) (history)
  • /embios/trunk/fat.h (added) (history)
  • /embios/trunk/fat32.c (deleted) (history)
  • /embios/trunk/fat32.h (deleted) (history)
  • /embios/trunk/file.c (added) (history)
  • /embios/trunk/file.h (added) (history)
  • /embios/trunk/init.c (modified) (history)

Diff [purge]

Index: embios/trunk/fat32.c
@@ -1,349 +0,0 @@
2 -//
3 -//
4 -// Copyright 2010 TheSeven
5 -//
6 -//
7 -// This file is part of emBIOS.
8 -//
9 -// emBIOS is free software: you can redistribute it and/or
10 -// modify it under the terms of the GNU General Public License as
11 -// published by the Free Software Foundation, either version 2 of the
12 -// License, or (at your option) any later version.
13 -//
14 -// emBIOS is distributed in the hope that it will be useful,
15 -// but WITHOUT ANY WARRANTY; without even the implied warranty of
16 -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 -// See the GNU General Public License for more details.
18 -//
19 -// You should have received a copy of the GNU General Public License along
20 -// with emBIOS. If not, see <http://www.gnu.org/licenses/>.
21 -//
22 -//
23 -
24 -
25 -#include "global.h"
26 -#include "storage.h"
27 -#include "fat32.h"
28 -#include "util.h"
29 -
30 -
31 -uint32_t fat32_ok;
32 -uint32_t fat32_startsector;
33 -uint32_t fat32_secperclus;
34 -uint32_t fat32_database;
35 -uint32_t fat32_fatbase;
36 -uint32_t fat32_fatsize;
37 -uint32_t fat32_fatcount;
38 -uint32_t fat32_sectorcount;
39 -uint32_t fat32_clustercount;
40 -uint32_t fat32_rootdirclus;
41 -uint32_t fat32_buf1[0x200] __attribute__((aligned(16)));
42 -uint32_t fat32_buf2[0x200] __attribute__((aligned(16)));
43 -
44 -
45 -uint32_t fat32_get_root()
46 -{
47 - return fat32_rootdirclus;
48 -}
49 -
50 -uint32_t fat32_get_clusterchain(uint32_t clusterchain, uint32_t maxsize, void* buffer)
51 -{
52 - uint32_t i;
53 - for (i = 0; i < (maxsize >> 11); )
54 - {
55 - uint32_t sector = (clusterchain - 2) * fat32_secperclus + fat32_database;
56 - uint32_t count = fat32_secperclus;
57 - if (count + i > (maxsize >> 11)) count = (maxsize >> 11) - i;
58 - uint32_t fatsector = fat32_fatbase + (clusterchain >> 9);
59 - if (storage_read(fatsector, 1, &((uint32_t*)buffer)[i << 9])) return 1;
60 - clusterchain = ((uint32_t*)buffer)[(i << 9) + (clusterchain & 0x1FF)];
61 - if (storage_read(sector, count, &((uint32_t*)buffer)[i << 9])) return 1;
62 - i += count;
63 - if (clusterchain >= 0x0ffffff0) return 0;
64 - }
65 - return clusterchain;
66 -}
67 -
68 -uint32_t fat32_get_direntry(uint32_t clusterchain, const char* filename, uint32_t* filesize)
69 -{
70 - uint32_t i, j;
71 - while (clusterchain > 1 && clusterchain < 0x0ffffff0)
72 - {
73 - uint32_t sector = (clusterchain - 2) * fat32_secperclus + fat32_database;
74 - for (j = 0; j < fat32_secperclus; j++)
75 - {
76 - if (storage_read(sector + j, 1, fat32_buf1)) return 1;
77 - for (i = 0; i < 0x200; i += 8)
78 - if (((uint8_t*)fat32_buf1)[i << 2] == 0) return 0;
79 - else if (((uint8_t*)fat32_buf1)[i << 2] == 0xe5) continue;
80 - else if (memcmp(&fat32_buf1[i], filename, 11) == 0)
81 - {
82 - *filesize = fat32_buf1[i + 7];
83 - return (((uint16_t*)fat32_buf1)[(i << 1) + 0xA] << 16)
84 - | ((uint16_t*)fat32_buf1)[(i << 1) + 0xD];
85 - }
86 - }
87 - uint32_t fatsector = fat32_fatbase + (clusterchain >> 9);
88 - if (storage_read(fatsector, 1, fat32_buf1)) return 1;
89 - clusterchain = fat32_buf1[(i << 9) + (clusterchain & 0x1FF)];
90 - }
91 - return 0;
92 -}
93 -
94 -uint32_t fat32_delete_clusterchain(uint32_t clusterchain)
95 -{
96 - while (1)
97 - {
98 - uint32_t fatsector = fat32_fatbase + (clusterchain >> 9);
99 - if (storage_read(fatsector, 1, fat32_buf1)) return 1;
100 - clusterchain = fat32_buf1[clusterchain & 0x1FF];
101 - fat32_buf1[(clusterchain & 0x1FF)] = 0;
102 - if (storage_write(fatsector, 1, fat32_buf1)) return 1;
103 - if (clusterchain >= 0x0ffffff0) return 0;
104 - }
105 -}
106 -
107 -uint32_t fat32_delete_direntry(uint32_t clusterchain, const char* filename)
108 -{
109 - uint32_t i, j;
110 - while (clusterchain > 1 && clusterchain < 0x0ffffff0)
111 - {
112 - uint32_t sector = (clusterchain - 2) * fat32_secperclus + fat32_database;
113 - for (j = 0; j < fat32_secperclus; j++)
114 - {
115 - if (storage_read(sector + j, 1, fat32_buf1)) return 1;
116 - for (i = 0; i < 0x200; i += 8)
117 - if (((uint8_t*)fat32_buf1)[i << 2] == 0) return 0;
118 - else if (((uint8_t*)fat32_buf1)[i << 2] == 0xe5) continue;
119 - else if (memcmp(&fat32_buf1[i], filename, 11) == 0)
120 - {
121 - ((uint8_t*)fat32_buf1)[i << 2] = 0xe5;
122 - if (storage_write(sector + j, 1, fat32_buf1)) return 1;
123 - return 0;
124 - }
125 - }
126 - uint32_t fatsector = fat32_fatbase + (clusterchain >> 9);
127 - if (storage_read(fatsector, 1, fat32_buf1)) return 1;
128 - clusterchain = fat32_buf1[(i << 9) + (clusterchain & 0x1FF)];
129 - }
130 - return 0;
131 -}
132 -
133 -uint32_t fat32_store_stream(void* buffer, uint32_t size)
134 -{
135 - uint32_t i;
136 - uint32_t clusterchain = 0;
137 - uint32_t scanidx = 2;
138 - uint32_t scansect = 0xffffffff;
139 - uint32_t lastidx;
140 - uint32_t lastsect = 0xffffffff;
141 - uint32_t dirty = 0;
142 - while (size)
143 - {
144 - while (scanidx < fat32_clustercount + 2)
145 - {
146 - if ((scanidx >> 9) != scansect)
147 - {
148 - scansect = scanidx >> 9;
149 - if (storage_read(fat32_fatbase + scansect, 1, fat32_buf1)) return 0;
150 - }
151 - if (!fat32_buf1[scanidx & 0x1ff]) break;
152 - scanidx++;
153 - }
154 - if (scanidx >= fat32_clustercount + 2) return 0;
155 - if (!clusterchain) clusterchain = scanidx;
156 - else
157 - {
158 - fat32_buf2[lastidx & 0x1ff] = scanidx;
159 - dirty = 1;
160 - }
161 - lastidx = scanidx;
162 - if ((lastidx >> 9) != lastsect)
163 - {
164 - if (dirty)
165 - if (storage_write(fat32_fatbase + lastsect, 1, fat32_buf2)) return 0;
166 - dirty = 0;
167 - lastsect = lastidx >> 9;
168 - memcpy(fat32_buf2, fat32_buf1, 0x800);
169 - }
170 - uint32_t sector = (scanidx - 2) * fat32_secperclus + fat32_database;
171 - uint32_t count = (size + 0x7ff) >> 11;
172 - if (count > fat32_secperclus) count = fat32_secperclus;
173 - if (storage_write(sector, count, &((uint32_t*)buffer)[i << 9])) return 0;
174 - if ((count << 11) >= size)
175 - {
176 - fat32_buf2[lastidx & 0x1ff] = 0x0fffffff;
177 - if (storage_write(fat32_fatbase + lastsect, 1, fat32_buf2)) return 0;
178 - break;
179 - }
180 - size -= count << 11;
181 - buffer = (void*)((uint32_t)buffer + (count << 11));
182 - scanidx++;
183 - }
184 - return clusterchain;
185 -}
186 -
187 -void fat32_set_direntry(uint32_t* ptr, const char* filename, uint32_t filechain,
188 - uint32_t filesize, uint32_t flags)
189 -{
190 - memcpy(ptr, filename, 11);
191 - ((uint16_t*)ptr)[0xa] = filechain >> 16;
192 - ((uint8_t*)ptr)[0xb] = flags;
193 - ((uint16_t*)ptr)[0xa] = filechain >> 16;
194 - ((uint16_t*)ptr)[0xd] = filechain & 0xffff;
195 - ptr[7] = filesize;
196 -}
197 -
198 -uint32_t fat32_store_direntry(uint32_t dirchain, const char* filename,
199 - uint32_t filechain, uint32_t filesize, uint32_t flags)
200 -{
201 - uint32_t i, j;
202 - uint32_t lastidx;
203 - while (dirchain > 1 && dirchain < 0x0ffffff0)
204 - {
205 - uint32_t sector = (dirchain - 2) * fat32_secperclus + fat32_database;
206 - for (j = 0; j < fat32_secperclus; j++)
207 - {
208 - if (storage_read(sector + j, 1, fat32_buf1)) return 1;
209 - for (i = 0; i < 0x200; i += 8)
210 - if (((uint8_t*)fat32_buf1)[i << 2] == 0
211 - || ((uint8_t*)fat32_buf1)[i << 2] == 0xe5)
212 - {
213 - fat32_set_direntry(&fat32_buf1[i], filename, filechain, filesize, flags);
214 - if (storage_write(sector + j, 1, fat32_buf1)) return 1;
215 - return 0;
216 - }
217 - }
218 - uint32_t fatsector = fat32_fatbase + (dirchain >> 9);
219 - if (storage_read(fatsector, 1, fat32_buf1)) return 1;
220 - lastidx = dirchain;
221 - dirchain = fat32_buf1[(i << 9) + (dirchain & 0x1FF)];
222 - }
223 - uint32_t scanidx = 2;
224 - uint32_t scansect = 0xffffffff;
225 - while (scanidx < fat32_clustercount + 2)
226 - {
227 - if ((scanidx >> 9) != scansect)
228 - {
229 - scansect = scanidx >> 9;
230 - if (storage_read(fat32_fatbase + scansect, 1, fat32_buf1)) return 1;
231 - }
232 - if (!fat32_buf1[scanidx & 0x1ff]) break;
233 - scanidx++;
234 - }
235 - if (scanidx >= fat32_clustercount + 2) return 1;
236 - fat32_buf1[scanidx & 0x1ff] = 0x0fffffff;
237 - if (storage_write(fat32_fatbase + scansect, 1, fat32_buf1)) return 1;
238 - if (storage_read(fat32_fatbase + (lastidx >> 9), 1, fat32_buf1)) return 1;
239 - fat32_buf1[lastidx & 0x1ff] = scanidx;
240 - if (storage_write(fat32_fatbase + scansect, 1, fat32_buf1)) return 1;
241 - uint32_t sector = (scanidx - 2) * fat32_secperclus + fat32_database;
242 - for (i = 0; i < fat32_secperclus; i++)
243 - {
244 - memset(fat32_buf1, 0, 0x800);
245 - if (!i) fat32_set_direntry(fat32_buf1, filename, filechain, filesize, flags);
246 - if (storage_write(sector + i, 1, fat32_buf1)) return 1;
247 - }
248 - return 0;
249 -}
250 -
251 -uint32_t fat32_create_dir(uint32_t parent, const char* dirname)
252 -{
253 - uint32_t i;
254 - uint32_t scanidx = 2;
255 - uint32_t scansect = 0xffffffff;
256 - while (scanidx < fat32_clustercount + 2)
257 - {
258 - if ((scanidx >> 9) != scansect)
259 - {
260 - scansect = scanidx >> 9;
261 - if (storage_read(fat32_fatbase + scansect, 1, fat32_buf1)) return 0;
262 - }
263 - if (!fat32_buf1[scanidx & 0x1ff]) break;
264 - scanidx++;
265 - }
266 - if (scanidx >= fat32_clustercount + 2) return 0;
267 - fat32_buf1[scanidx & 0x1ff] = 0x0fffffff;
268 - if (storage_write(fat32_fatbase + scansect, 1, fat32_buf1)) return 0;
269 - fat32_store_direntry(parent, dirname, scanidx, 0, 0x10);
270 - uint32_t sector = (scanidx - 2) * fat32_secperclus + fat32_database;
271 - for (i = 0; i < fat32_secperclus; i++)
272 - {
273 - memset(fat32_buf1, 0, 0x800);
274 - if (!i)
275 - {
276 - fat32_set_direntry(fat32_buf1, ". ", scanidx, 0, 0x10);
277 - if (parent == fat32_rootdirclus) parent = 0;
278 - fat32_set_direntry(&fat32_buf1[8], ".. ", parent, 0, 0x10);
279 - }
280 - if (storage_write(sector + i, 1, fat32_buf1)) return 0;
281 - }
282 - return scanidx;
283 -}
284 -
285 -uint32_t fat32_read_file(const char* filename, uint32_t maxsize, void* buffer, uint32_t* filesize)
286 -{
287 -}
288 -
289 -uint32_t fat32_get_partition_start()
290 -{
291 - return fat32_startsector;
292 -}
293 -
294 -uint32_t fat32_init()
295 -{
296 - uint32_t i;
297 - fat32_ok = 0;
298 - fat32_startsector = 0xFFFFFFFF;
299 - if (storage_init()) return 1;
300 -
301 - if (storage_read(0, 1, fat32_buf1)) return 1;
302 -
303 - if (*((uint16_t*)((uint32_t)fat32_buf1 + 0x1FE)) != 0xAA55)
304 - {
305 - return 1;
306 - }
307 -
308 - for (i = 0x1C2; i < 0x200; i += 0x10)
309 - if (((uint8_t*)fat32_buf1)[i] == 0xB)
310 - {
311 - fat32_startsector = *((uint16_t*)((uint32_t)fat32_buf1 + i + 4))
312 - | (*((uint16_t*)((uint32_t)fat32_buf1 + i + 6)) << 16);
313 - break;
314 - }
315 -
316 - if (fat32_startsector == 0xFFFFFFFF
317 - && *((uint16_t*)((uint32_t)fat32_buf1 + 0x52)) == 0x4146
318 - && *((uint8_t*)((uint32_t)fat32_buf1 + 0x54)) == 0x54)
319 - fat32_startsector = 0;
320 -
321 - if (fat32_startsector == 0xFFFFFFFF) return 1;
322 -
323 - if (storage_read(fat32_startsector, 1, fat32_buf1)) return 1;
324 -
325 - if (*((uint16_t*)((uint32_t)fat32_buf1 + 0x1FE)) != 0xAA55) return 1;
326 -
327 - if (((uint8_t*)fat32_buf1)[0xB] != 0 || ((uint8_t*)fat32_buf1)[0xC] != 8) return 1;
328 -
329 - fat32_secperclus = ((uint8_t*)fat32_buf1)[0xD];
330 - uint32_t reserved = ((uint16_t*)fat32_buf1)[0x7];
331 - fat32_fatcount = ((uint8_t*)fat32_buf1)[0x10];
332 -
333 - if (((uint8_t*)fat32_buf1)[0x11] != 0) return 1;
334 -
335 - fat32_sectorcount = fat32_buf1[8];
336 - fat32_fatsize = fat32_buf1[9];
337 -
338 - if (((uint16_t*)fat32_buf1)[0x15] != 0) return 1;
339 -
340 - fat32_rootdirclus = fat32_buf1[0xB];
341 -
342 - fat32_clustercount = (fat32_sectorcount - reserved
343 - - fat32_fatcount * fat32_fatsize) / fat32_secperclus;
344 -
345 - fat32_fatbase = fat32_startsector + reserved;
346 - fat32_database = fat32_fatbase + fat32_fatcount * fat32_fatsize;
347 -
348 - fat32_ok = 1;
349 - return 0;
350 -}
Index: embios/trunk/fat32.h
@@ -1,44 +0,0 @@
2 -//
3 -//
4 -// Copyright 2010 TheSeven
5 -//
6 -//
7 -// This file is part of emBIOS.
8 -//
9 -// emBIOS is free software: you can redistribute it and/or
10 -// modify it under the terms of the GNU General Public License as
11 -// published by the Free Software Foundation, either version 2 of the
12 -// License, or (at your option) any later version.
13 -//
14 -// emBIOS is distributed in the hope that it will be useful,
15 -// but WITHOUT ANY WARRANTY; without even the implied warranty of
16 -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 -// See the GNU General Public License for more details.
18 -//
19 -// You should have received a copy of the GNU General Public License along
20 -// with emBIOS. If not, see <http://www.gnu.org/licenses/>.
21 -//
22 -//
23 -
24 -
25 -#ifndef __FAT32_H__
26 -#define __FAT32_H__
27 -
28 -#include "global.h"
29 -
30 -uint32_t fat32_get_root();
31 -uint32_t fat32_get_clusterchain(uint32_t clusterchain, uint32_t maxsize, void* buffer);
32 -uint32_t fat32_get_direntry(uint32_t clusterchain, const char* filename, uint32_t* filesize);
33 -uint32_t fat32_delete_clusterchain(uint32_t clusterchain);
34 -uint32_t fat32_delete_direntry(uint32_t clusterchain, const char* filename);
35 -uint32_t fat32_store_stream(void* buffer, uint32_t size);
36 -uint32_t fat32_store_direntry(uint32_t dirchain, const char* filename,
37 - uint32_t filechain, uint32_t filesize, uint32_t flags);
38 -uint32_t fat32_create_dir(uint32_t parent, const char* dirname);
39 -uint32_t fat32_read_file(const char* filename, uint32_t maxsize, void* buffer, uint32_t* filesize);
40 -uint32_t fat32_resize_patchdirs(uint32_t clusterchain, uint32_t clustoffset);
41 -uint32_t fat32_resize_fulldisk();
42 -uint32_t fat32_get_partition_start();
43 -uint32_t fat32_init();
44 -
45 -#endif
Index: embios/trunk/init.c
@@ -45,5 +45,4 @@
4646 i2c_init();
4747 pmu_init();
4848 usb_init();
49 - DEBUGF("init completed!");
5049 }
\ No newline at end of file
Index: embios/trunk/disk.c
@@ -0,0 +1,232 @@
 2+/***************************************************************************
 3+ * __________ __ ___.
 4+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
 5+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
 6+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
 7+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
 8+ * \/ \/ \/ \/ \/
 9+ * $Id: disk.c 26629 2010-06-06 13:28:13Z gevaerts $
 10+ *
 11+ * Copyright (C) 2002 by Björn Stenberg
 12+ *
 13+ * This program is free software; you can redistribute it and/or
 14+ * modify it under the terms of the GNU General Public License
 15+ * as published by the Free Software Foundation; either version 2
 16+ * of the License, or (at your option) any later version.
 17+ *
 18+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 19+ * KIND, either express or implied.
 20+ *
 21+ ****************************************************************************/
 22+#include <stdio.h>
 23+#include "kernel.h"
 24+#include "storage.h"
 25+#include "debug.h"
 26+#include "fat.h"
 27+#ifdef HAVE_HOTSWAP
 28+#include "dir.h" /* for release_dirs() */
 29+#include "file.h" /* for release_files() */
 30+#endif
 31+#include "disk.h"
 32+#include <string.h>
 33+
 34+/* Partition table entry layout:
 35+ -----------------------
 36+ 0: 0x80 - active
 37+ 1: starting head
 38+ 2: starting sector
 39+ 3: starting cylinder
 40+ 4: partition type
 41+ 5: end head
 42+ 6: end sector
 43+ 7: end cylinder
 44+ 8-11: starting sector (LBA)
 45+ 12-15: nr of sectors in partition
 46+*/
 47+
 48+#define BYTES2INT32(array,pos) \
 49+ ((long)array[pos] | ((long)array[pos+1] << 8 ) | \
 50+ ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
 51+
 52+static const unsigned char fat_partition_types[] = {
 53+ 0x0b, 0x1b, /* FAT32 + hidden variant */
 54+ 0x0c, 0x1c, /* FAT32 (LBA) + hidden variant */
 55+#ifdef HAVE_FAT16SUPPORT
 56+ 0x04, 0x14, /* FAT16 <= 32MB + hidden variant */
 57+ 0x06, 0x16, /* FAT16 > 32MB + hidden variant */
 58+ 0x0e, 0x1e, /* FAT16 (LBA) + hidden variant */
 59+#endif
 60+};
 61+
 62+static struct partinfo part[NUM_DRIVES*4]; /* space for 4 partitions on 2 drives */
 63+static int vol_drive[NUM_VOLUMES]; /* mounted to which drive (-1 if none) */
 64+static struct mutex disk_mutex;
 65+
 66+struct partinfo* disk_init(IF_MD_NONVOID(int drive))
 67+{
 68+ int i;
 69+ unsigned char sector[SECTOR_SIZE];
 70+#ifdef HAVE_MULTIDRIVE
 71+ /* For each drive, start at a different position, in order not to destroy
 72+ the first entry of drive 0.
 73+ That one is needed to calculate config sector position. */
 74+ struct partinfo* pinfo = &part[drive*4];
 75+ if ((size_t)drive >= sizeof(part)/sizeof(*part)/4)
 76+ return NULL; /* out of space in table */
 77+#else
 78+ struct partinfo* pinfo = part;
 79+ const int drive = 0;
 80+ (void)drive;
 81+#endif
 82+
 83+ storage_read_sectors(IF_MD2(drive,) 0,1, sector);
 84+ /* check that the boot sector is initialized */
 85+ if ( (sector[510] != 0x55) ||
 86+ (sector[511] != 0xaa)) {
 87+ DEBUGF("Bad boot sector signature\n");
 88+ return NULL;
 89+ }
 90+
 91+ /* parse partitions */
 92+ for ( i=0; i<4; i++ ) {
 93+ unsigned char* ptr = sector + 0x1be + 16*i;
 94+ pinfo[i].type = ptr[4];
 95+ pinfo[i].start = BYTES2INT32(ptr, 8);
 96+ pinfo[i].size = BYTES2INT32(ptr, 12);
 97+
 98+ DEBUGF("Part%d: Type %02x, start: %08lx size: %08lx\n",
 99+ i,pinfo[i].type,pinfo[i].start,pinfo[i].size);
 100+
 101+ /* extended? */
 102+ if ( pinfo[i].type == 5 ) {
 103+ /* not handled yet */
 104+ }
 105+ }
 106+ return pinfo;
 107+}
 108+
 109+struct partinfo* disk_partinfo(int partition)
 110+{
 111+ return &part[partition];
 112+}
 113+
 114+void disk_init_subsystem(void)
 115+{
 116+ mutex_init(&disk_mutex);
 117+}
 118+
 119+int disk_mount_all(void)
 120+{
 121+ int mounted=0;
 122+ int i;
 123+
 124+#ifdef HAVE_HOTSWAP
 125+ mutex_lock(&disk_mutex);
 126+#endif
 127+
 128+ fat_init(); /* reset all mounted partitions */
 129+ for (i=0; i<NUM_VOLUMES; i++)
 130+ vol_drive[i] = -1; /* mark all as unassigned */
 131+
 132+#ifndef HAVE_MULTIDRIVE
 133+ mounted = disk_mount(0);
 134+#else
 135+ for(i=0;i<NUM_DRIVES;i++)
 136+ {
 137+#ifdef HAVE_HOTSWAP
 138+ if (storage_present(i))
 139+#endif
 140+ mounted += disk_mount(i);
 141+ }
 142+#endif
 143+
 144+#ifdef HAVE_HOTSWAP
 145+ mutex_unlock(&disk_mutex);
 146+#endif
 147+ return mounted;
 148+}
 149+
 150+static int get_free_volume(void)
 151+{
 152+ int i;
 153+ for (i=0; i<NUM_VOLUMES; i++)
 154+ {
 155+ if (vol_drive[i] == -1) /* unassigned? */
 156+ return i;
 157+ }
 158+
 159+ return -1; /* none found */
 160+}
 161+
 162+int disk_mount(int drive)
 163+{
 164+ int i;
 165+ int mounted = 0; /* reset partition-on-drive flag */
 166+ int volume;
 167+ struct partinfo* pinfo;
 168+
 169+#ifdef HAVE_HOTSWAP
 170+ mutex_lock(&disk_mutex);
 171+#endif
 172+
 173+ volume = get_free_volume();
 174+ pinfo = disk_init(IF_MD(drive));
 175+
 176+ if (pinfo == NULL)
 177+ {
 178+#ifdef HAVE_HOTSWAP
 179+ mutex_unlock(&disk_mutex);
 180+#endif
 181+ return 0;
 182+ }
 183+ for (i = 0; volume != -1 && i<4 && mounted<NUM_VOLUMES_PER_DRIVE; i++)
 184+ {
 185+ if (memchr(fat_partition_types, pinfo[i].type,
 186+ sizeof(fat_partition_types)) == NULL)
 187+ continue; /* not an accepted partition type */
 188+
 189+ if (!fat_mount(IF_MV2(volume,) IF_MD2(drive,) pinfo[i].start))
 190+ {
 191+ mounted++;
 192+ vol_drive[volume] = drive; /* remember the drive for this volume */
 193+ volume = get_free_volume(); /* prepare next entry */
 194+ }
 195+ }
 196+
 197+ if (mounted == 0 && volume != -1) /* none of the 4 entries worked? */
 198+ { /* try "superfloppy" mode */
 199+ DEBUGF("No partition found, trying to mount sector 0.\n");
 200+ if (!fat_mount(IF_MV2(volume,) IF_MD2(drive,) 0))
 201+ {
 202+ mounted = 1;
 203+ vol_drive[volume] = drive; /* remember the drive for this volume */
 204+ }
 205+ }
 206+#ifdef HAVE_HOTSWAP
 207+ mutex_unlock(&disk_mutex);
 208+#endif
 209+ return mounted;
 210+}
 211+
 212+#ifdef HAVE_HOTSWAP
 213+int disk_unmount(int drive)
 214+{
 215+ int unmounted = 0;
 216+ int i;
 217+ mutex_lock(&disk_mutex);
 218+ for (i=0; i<NUM_VOLUMES; i++)
 219+ {
 220+ if (vol_drive[i] == drive)
 221+ { /* force releasing resources */
 222+ vol_drive[i] = -1; /* mark unused */
 223+ unmounted++;
 224+ release_files(i);
 225+ release_dirs(i);
 226+ fat_unmount(i, false);
 227+ }
 228+ }
 229+ mutex_unlock(&disk_mutex);
 230+
 231+ return unmounted;
 232+}
 233+#endif /* #ifdef HAVE_HOTSWAP */
Index: embios/trunk/fat.c
@@ -0,0 +1,2523 @@
 2+/***************************************************************************
 3+ * __________ __ ___.
 4+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
 5+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
 6+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
 7+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
 8+ * \/ \/ \/ \/ \/
 9+ * $Id: fat.c 25459 2010-04-03 22:02:09Z gevaerts $
 10+ *
 11+ * Copyright (C) 2002 by Linus Nielsen Feltzing
 12+ *
 13+ * This program is free software; you can redistribute it and/or
 14+ * modify it under the terms of the GNU General Public License
 15+ * as published by the Free Software Foundation; either version 2
 16+ * of the License, or (at your option) any later version.
 17+ *
 18+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 19+ * KIND, either express or implied.
 20+ *
 21+ ****************************************************************************/
 22+#include <stdio.h>
 23+#include <string.h>
 24+#include <stdlib.h>
 25+#include <ctype.h>
 26+#include <stdbool.h>
 27+#include "fat.h"
 28+#include "storage.h"
 29+#include "debug.h"
 30+#include "panic.h"
 31+#include "system.h"
 32+#include "timefuncs.h"
 33+#include "kernel.h"
 34+#include "rbunicode.h"
 35+/*#define LOGF_ENABLE*/
 36+#include "logf.h"
 37+
 38+#define BYTES2INT16(array,pos) \
 39+ (array[pos] | (array[pos+1] << 8 ))
 40+#define BYTES2INT32(array,pos) \
 41+ ((long)array[pos] | ((long)array[pos+1] << 8 ) | \
 42+ ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
 43+
 44+#define FATTYPE_FAT12 0
 45+#define FATTYPE_FAT16 1
 46+#define FATTYPE_FAT32 2
 47+
 48+/* BPB offsets; generic */
 49+#define BS_JMPBOOT 0
 50+#define BS_OEMNAME 3
 51+#define BPB_BYTSPERSEC 11
 52+#define BPB_SECPERCLUS 13
 53+#define BPB_RSVDSECCNT 14
 54+#define BPB_NUMFATS 16
 55+#define BPB_ROOTENTCNT 17
 56+#define BPB_TOTSEC16 19
 57+#define BPB_MEDIA 21
 58+#define BPB_FATSZ16 22
 59+#define BPB_SECPERTRK 24
 60+#define BPB_NUMHEADS 26
 61+#define BPB_HIDDSEC 28
 62+#define BPB_TOTSEC32 32
 63+
 64+/* fat12/16 */
 65+#define BS_DRVNUM 36
 66+#define BS_RESERVED1 37
 67+#define BS_BOOTSIG 38
 68+#define BS_VOLID 39
 69+#define BS_VOLLAB 43
 70+#define BS_FILSYSTYPE 54
 71+
 72+/* fat32 */
 73+#define BPB_FATSZ32 36
 74+#define BPB_EXTFLAGS 40
 75+#define BPB_FSVER 42
 76+#define BPB_ROOTCLUS 44
 77+#define BPB_FSINFO 48
 78+#define BPB_BKBOOTSEC 50
 79+#define BS_32_DRVNUM 64
 80+#define BS_32_BOOTSIG 66
 81+#define BS_32_VOLID 67
 82+#define BS_32_VOLLAB 71
 83+#define BS_32_FILSYSTYPE 82
 84+
 85+#define BPB_LAST_WORD 510
 86+
 87+
 88+/* attributes */
 89+#define FAT_ATTR_LONG_NAME (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
 90+ FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID)
 91+#define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
 92+ FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \
 93+ FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE )
 94+
 95+/* NTRES flags */
 96+#define FAT_NTRES_LC_NAME 0x08
 97+#define FAT_NTRES_LC_EXT 0x10
 98+
 99+#define FATDIR_NAME 0
 100+#define FATDIR_ATTR 11
 101+#define FATDIR_NTRES 12
 102+#define FATDIR_CRTTIMETENTH 13
 103+#define FATDIR_CRTTIME 14
 104+#define FATDIR_CRTDATE 16
 105+#define FATDIR_LSTACCDATE 18
 106+#define FATDIR_FSTCLUSHI 20
 107+#define FATDIR_WRTTIME 22
 108+#define FATDIR_WRTDATE 24
 109+#define FATDIR_FSTCLUSLO 26
 110+#define FATDIR_FILESIZE 28
 111+
 112+#define FATLONG_ORDER 0
 113+#define FATLONG_TYPE 12
 114+#define FATLONG_CHKSUM 13
 115+#define FATLONG_LAST_LONG_ENTRY 0x40
 116+#define FATLONG_NAME_BYTES_PER_ENTRY 26
 117+/* at most 20 LFN entries, keep coherent with fat_dir->longname size ! */
 118+#define FATLONG_MAX_ORDER 20
 119+
 120+#define FATLONG_NAME_CHUNKS 3
 121+static unsigned char FATLONG_NAME_POS[FATLONG_NAME_CHUNKS] = {1, 14, 28};
 122+static unsigned char FATLONG_NAME_SIZE[FATLONG_NAME_CHUNKS] = {10, 12, 4};
 123+
 124+#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4)
 125+#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2)
 126+#define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE)
 127+#define DIR_ENTRY_SIZE 32
 128+#define NAME_BYTES_PER_ENTRY 13
 129+#define FAT_BAD_MARK 0x0ffffff7
 130+#define FAT_EOF_MARK 0x0ffffff8
 131+#define FAT_LONGNAME_PAD_BYTE 0xff
 132+#define FAT_LONGNAME_PAD_UCS 0xffff
 133+
 134+struct fsinfo {
 135+ unsigned long freecount; /* last known free cluster count */
 136+ unsigned long nextfree; /* first cluster to start looking for free
 137+ clusters, or 0xffffffff for no hint */
 138+};
 139+/* fsinfo offsets */
 140+#define FSINFO_FREECOUNT 488
 141+#define FSINFO_NEXTFREE 492
 142+
 143+/* Note: This struct doesn't hold the raw values after mounting if
 144+ * bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte
 145+ * physical sectors. */
 146+struct bpb
 147+{
 148+ int bpb_bytspersec; /* Bytes per sector, typically 512 */
 149+ unsigned int bpb_secperclus; /* Sectors per cluster */
 150+ int bpb_rsvdseccnt; /* Number of reserved sectors */
 151+ int bpb_numfats; /* Number of FAT structures, typically 2 */
 152+ int bpb_totsec16; /* Number of sectors on the volume (old 16-bit) */
 153+ int bpb_media; /* Media type (typically 0xf0 or 0xf8) */
 154+ int bpb_fatsz16; /* Number of used sectors per FAT structure */
 155+ unsigned long bpb_totsec32; /* Number of sectors on the volume
 156+ (new 32-bit) */
 157+ unsigned int last_word; /* 0xAA55 */
 158+
 159+ /**** FAT32 specific *****/
 160+ long bpb_fatsz32;
 161+ long bpb_rootclus;
 162+ long bpb_fsinfo;
 163+
 164+ /* variables for internal use */
 165+ unsigned long fatsize;
 166+ unsigned long totalsectors;
 167+ unsigned long rootdirsector;
 168+ unsigned long firstdatasector;
 169+ unsigned long startsector;
 170+ unsigned long dataclusters;
 171+ struct fsinfo fsinfo;
 172+#ifdef HAVE_FAT16SUPPORT
 173+ int bpb_rootentcnt; /* Number of dir entries in the root */
 174+ /* internals for FAT16 support */
 175+ bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */
 176+ unsigned int rootdiroffset; /* sector offset of root dir relative to start
 177+ * of first pseudo cluster */
 178+#endif /* #ifdef HAVE_FAT16SUPPORT */
 179+#ifdef HAVE_MULTIVOLUME
 180+#ifdef HAVE_MULTIDRIVE
 181+ int drive; /* on which physical device is this located */
 182+#endif
 183+ bool mounted; /* flag if this volume is mounted */
 184+#endif
 185+};
 186+
 187+static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */
 188+static bool initialized = false;
 189+
 190+static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb));
 191+static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb));
 192+static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb));
 193+static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,)
 194+ long secnum, bool dirty);
 195+static void create_dos_name(const unsigned char *name, unsigned char *newname);
 196+static void randomize_dos_name(unsigned char *name);
 197+static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,)
 198+ unsigned long start);
 199+static int transfer(IF_MV2(struct bpb* fat_bpb,) unsigned long start,
 200+ long count, char* buf, bool write );
 201+
 202+#define FAT_CACHE_SIZE 0x20
 203+#define FAT_CACHE_MASK (FAT_CACHE_SIZE-1)
 204+
 205+struct fat_cache_entry
 206+{
 207+ long secnum;
 208+ bool inuse;
 209+ bool dirty;
 210+#ifdef HAVE_MULTIVOLUME
 211+ struct bpb* fat_vol ; /* shared cache for all volumes */
 212+#endif
 213+};
 214+
 215+static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE];
 216+static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE];
 217+static struct mutex cache_mutex SHAREDBSS_ATTR;
 218+
 219+#if defined(HAVE_HOTSWAP) && !(CONFIG_STORAGE & STORAGE_MMC) /* A better condition ?? */
 220+void fat_lock(void)
 221+{
 222+ mutex_lock(&cache_mutex);
 223+}
 224+
 225+void fat_unlock(void)
 226+{
 227+ mutex_unlock(&cache_mutex);
 228+}
 229+#endif
 230+
 231+static long cluster2sec(IF_MV2(struct bpb* fat_bpb,) long cluster)
 232+{
 233+#ifndef HAVE_MULTIVOLUME
 234+ struct bpb* fat_bpb = &fat_bpbs[0];
 235+#endif
 236+#ifdef HAVE_FAT16SUPPORT
 237+ /* negative clusters (FAT16 root dir) don't get the 2 offset */
 238+ int zerocluster = cluster < 0 ? 0 : 2;
 239+#else
 240+ const long zerocluster = 2;
 241+#endif
 242+
 243+ if (cluster > (long)(fat_bpb->dataclusters + 1))
 244+ {
 245+ DEBUGF( "cluster2sec() - Bad cluster number (%ld)\n", cluster);
 246+ return -1;
 247+ }
 248+
 249+ return (cluster - zerocluster) * fat_bpb->bpb_secperclus
 250+ + fat_bpb->firstdatasector;
 251+}
 252+
 253+void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
 254+{
 255+#ifndef HAVE_MULTIVOLUME
 256+ const int volume = 0;
 257+#endif
 258+ struct bpb* fat_bpb = &fat_bpbs[volume];
 259+ if (size)
 260+ *size = fat_bpb->dataclusters * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024);
 261+ if (free)
 262+ *free = fat_bpb->fsinfo.freecount * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024);
 263+}
 264+
 265+void fat_init(void)
 266+{
 267+ unsigned int i;
 268+
 269+ if (!initialized)
 270+ {
 271+ initialized = true;
 272+ mutex_init(&cache_mutex);
 273+ }
 274+
 275+#ifdef HAVE_PRIORITY_SCHEDULING
 276+ /* Disable this because it is dangerous due to the assumption that
 277+ * mutex_unlock won't yield */
 278+ mutex_set_preempt(&cache_mutex, false);
 279+#endif
 280+
 281+ /* mark the FAT cache as unused */
 282+ for(i = 0;i < FAT_CACHE_SIZE;i++)
 283+ {
 284+ fat_cache[i].secnum = 8; /* We use a "safe" sector just in case */
 285+ fat_cache[i].inuse = false;
 286+ fat_cache[i].dirty = false;
 287+#ifdef HAVE_MULTIVOLUME
 288+ fat_cache[i].fat_vol = NULL;
 289+#endif
 290+ }
 291+#ifdef HAVE_MULTIVOLUME
 292+ /* mark the possible volumes as not mounted */
 293+ for (i=0; i<NUM_VOLUMES;i++)
 294+ {
 295+ fat_bpbs[i].mounted = false;
 296+ }
 297+#endif
 298+}
 299+
 300+int fat_mount(IF_MV2(int volume,) IF_MD2(int drive,) long startsector)
 301+{
 302+#ifndef HAVE_MULTIVOLUME
 303+ const int volume = 0;
 304+#endif
 305+ struct bpb* fat_bpb = &fat_bpbs[volume];
 306+ unsigned char buf[SECTOR_SIZE];
 307+ int rc;
 308+ int secmult;
 309+ long datasec;
 310+#ifdef HAVE_FAT16SUPPORT
 311+ int rootdirsectors;
 312+#endif
 313+
 314+ /* Read the sector */
 315+ rc = storage_read_sectors(IF_MD2(drive,) startsector,1,buf);
 316+ if(rc)
 317+ {
 318+ DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)\n", rc);
 319+ return rc * 10 - 1;
 320+ }
 321+
 322+ memset(fat_bpb, 0, sizeof(struct bpb));
 323+ fat_bpb->startsector = startsector;
 324+#ifdef HAVE_MULTIDRIVE
 325+ fat_bpb->drive = drive;
 326+#endif
 327+
 328+ fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC);
 329+ secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE;
 330+ /* Sanity check is performed later */
 331+
 332+ fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS];
 333+ fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf,BPB_RSVDSECCNT);
 334+ fat_bpb->bpb_numfats = buf[BPB_NUMFATS];
 335+ fat_bpb->bpb_media = buf[BPB_MEDIA];
 336+ fat_bpb->bpb_fatsz16 = secmult * BYTES2INT16(buf,BPB_FATSZ16);
 337+ fat_bpb->bpb_fatsz32 = secmult * BYTES2INT32(buf,BPB_FATSZ32);
 338+ fat_bpb->bpb_totsec16 = secmult * BYTES2INT16(buf,BPB_TOTSEC16);
 339+ fat_bpb->bpb_totsec32 = secmult * BYTES2INT32(buf,BPB_TOTSEC32);
 340+ fat_bpb->last_word = BYTES2INT16(buf,BPB_LAST_WORD);
 341+
 342+ /* calculate a few commonly used values */
 343+ if (fat_bpb->bpb_fatsz16 != 0)
 344+ fat_bpb->fatsize = fat_bpb->bpb_fatsz16;
 345+ else
 346+ fat_bpb->fatsize = fat_bpb->bpb_fatsz32;
 347+
 348+ if (fat_bpb->bpb_totsec16 != 0)
 349+ fat_bpb->totalsectors = fat_bpb->bpb_totsec16;
 350+ else
 351+ fat_bpb->totalsectors = fat_bpb->bpb_totsec32;
 352+
 353+#ifdef HAVE_FAT16SUPPORT
 354+ fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT);
 355+ if (!fat_bpb->bpb_bytspersec)
 356+ return -2;
 357+ rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE
 358+ + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec);
 359+#endif /* #ifdef HAVE_FAT16SUPPORT */
 360+
 361+ fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt
 362+#ifdef HAVE_FAT16SUPPORT
 363+ + rootdirsectors
 364+#endif
 365+ + fat_bpb->bpb_numfats * fat_bpb->fatsize;
 366+
 367+ /* Determine FAT type */
 368+ datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector;
 369+ if (fat_bpb->bpb_secperclus)
 370+ fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus;
 371+ else
 372+ return -2;
 373+
 374+#ifdef TEST_FAT
 375+ /*
 376+ we are sometimes testing with "illegally small" fat32 images,
 377+ so we don't use the proper fat32 test case for test code
 378+ */
 379+ if ( fat_bpb->bpb_fatsz16 )
 380+#else
 381+ if ( fat_bpb->dataclusters < 65525 )
 382+#endif
 383+ { /* FAT16 */
 384+#ifdef HAVE_FAT16SUPPORT
 385+ fat_bpb->is_fat16 = true;
 386+ if (fat_bpb->dataclusters < 4085)
 387+ { /* FAT12 */
 388+ DEBUGF("This is FAT12. Go away!\n");
 389+ return -2;
 390+ }
 391+#else /* #ifdef HAVE_FAT16SUPPORT */
 392+ DEBUGF("This is not FAT32. Go away!\n");
 393+ return -2;
 394+#endif /* #ifndef HAVE_FAT16SUPPORT */
 395+ }
 396+
 397+#ifdef HAVE_FAT16SUPPORT
 398+ if (fat_bpb->is_fat16)
 399+ { /* FAT16 specific part of BPB */
 400+ int dirclusters;
 401+ fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt
 402+ + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16;
 403+ dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1)
 404+ / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */
 405+ /* I assign negative pseudo cluster numbers for the root directory,
 406+ their range is counted upward until -1. */
 407+ fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data*/
 408+ fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus
 409+ - rootdirsectors;
 410+ }
 411+ else
 412+#endif /* #ifdef HAVE_FAT16SUPPORT */
 413+ { /* FAT32 specific part of BPB */
 414+ fat_bpb->bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS);
 415+ fat_bpb->bpb_fsinfo = secmult * BYTES2INT16(buf,BPB_FSINFO);
 416+ fat_bpb->rootdirsector = cluster2sec(IF_MV2(fat_bpb,)
 417+ fat_bpb->bpb_rootclus);
 418+ }
 419+
 420+ rc = bpb_is_sane(IF_MV(fat_bpb));
 421+ if (rc < 0)
 422+ {
 423+ DEBUGF( "fat_mount() - BPB is not sane\n");
 424+ return rc * 10 - 3;
 425+ }
 426+
 427+#ifdef HAVE_FAT16SUPPORT
 428+ if (fat_bpb->is_fat16)
 429+ {
 430+ fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */
 431+ fat_bpb->fsinfo.nextfree = 0xffffffff;
 432+ }
 433+ else
 434+#endif /* #ifdef HAVE_FAT16SUPPORT */
 435+ {
 436+ /* Read the fsinfo sector */
 437+ rc = storage_read_sectors(IF_MD2(drive,)
 438+ startsector + fat_bpb->bpb_fsinfo, 1, buf);
 439+ if (rc < 0)
 440+ {
 441+ DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)\n", rc);
 442+ return rc * 10 - 4;
 443+ }
 444+ fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT);
 445+ fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE);
 446+ }
 447+
 448+ /* calculate freecount if unset */
 449+ if ( fat_bpb->fsinfo.freecount == 0xffffffff )
 450+ {
 451+ fat_recalc_free(IF_MV(volume));
 452+ }
 453+
 454+ LDEBUGF("Freecount: %ld\n",fat_bpb->fsinfo.freecount);
 455+ LDEBUGF("Nextfree: 0x%lx\n",fat_bpb->fsinfo.nextfree);
 456+ LDEBUGF("Cluster count: 0x%lx\n",fat_bpb->dataclusters);
 457+ LDEBUGF("Sectors per cluster: %d\n",fat_bpb->bpb_secperclus);
 458+ LDEBUGF("FAT sectors: 0x%lx\n",fat_bpb->fatsize);
 459+
 460+#ifdef HAVE_MULTIVOLUME
 461+ fat_bpb->mounted = true;
 462+#endif
 463+
 464+ return 0;
 465+}
 466+
 467+#ifdef HAVE_HOTSWAP
 468+int fat_unmount(int volume, bool flush)
 469+{
 470+ int rc;
 471+#ifdef HAVE_MULTIVOLUME
 472+ struct bpb* fat_bpb = &fat_bpbs[volume];
 473+#else
 474+ (void)volume;
 475+#endif
 476+
 477+ if(flush)
 478+ {
 479+ rc = flush_fat(IF_MV(fat_bpb)); /* the clean way, while still alive */
 480+ }
 481+ else
 482+ { /* volume is not accessible any more, e.g. MMC removed */
 483+ int i;
 484+ mutex_lock(&cache_mutex);
 485+ for(i = 0;i < FAT_CACHE_SIZE;i++)
 486+ {
 487+ struct fat_cache_entry *fce = &fat_cache[i];
 488+ if(fce->inuse
 489+#ifdef HAVE_MULTIVOLUME
 490+ && fce->fat_vol == fat_bpb
 491+#endif
 492+ )
 493+ {
 494+ fce->inuse = false; /* discard all from that volume */
 495+ fce->dirty = false;
 496+ }
 497+ }
 498+ mutex_unlock(&cache_mutex);
 499+ rc = 0;
 500+ }
 501+#ifdef HAVE_MULTIVOLUME
 502+ fat_bpb->mounted = false;
 503+#endif
 504+ return rc;
 505+}
 506+#endif /* #ifdef HAVE_HOTSWAP */
 507+
 508+void fat_recalc_free(IF_MV_NONVOID(int volume))
 509+{
 510+#ifndef HAVE_MULTIVOLUME
 511+ const int volume = 0;
 512+#endif
 513+ struct bpb* fat_bpb = &fat_bpbs[volume];
 514+ long free = 0;
 515+ unsigned long i;
 516+#ifdef HAVE_FAT16SUPPORT
 517+ if (fat_bpb->is_fat16)
 518+ {
 519+ for (i = 0; i<fat_bpb->fatsize; i++) {
 520+ unsigned int j;
 521+ unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
 522+ for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) {
 523+ unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j;
 524+ if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
 525+ break;
 526+
 527+ if (letoh16(fat[j]) == 0x0000) {
 528+ free++;
 529+ if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
 530+ fat_bpb->fsinfo.nextfree = c;
 531+ }
 532+ }
 533+ }
 534+ }
 535+ else
 536+#endif /* #ifdef HAVE_FAT16SUPPORT */
 537+ {
 538+ for (i = 0; i<fat_bpb->fatsize; i++) {
 539+ unsigned int j;
 540+ unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
 541+ for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) {
 542+ unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j;
 543+ if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
 544+ break;
 545+
 546+ if (!(letoh32(fat[j]) & 0x0fffffff)) {
 547+ free++;
 548+ if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
 549+ fat_bpb->fsinfo.nextfree = c;
 550+ }
 551+ }
 552+ }
 553+ }
 554+ fat_bpb->fsinfo.freecount = free;
 555+ update_fsinfo(IF_MV(fat_bpb));
 556+}
 557+
 558+static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb))
 559+{
 560+#ifndef HAVE_MULTIVOLUME
 561+ struct bpb* fat_bpb = &fat_bpbs[0];
 562+#endif
 563+ if(fat_bpb->bpb_bytspersec % SECTOR_SIZE)
 564+ {
 565+ DEBUGF( "bpb_is_sane() - Error: sector size is not sane (%d)\n",
 566+ fat_bpb->bpb_bytspersec);
 567+ return -1;
 568+ }
 569+ if((long)fat_bpb->bpb_secperclus * (long)fat_bpb->bpb_bytspersec
 570+ > 128L*1024L)
 571+ {
 572+ DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K "
 573+ "(%d * %d = %d)\n",
 574+ fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus,
 575+ fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus);
 576+ return -2;
 577+ }
 578+ if(fat_bpb->bpb_numfats != 2)
 579+ {
 580+ DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n",
 581+ fat_bpb->bpb_numfats);
 582+ }
 583+ if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8)
 584+ {
 585+ DEBUGF( "bpb_is_sane() - Warning: Non-standard "
 586+ "media type (0x%02x)\n",
 587+ fat_bpb->bpb_media);
 588+ }
 589+ if(fat_bpb->last_word != 0xaa55)
 590+ {
 591+ DEBUGF( "bpb_is_sane() - Error: Last word is not "
 592+ "0xaa55 (0x%04x)\n", fat_bpb->last_word);
 593+ return -3;
 594+ }
 595+
 596+ if (fat_bpb->fsinfo.freecount >
 597+ (fat_bpb->totalsectors - fat_bpb->firstdatasector)/
 598+ fat_bpb->bpb_secperclus)
 599+ {
 600+ DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size "
 601+ "(0x%04lx)\n", fat_bpb->fsinfo.freecount);
 602+ return -4;
 603+ }
 604+
 605+ return 0;
 606+}
 607+
 608+static void flush_fat_sector(struct fat_cache_entry *fce,
 609+ unsigned char *sectorbuf)
 610+{
 611+ int rc;
 612+ long secnum;
 613+
 614+ /* With multivolume, use only the FAT info from the cached sector! */
 615+#ifdef HAVE_MULTIVOLUME
 616+ secnum = fce->secnum + fce->fat_vol->startsector;
 617+#else
 618+ secnum = fce->secnum + fat_bpbs[0].startsector;
 619+#endif
 620+
 621+ /* Write to the first FAT */
 622+ rc = storage_write_sectors(IF_MD2(fce->fat_vol->drive,)
 623+ secnum, 1,
 624+ sectorbuf);
 625+ if(rc < 0)
 626+ {
 627+ panicf("flush_fat_sector() - Could not write sector %ld"
 628+ " (error %d)\n",
 629+ secnum, rc);
 630+ }
 631+#ifdef HAVE_MULTIVOLUME
 632+ if(fce->fat_vol->bpb_numfats > 1)
 633+#else
 634+ if(fat_bpbs[0].bpb_numfats > 1)
 635+#endif
 636+ {
 637+ /* Write to the second FAT */
 638+#ifdef HAVE_MULTIVOLUME
 639+ secnum += fce->fat_vol->fatsize;
 640+#else
 641+ secnum += fat_bpbs[0].fatsize;
 642+#endif
 643+ rc = storage_write_sectors(IF_MD2(fce->fat_vol->drive,)
 644+ secnum, 1, sectorbuf);
 645+ if(rc < 0)
 646+ {
 647+ panicf("flush_fat_sector() - Could not write sector %ld"
 648+ " (error %d)\n",
 649+ secnum, rc);
 650+ }
 651+ }
 652+ fce->dirty = false;
 653+}
 654+
 655+/* Note: The returned pointer is only safely valid until the next
 656+ task switch! (Any subsequent ata read/write may yield.) */
 657+static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,)
 658+ long fatsector, bool dirty)
 659+{
 660+#ifndef HAVE_MULTIVOLUME
 661+ struct bpb* fat_bpb = &fat_bpbs[0];
 662+#endif
 663+ long secnum = fatsector + fat_bpb->bpb_rsvdseccnt;
 664+ int cache_index = secnum & FAT_CACHE_MASK;
 665+ struct fat_cache_entry *fce = &fat_cache[cache_index];
 666+ unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0];
 667+ int rc;
 668+
 669+ mutex_lock(&cache_mutex); /* make changes atomic */
 670+
 671+ /* Delete the cache entry if it isn't the sector we want */
 672+ if(fce->inuse && (fce->secnum != secnum
 673+#ifdef HAVE_MULTIVOLUME
 674+ || fce->fat_vol != fat_bpb
 675+#endif
 676+ ))
 677+ {
 678+ /* Write back if it is dirty */
 679+ if(fce->dirty)
 680+ {
 681+ flush_fat_sector(fce, sectorbuf);
 682+ }
 683+ fce->inuse = false;
 684+ }
 685+
 686+ /* Load the sector if it is not cached */
 687+ if(!fce->inuse)
 688+ {
 689+ rc = storage_read_sectors(IF_MD2(fat_bpb->drive,)
 690+ secnum + fat_bpb->startsector,1,
 691+ sectorbuf);
 692+ if(rc < 0)
 693+ {
 694+ DEBUGF( "cache_fat_sector() - Could not read sector %ld"
 695+ " (error %d)\n", secnum, rc);
 696+ mutex_unlock(&cache_mutex);
 697+ return NULL;
 698+ }
 699+ fce->inuse = true;
 700+ fce->secnum = secnum;
 701+#ifdef HAVE_MULTIVOLUME
 702+ fce->fat_vol = fat_bpb;
 703+#endif
 704+ }
 705+ if (dirty)
 706+ fce->dirty = true; /* dirt remains, sticky until flushed */
 707+ mutex_unlock(&cache_mutex);
 708+ return sectorbuf;
 709+}
 710+
 711+static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,)
 712+ unsigned long startcluster)
 713+{
 714+#ifndef HAVE_MULTIVOLUME
 715+ struct bpb* fat_bpb = &fat_bpbs[0];
 716+#endif
 717+ unsigned long sector;
 718+ unsigned long offset;
 719+ unsigned long i;
 720+
 721+#ifdef HAVE_FAT16SUPPORT
 722+ if (fat_bpb->is_fat16)
 723+ {
 724+ sector = startcluster / CLUSTERS_PER_FAT16_SECTOR;
 725+ offset = startcluster % CLUSTERS_PER_FAT16_SECTOR;
 726+
 727+ for (i = 0; i<fat_bpb->fatsize; i++) {
 728+ unsigned int j;
 729+ unsigned int nr = (i + sector) % fat_bpb->fatsize;
 730+ unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false);
 731+ if ( !fat )
 732+ break;
 733+ for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) {
 734+ int k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR;
 735+ if (letoh16(fat[k]) == 0x0000) {
 736+ unsigned int c = nr * CLUSTERS_PER_FAT16_SECTOR + k;
 737+ /* Ignore the reserved clusters 0 & 1, and also
 738+ cluster numbers out of bounds */
 739+ if ( c < 2 || c > fat_bpb->dataclusters+1 )
 740+ continue;
 741+ LDEBUGF("find_free_cluster(%x) == %x\n",startcluster,c);
 742+ fat_bpb->fsinfo.nextfree = c;
 743+ return c;
 744+ }
 745+ }
 746+ offset = 0;
 747+ }
 748+ }
 749+ else
 750+#endif /* #ifdef HAVE_FAT16SUPPORT */
 751+ {
 752+ sector = startcluster / CLUSTERS_PER_FAT_SECTOR;
 753+ offset = startcluster % CLUSTERS_PER_FAT_SECTOR;
 754+
 755+ for (i = 0; i<fat_bpb->fatsize; i++) {
 756+ unsigned int j;
 757+ unsigned long nr = (i + sector) % fat_bpb->fatsize;
 758+ unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false);
 759+ if ( !fat )
 760+ break;
 761+ for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) {
 762+ int k = (j + offset) % CLUSTERS_PER_FAT_SECTOR;
 763+ if (!(letoh32(fat[k]) & 0x0fffffff)) {
 764+ unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k;
 765+ /* Ignore the reserved clusters 0 & 1, and also
 766+ cluster numbers out of bounds */
 767+ if ( c < 2 || c > fat_bpb->dataclusters+1 )
 768+ continue;
 769+ LDEBUGF("find_free_cluster(%lx) == %lx\n",startcluster,c);
 770+ fat_bpb->fsinfo.nextfree = c;
 771+ return c;
 772+ }
 773+ }
 774+ offset = 0;
 775+ }
 776+ }
 777+
 778+ LDEBUGF("find_free_cluster(%lx) == 0\n",startcluster);
 779+ return 0; /* 0 is an illegal cluster number */
 780+}
 781+
 782+static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry,
 783+ unsigned long val)
 784+{
 785+#ifndef HAVE_MULTIVOLUME
 786+ struct bpb* fat_bpb = &fat_bpbs[0];
 787+#endif
 788+#ifdef HAVE_FAT16SUPPORT
 789+ if (fat_bpb->is_fat16)
 790+ {
 791+ int sector = entry / CLUSTERS_PER_FAT16_SECTOR;
 792+ int offset = entry % CLUSTERS_PER_FAT16_SECTOR;
 793+ unsigned short* sec;
 794+
 795+ val &= 0xFFFF;
 796+
 797+ LDEBUGF("update_fat_entry(%x,%x)\n",entry,val);
 798+
 799+ if (entry==val)
 800+ panicf("Creating FAT loop: %lx,%lx\n",entry,val);
 801+
 802+ if ( entry < 2 )
 803+ panicf("Updating reserved FAT entry %ld.\n",entry);
 804+
 805+ sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true);
 806+ if (!sec)
 807+ {
 808+ DEBUGF( "update_fat_entry() - Could not cache sector %d\n", sector);
 809+ return -1;
 810+ }
 811+
 812+ if ( val ) {
 813+ if (letoh16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0)
 814+ fat_bpb->fsinfo.freecount--;
 815+ }
 816+ else {
 817+ if (letoh16(sec[offset]))
 818+ fat_bpb->fsinfo.freecount++;
 819+ }
 820+
 821+ LDEBUGF("update_fat_entry: %d free clusters\n",
 822+ fat_bpb->fsinfo.freecount);
 823+
 824+ sec[offset] = htole16(val);
 825+ }
 826+ else
 827+#endif /* #ifdef HAVE_FAT16SUPPORT */
 828+ {
 829+ long sector = entry / CLUSTERS_PER_FAT_SECTOR;
 830+ int offset = entry % CLUSTERS_PER_FAT_SECTOR;
 831+ unsigned long* sec;
 832+
 833+ LDEBUGF("update_fat_entry(%lx,%lx)\n",entry,val);
 834+
 835+ if (entry==val)
 836+ panicf("Creating FAT loop: %lx,%lx\n",entry,val);
 837+
 838+ if ( entry < 2 )
 839+ panicf("Updating reserved FAT entry %ld.\n",entry);
 840+
 841+ sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true);
 842+ if (!sec)
 843+ {
 844+ DEBUGF("update_fat_entry() - Could not cache sector %ld\n", sector);
 845+ return -1;
 846+ }
 847+
 848+ if ( val ) {
 849+ if (!(letoh32(sec[offset]) & 0x0fffffff) &&
 850+ fat_bpb->fsinfo.freecount > 0)
 851+ fat_bpb->fsinfo.freecount--;
 852+ }
 853+ else {
 854+ if (letoh32(sec[offset]) & 0x0fffffff)
 855+ fat_bpb->fsinfo.freecount++;
 856+ }
 857+
 858+ LDEBUGF("update_fat_entry: %ld free clusters\n",
 859+ fat_bpb->fsinfo.freecount);
 860+
 861+ /* don't change top 4 bits */
 862+ sec[offset] &= htole32(0xf0000000);
 863+ sec[offset] |= htole32(val & 0x0fffffff);
 864+ }
 865+
 866+ return 0;
 867+}
 868+
 869+static long read_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry)
 870+{
 871+#ifdef HAVE_FAT16SUPPORT
 872+#ifndef HAVE_MULTIVOLUME
 873+ struct bpb* fat_bpb = &fat_bpbs[0];
 874+#endif
 875+ if (fat_bpb->is_fat16)
 876+ {
 877+ int sector = entry / CLUSTERS_PER_FAT16_SECTOR;
 878+ int offset = entry % CLUSTERS_PER_FAT16_SECTOR;
 879+ unsigned short* sec;
 880+
 881+ sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false);
 882+ if (!sec)
 883+ {
 884+ DEBUGF( "read_fat_entry() - Could not cache sector %d\n", sector);
 885+ return -1;
 886+ }
 887+
 888+ return letoh16(sec[offset]);
 889+ }
 890+ else
 891+#endif /* #ifdef HAVE_FAT16SUPPORT */
 892+ {
 893+ long sector = entry / CLUSTERS_PER_FAT_SECTOR;
 894+ int offset = entry % CLUSTERS_PER_FAT_SECTOR;
 895+ unsigned long* sec;
 896+
 897+ sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false);
 898+ if (!sec)
 899+ {
 900+ DEBUGF( "read_fat_entry() - Could not cache sector %ld\n", sector);
 901+ return -1;
 902+ }
 903+
 904+ return letoh32(sec[offset]) & 0x0fffffff;
 905+ }
 906+}
 907+
 908+static long get_next_cluster(IF_MV2(struct bpb* fat_bpb,) long cluster)
 909+{
 910+ long next_cluster;
 911+ long eof_mark = FAT_EOF_MARK;
 912+
 913+#ifdef HAVE_FAT16SUPPORT
 914+#ifndef HAVE_MULTIVOLUME
 915+ struct bpb* fat_bpb = &fat_bpbs[0];
 916+#endif
 917+ if (fat_bpb->is_fat16)
 918+ {
 919+ eof_mark &= 0xFFFF; /* only 16 bit */
 920+ if (cluster < 0) /* FAT16 root dir */
 921+ return cluster + 1; /* don't use the FAT */
 922+ }
 923+#endif
 924+ next_cluster = read_fat_entry(IF_MV2(fat_bpb,) cluster);
 925+
 926+ /* is this last cluster in chain? */
 927+ if ( next_cluster >= eof_mark )
 928+ return 0;
 929+ else
 930+ return next_cluster;
 931+}
 932+
 933+static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb))
 934+{
 935+#ifndef HAVE_MULTIVOLUME
 936+ struct bpb* fat_bpb = &fat_bpbs[0];
 937+#endif
 938+ unsigned char fsinfo[SECTOR_SIZE];
 939+ unsigned long* intptr;
 940+ int rc;
 941+
 942+#ifdef HAVE_FAT16SUPPORT
 943+ if (fat_bpb->is_fat16)
 944+ return 0; /* FAT16 has no FsInfo */
 945+#endif /* #ifdef HAVE_FAT16SUPPORT */
 946+
 947+ /* update fsinfo */
 948+ rc = storage_read_sectors(IF_MD2(fat_bpb->drive,)
 949+ fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo);
 950+ if (rc < 0)
 951+ {
 952+ DEBUGF( "flush_fat() - Couldn't read FSInfo (error code %d)\n", rc);
 953+ return rc * 10 - 1;
 954+ }
 955+ intptr = (long*)&(fsinfo[FSINFO_FREECOUNT]);
 956+ *intptr = htole32(fat_bpb->fsinfo.freecount);
 957+
 958+ intptr = (long*)&(fsinfo[FSINFO_NEXTFREE]);
 959+ *intptr = htole32(fat_bpb->fsinfo.nextfree);
 960+
 961+ rc = storage_write_sectors(IF_MD2(fat_bpb->drive,)
 962+ fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo);
 963+ if (rc < 0)
 964+ {
 965+ DEBUGF( "flush_fat() - Couldn't write FSInfo (error code %d)\n", rc);
 966+ return rc * 10 - 2;
 967+ }
 968+
 969+ return 0;
 970+}
 971+
 972+static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb))
 973+{
 974+ int i;
 975+ int rc;
 976+ unsigned char *sec;
 977+ LDEBUGF("flush_fat()\n");
 978+
 979+ mutex_lock(&cache_mutex);
 980+ for(i = 0;i < FAT_CACHE_SIZE;i++)
 981+ {
 982+ struct fat_cache_entry *fce = &fat_cache[i];
 983+ if(fce->inuse
 984+#ifdef HAVE_MULTIVOLUME
 985+ && fce->fat_vol == fat_bpb
 986+#endif
 987+ && fce->dirty)
 988+ {
 989+ sec = fat_cache_sectors[i];
 990+ flush_fat_sector(fce, sec);
 991+ }
 992+ }
 993+ mutex_unlock(&cache_mutex);
 994+
 995+ rc = update_fsinfo(IF_MV(fat_bpb));
 996+ if (rc < 0)
 997+ return rc * 10 - 3;
 998+
 999+ return 0;
 1000+}
 1001+
 1002+static void fat_time(unsigned short* date,
 1003+ unsigned short* time,
 1004+ unsigned short* tenth )
 1005+{
 1006+#if CONFIG_RTC
 1007+ struct tm* tm = get_time();
 1008+
 1009+ if (date)
 1010+ *date = ((tm->tm_year - 80) << 9) |
 1011+ ((tm->tm_mon + 1) << 5) |
 1012+ tm->tm_mday;
 1013+
 1014+ if (time)
 1015+ *time = (tm->tm_hour << 11) |
 1016+ (tm->tm_min << 5) |
 1017+ (tm->tm_sec >> 1);
 1018+
 1019+ if (tenth)
 1020+ *tenth = (tm->tm_sec & 1) * 100;
 1021+#else
 1022+ /* non-RTC version returns an increment from the supplied time, or a
 1023+ * fixed standard time/date if no time given as input */
 1024+
 1025+/* Macros to convert a 2-digit string to a decimal constant.
 1026+ (YEAR), MONTH and DAY are set by the date command, which outputs
 1027+ DAY as 00..31 and MONTH as 01..12. The leading zero would lead to
 1028+ misinterpretation as an octal constant. */
 1029+#define S100(x) 1 ## x
 1030+#define C2DIG2DEC(x) (S100(x)-100)
 1031+/* The actual build date, as FAT date constant */
 1032+#define BUILD_DATE_FAT (((YEAR - 1980) << 9) \
 1033+ | (C2DIG2DEC(MONTH) << 5) \
 1034+ | C2DIG2DEC(DAY))
 1035+
 1036+ bool date_forced = false;
 1037+ bool next_day = false;
 1038+ unsigned time2 = 0; /* double time, for CRTTIME with 1s precision */
 1039+
 1040+ if (date && *date < BUILD_DATE_FAT)
 1041+ {
 1042+ *date = BUILD_DATE_FAT;
 1043+ date_forced = true;
 1044+ }
 1045+
 1046+ if (time)
 1047+ {
 1048+ time2 = *time << 1;
 1049+ if (time2 == 0 || date_forced)
 1050+ {
 1051+ time2 = (11 < 6) | 11; /* set to 00:11:11 */
 1052+ }
 1053+ else
 1054+ {
 1055+ unsigned mins = (time2 >> 6) & 0x3f;
 1056+ unsigned hours = (time2 >> 12) & 0x1f;
 1057+
 1058+ mins = 11 * ((mins/11) + 1); /* advance to next multiple of 11 */
 1059+ if (mins > 59)
 1060+ {
 1061+ mins = 11; /* 00 would be a bad marker */
 1062+ if (++hours > 23)
 1063+ {
 1064+ hours = 0;
 1065+ next_day = true;
 1066+ }
 1067+ }
 1068+ time2 = (hours << 12) | (mins << 6) | mins; /* secs = mins */
 1069+ }
 1070+ *time = time2 >> 1;
 1071+ }
 1072+
 1073+ if (tenth)
 1074+ *tenth = (time2 & 1) * 100;
 1075+
 1076+ if (date && next_day)
 1077+ {
 1078+ static const unsigned char daysinmonth[] =
 1079+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 1080+ unsigned day = *date & 0x1f;
 1081+ unsigned month = (*date >> 5) & 0x0f;
 1082+ unsigned year = (*date >> 9) & 0x7f;
 1083+
 1084+ /* simplification: ignore leap years */
 1085+ if (++day > daysinmonth[month-1])
 1086+ {
 1087+ day = 1;
 1088+ if (++month > 12)
 1089+ {
 1090+ month = 1;
 1091+ year++;
 1092+ }
 1093+ }
 1094+ *date = (year << 9) | (month << 5) | day;
 1095+ }
 1096+
 1097+#endif /* CONFIG_RTC */
 1098+}
 1099+
 1100+static int write_long_name(struct fat_file* file,
 1101+ unsigned int firstentry,
 1102+ unsigned int numentries,
 1103+ const unsigned char* name,
 1104+ const unsigned char* shortname,
 1105+ bool is_directory)
 1106+{
 1107+ unsigned char buf[SECTOR_SIZE];
 1108+ unsigned char* entry;
 1109+ unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR;
 1110+ unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR;
 1111+ unsigned char chksum = 0;
 1112+ unsigned int i, j=0;
 1113+ unsigned int nameidx=0, namelen = utf8length(name);
 1114+ int rc;
 1115+ unsigned short name_utf16[namelen + 1];
 1116+
 1117+ LDEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)\n",
 1118+ file->firstcluster, firstentry, numentries, name);
 1119+
 1120+ rc = fat_seek(file, sector);
 1121+ if (rc<0)
 1122+ return rc * 10 - 1;
 1123+
 1124+ rc = fat_readwrite(file, 1, buf, false);
 1125+ if (rc<1)
 1126+ return rc * 10 - 2;
 1127+
 1128+ /* calculate shortname checksum */
 1129+ for (i=11; i>0; i--)
 1130+ chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++];
 1131+
 1132+ /* calc position of last name segment */
 1133+ if ( namelen > NAME_BYTES_PER_ENTRY )
 1134+ for (nameidx=0;
 1135+ nameidx < (namelen - NAME_BYTES_PER_ENTRY);
 1136+ nameidx += NAME_BYTES_PER_ENTRY);
 1137+
 1138+ /* we need to convert the name first */
 1139+ /* since it is written in reverse order */
 1140+ for (i = 0; i <= namelen; i++)
 1141+ name = utf8decode(name, &name_utf16[i]);
 1142+
 1143+ for (i=0; i < numentries; i++) {
 1144+ /* new sector? */
 1145+ if ( idx >= DIR_ENTRIES_PER_SECTOR ) {
 1146+ /* update current sector */
 1147+ rc = fat_seek(file, sector);
 1148+ if (rc<0)
 1149+ return rc * 10 - 3;
 1150+
 1151+ rc = fat_readwrite(file, 1, buf, true);
 1152+ if (rc<1)
 1153+ return rc * 10 - 4;
 1154+
 1155+ /* read next sector */
 1156+ rc = fat_readwrite(file, 1, buf, false);
 1157+ if (rc<0) {
 1158+ LDEBUGF("Failed writing new sector: %d\n",rc);
 1159+ return rc * 10 - 5;
 1160+ }
 1161+ if (rc==0)
 1162+ /* end of dir */
 1163+ memset(buf, 0, sizeof buf);
 1164+
 1165+ sector++;
 1166+ idx = 0;
 1167+ }
 1168+
 1169+ entry = buf + idx * DIR_ENTRY_SIZE;
 1170+
 1171+ /* verify this entry is free */
 1172+ if (entry[0] && entry[0] != 0xe5 )
 1173+ panicf("Dir entry %d in sector %x is not free! "
 1174+ "%02x %02x %02x %02x",
 1175+ idx, sector,
 1176+ entry[0], entry[1], entry[2], entry[3]);
 1177+
 1178+ memset(entry, 0, DIR_ENTRY_SIZE);
 1179+ if ( i+1 < numentries ) {
 1180+ /* longname entry */
 1181+ unsigned int k, l = nameidx;
 1182+
 1183+ entry[FATLONG_ORDER] = numentries-i-1;
 1184+ if (i==0) {
 1185+ /* mark this as last long entry */
 1186+ entry[FATLONG_ORDER] |= FATLONG_LAST_LONG_ENTRY;
 1187+
 1188+ /* pad name with 0xffff */
 1189+ for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
 1190+ for (k=14; k<26; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
 1191+ for (k=28; k<32; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
 1192+ };
 1193+ /* set name */
 1194+ for (k=0; k<5 && l <= namelen; k++) {
 1195+ entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff);
 1196+ entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8);
 1197+ }
 1198+ for (k=0; k<6 && l <= namelen; k++) {
 1199+ entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff);
 1200+ entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8);
 1201+ }
 1202+ for (k=0; k<2 && l <= namelen; k++) {
 1203+ entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff);
 1204+ entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8);
 1205+ }
 1206+
 1207+ entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME;
 1208+ entry[FATDIR_FSTCLUSLO] = 0;
 1209+ entry[FATLONG_TYPE] = 0;
 1210+ entry[FATLONG_CHKSUM] = chksum;
 1211+ LDEBUGF("Longname entry %d: %s\n", idx, name+nameidx);
 1212+ }
 1213+ else {
 1214+ /* shortname entry */
 1215+ unsigned short date=0, time=0, tenth=0;
 1216+ LDEBUGF("Shortname entry: %s\n", shortname);
 1217+ memcpy(entry + FATDIR_NAME, shortname, 11);
 1218+ entry[FATDIR_ATTR] = is_directory?FAT_ATTR_DIRECTORY:0;
 1219+ entry[FATDIR_NTRES] = 0;
 1220+
 1221+ fat_time(&date, &time, &tenth);
 1222+ entry[FATDIR_CRTTIMETENTH] = tenth;
 1223+ *(unsigned short*)(entry + FATDIR_CRTTIME) = htole16(time);
 1224+ *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time);
 1225+ *(unsigned short*)(entry + FATDIR_CRTDATE) = htole16(date);
 1226+ *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date);
 1227+ *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date);
 1228+ }
 1229+ idx++;
 1230+ nameidx -= NAME_BYTES_PER_ENTRY;
 1231+ }
 1232+
 1233+ /* update last sector */
 1234+ rc = fat_seek(file, sector);
 1235+ if (rc<0)
 1236+ return rc * 10 - 6;
 1237+
 1238+ rc = fat_readwrite(file, 1, buf, true);
 1239+ if (rc<1)
 1240+ return rc * 10 - 7;
 1241+
 1242+ return 0;
 1243+}
 1244+
 1245+static int fat_checkname(const unsigned char* newname)
 1246+{
 1247+ static const char invalid_chars[] = "\"*/:<>?\\|";
 1248+ int len = strlen(newname);
 1249+ /* More sanity checks are probably needed */
 1250+ if (len > 255 || newname[len - 1] == '.')
 1251+ {
 1252+ return -1;
 1253+ }
 1254+ while (*newname)
 1255+ {
 1256+ if (*newname < ' ' || strchr(invalid_chars, *newname) != NULL)
 1257+ return -1;
 1258+ newname++;
 1259+ }
 1260+ /* check trailing space(s) */
 1261+ if(*(--newname) == ' ')
 1262+ return -1;
 1263+
 1264+ return 0;
 1265+}
 1266+
 1267+static int add_dir_entry(struct fat_dir* dir,
 1268+ struct fat_file* file,
 1269+ const char* name,
 1270+ bool is_directory,
 1271+ bool dotdir)
 1272+{
 1273+#ifdef HAVE_MULTIVOLUME
 1274+ struct bpb* fat_bpb = &fat_bpbs[dir->file.volume];
 1275+#else
 1276+ struct bpb* fat_bpb = &fat_bpbs[0];
 1277+#endif
 1278+ unsigned char buf[SECTOR_SIZE];
 1279+ unsigned char shortname[12];
 1280+ int rc;
 1281+ unsigned int sector;
 1282+ bool done = false;
 1283+ int entries_needed, entries_found = 0;
 1284+ int firstentry;
 1285+
 1286+ LDEBUGF( "add_dir_entry(%s,%lx)\n",
 1287+ name, file->firstcluster);
 1288+
 1289+ /* Don't check dotdirs name for validity */
 1290+ if (dotdir == false) {
 1291+ rc = fat_checkname(name);
 1292+ if (rc < 0) {
 1293+ /* filename is invalid */
 1294+ return rc * 10 - 1;
 1295+ }
 1296+ }
 1297+
 1298+#ifdef HAVE_MULTIVOLUME
 1299+ file->volume = dir->file.volume; /* inherit the volume, to make sure */
 1300+#endif
 1301+
 1302+ /* The "." and ".." directory entries must not be long names */
 1303+ if(dotdir) {
 1304+ int i;
 1305+ strlcpy(shortname, name, 12);
 1306+ for(i = strlen(shortname); i < 12; i++)
 1307+ shortname[i] = ' ';
 1308+
 1309+ entries_needed = 1;
 1310+ } else {
 1311+ create_dos_name(name, shortname);
 1312+
 1313+ /* one dir entry needed for every 13 bytes of filename,
 1314+ plus one entry for the short name */
 1315+ entries_needed = (utf8length(name) + (NAME_BYTES_PER_ENTRY-1))
 1316+ / NAME_BYTES_PER_ENTRY + 1;
 1317+ }
 1318+
 1319+ restart:
 1320+ firstentry = -1;
 1321+
 1322+ rc = fat_seek(&dir->file, 0);
 1323+ if (rc < 0)
 1324+ return rc * 10 - 2;
 1325+
 1326+ /* step 1: search for free entries and check for duplicate shortname */
 1327+ for (sector = 0; !done; sector++)
 1328+ {
 1329+ unsigned int i;
 1330+
 1331+ rc = fat_readwrite(&dir->file, 1, buf, false);
 1332+ if (rc < 0) {
 1333+ DEBUGF( "add_dir_entry() - Couldn't read dir"
 1334+ " (error code %d)\n", rc);
 1335+ return rc * 10 - 3;
 1336+ }
 1337+
 1338+ if (rc == 0) { /* current end of dir reached */
 1339+ LDEBUGF("End of dir on cluster boundary\n");
 1340+ break;
 1341+ }
 1342+
 1343+ /* look for free slots */
 1344+ for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++)
 1345+ {
 1346+ switch (buf[i * DIR_ENTRY_SIZE]) {
 1347+ case 0:
 1348+ entries_found += DIR_ENTRIES_PER_SECTOR - i;
 1349+ LDEBUGF("Found end of dir %d\n",
 1350+ sector * DIR_ENTRIES_PER_SECTOR + i);
 1351+ i = DIR_ENTRIES_PER_SECTOR - 1;
 1352+ done = true;
 1353+ break;
 1354+
 1355+ case 0xe5:
 1356+ entries_found++;
 1357+ LDEBUGF("Found free entry %d (%d/%d)\n",
 1358+ sector * DIR_ENTRIES_PER_SECTOR + i,
 1359+ entries_found, entries_needed);
 1360+ break;
 1361+
 1362+ default:
 1363+ entries_found = 0;
 1364+
 1365+ /* check that our intended shortname doesn't already exist */
 1366+ if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 11)) {
 1367+ /* shortname exists already, make a new one */
 1368+ randomize_dos_name(shortname);
 1369+ LDEBUGF("Duplicate shortname, changing to %s\n",
 1370+ shortname);
 1371+
 1372+ /* name has changed, we need to restart search */
 1373+ goto restart;
 1374+ }
 1375+ break;
 1376+ }
 1377+ if (firstentry < 0 && (entries_found >= entries_needed))
 1378+ firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1
 1379+ - entries_found;
 1380+ }
 1381+ }
 1382+
 1383+ /* step 2: extend the dir if necessary */
 1384+ if (firstentry < 0)
 1385+ {
 1386+ LDEBUGF("Adding new sector(s) to dir\n");
 1387+ rc = fat_seek(&dir->file, sector);
 1388+ if (rc < 0)
 1389+ return rc * 10 - 4;
 1390+ memset(buf, 0, sizeof buf);
 1391+
 1392+ /* we must clear whole clusters */
 1393+ for (; (entries_found < entries_needed) ||
 1394+ (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); sector++)
 1395+ {
 1396+ if (sector >= (65536/DIR_ENTRIES_PER_SECTOR))
 1397+ return -5; /* dir too large -- FAT specification */
 1398+
 1399+ rc = fat_readwrite(&dir->file, 1, buf, true);
 1400+ if (rc < 1) /* No more room or something went wrong */
 1401+ return rc * 10 - 6;
 1402+
 1403+ entries_found += DIR_ENTRIES_PER_SECTOR;
 1404+ }
 1405+
 1406+ firstentry = sector * DIR_ENTRIES_PER_SECTOR - entries_found;
 1407+ }
 1408+
 1409+ /* step 3: add entry */
 1410+ sector = firstentry / DIR_ENTRIES_PER_SECTOR;
 1411+ LDEBUGF("Adding longname to entry %d in sector %d\n",
 1412+ firstentry, sector);
 1413+
 1414+ rc = write_long_name(&dir->file, firstentry,
 1415+ entries_needed, name, shortname, is_directory);
 1416+ if (rc < 0)
 1417+ return rc * 10 - 7;
 1418+
 1419+ /* remember where the shortname dir entry is located */
 1420+ file->direntry = firstentry + entries_needed - 1;
 1421+ file->direntries = entries_needed;
 1422+ file->dircluster = dir->file.firstcluster;
 1423+ LDEBUGF("Added new dir entry %d, using %d slots.\n",
 1424+ file->direntry, file->direntries);
 1425+
 1426+ return 0;
 1427+}
 1428+
 1429+static unsigned char char2dos(unsigned char c, int* randomize)
 1430+{
 1431+ static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|";
 1432+
 1433+ if (c <= 0x20)
 1434+ c = 0; /* Illegal char, remove */
 1435+ else if (strchr(invalid_chars, c) != NULL)
 1436+ {
 1437+ /* Illegal char, replace */
 1438+ c = '_';
 1439+ *randomize = 1; /* as per FAT spec */
 1440+ }
 1441+ else
 1442+ c = toupper(c);
 1443+
 1444+ return c;
 1445+}
 1446+
 1447+static void create_dos_name(const unsigned char *name, unsigned char *newname)
 1448+{
 1449+ int i;
 1450+ unsigned char *ext;
 1451+ int randomize = 0;
 1452+
 1453+ /* Find extension part */
 1454+ ext = strrchr(name, '.');
 1455+ if (ext == name) /* handle .dotnames */
 1456+ ext = NULL;
 1457+
 1458+ /* needs to randomize? */
 1459+ if((ext && (strlen(ext) > 4)) ||
 1460+ ((ext ? (unsigned int)(ext-name) : strlen(name)) > 8) )
 1461+ randomize = 1;
 1462+
 1463+ /* Name part */
 1464+ for (i = 0; *name && (!ext || name < ext) && (i < 8); name++)
 1465+ {
 1466+ unsigned char c = char2dos(*name, &randomize);
 1467+ if (c)
 1468+ newname[i++] = c;
 1469+ }
 1470+
 1471+ /* Pad both name and extension */
 1472+ while (i < 11)
 1473+ newname[i++] = ' ';
 1474+
 1475+ if (newname[0] == 0xe5) /* Special kanji character */
 1476+ newname[0] = 0x05;
 1477+
 1478+ if (ext)
 1479+ { /* Extension part */
 1480+ ext++;
 1481+ for (i = 8; *ext && (i < 11); ext++)
 1482+ {
 1483+ unsigned char c = char2dos(*ext, &randomize);
 1484+ if (c)
 1485+ newname[i++] = c;
 1486+ }
 1487+ }
 1488+
 1489+ if(randomize)
 1490+ randomize_dos_name(newname);
 1491+}
 1492+
 1493+static void randomize_dos_name(unsigned char *name)
 1494+{
 1495+ unsigned char* tilde = NULL; /* ~ location */
 1496+ unsigned char* lastpt = NULL; /* last point of filename */
 1497+ unsigned char* nameptr = name; /* working copy of name pointer */
 1498+ unsigned char num[9]; /* holds number as string */
 1499+ int i = 0;
 1500+ int cnt = 1;
 1501+ int numlen;
 1502+ int offset;
 1503+
 1504+ while(i++ < 8)
 1505+ {
 1506+ /* hunt for ~ and where to put it */
 1507+ if((!tilde) && (*nameptr == '~'))
 1508+ tilde = nameptr;
 1509+ if((!lastpt) && ((*nameptr == ' ' || *nameptr == '~')))
 1510+ lastpt = nameptr;
 1511+ nameptr++;
 1512+ }
 1513+ if(tilde)
 1514+ {
 1515+ /* extract current count and increment */
 1516+ memcpy(num,tilde+1,7-(unsigned int)(tilde-name));
 1517+ num[7-(unsigned int)(tilde-name)] = 0;
 1518+ cnt = atoi(num) + 1;
 1519+ }
 1520+ cnt %= 10000000; /* protection */
 1521+ snprintf(num, 9, "~%d", cnt); /* allow room for trailing zero */
 1522+ numlen = strlen(num); /* required space */
 1523+ offset = (unsigned int)(lastpt ? lastpt - name : 8); /* prev startpoint */
 1524+ if(offset > (8-numlen)) offset = 8-numlen; /* correct for new numlen */
 1525+
 1526+ memcpy(&name[offset], num, numlen);
 1527+
 1528+ /* in special case of counter overflow: pad with spaces */
 1529+ for(offset = offset+numlen; offset < 8; offset++)
 1530+ name[offset] = ' ';
 1531+}
 1532+
 1533+static int update_short_entry( struct fat_file* file, long size, int attr )
 1534+{
 1535+ unsigned char buf[SECTOR_SIZE];
 1536+ int sector = file->direntry / DIR_ENTRIES_PER_SECTOR;
 1537+ unsigned char* entry =
 1538+ buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR);
 1539+ unsigned long* sizeptr;
 1540+ unsigned short* clusptr;
 1541+ struct fat_file dir;
 1542+ int rc;
 1543+
 1544+ LDEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)\n",
 1545+ file->firstcluster, file->direntry, size);
 1546+
 1547+ /* create a temporary file handle for the dir holding this file */
 1548+ rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL);
 1549+ if (rc < 0)
 1550+ return rc * 10 - 1;
 1551+
 1552+ rc = fat_seek( &dir, sector );
 1553+ if (rc<0)
 1554+ return rc * 10 - 2;
 1555+
 1556+ rc = fat_readwrite(&dir, 1, buf, false);
 1557+ if (rc < 1)
 1558+ return rc * 10 - 3;
 1559+
 1560+ if (!entry[0] || entry[0] == 0xe5)
 1561+ panicf("Updating size on empty dir entry %d\n", file->direntry);
 1562+
 1563+ entry[FATDIR_ATTR] = attr & 0xFF;
 1564+
 1565+ clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
 1566+ *clusptr = htole16(file->firstcluster >> 16);
 1567+
 1568+ clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
 1569+ *clusptr = htole16(file->firstcluster & 0xffff);
 1570+
 1571+ sizeptr = (long*)(entry + FATDIR_FILESIZE);
 1572+ *sizeptr = htole32(size);
 1573+
 1574+ {
 1575+#if CONFIG_RTC
 1576+ unsigned short time = 0;
 1577+ unsigned short date = 0;
 1578+#else
 1579+ /* get old time to increment from */
 1580+ unsigned short time = htole16(*(unsigned short*)(entry+FATDIR_WRTTIME));
 1581+ unsigned short date = htole16(*(unsigned short*)(entry+FATDIR_WRTDATE));
 1582+#endif
 1583+ fat_time(&date, &time, NULL);
 1584+ *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time);
 1585+ *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date);
 1586+ *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date);
 1587+ }
 1588+
 1589+ rc = fat_seek( &dir, sector );
 1590+ if (rc < 0)
 1591+ return rc * 10 - 4;
 1592+
 1593+ rc = fat_readwrite(&dir, 1, buf, true);
 1594+ if (rc < 1)
 1595+ return rc * 10 - 5;
 1596+
 1597+ return 0;
 1598+}
 1599+
 1600+static int parse_direntry(struct fat_direntry *de, const unsigned char *buf)
 1601+{
 1602+ int i=0,j=0;
 1603+ unsigned char c;
 1604+ bool lowercase;
 1605+
 1606+ memset(de, 0, sizeof(struct fat_direntry));
 1607+ de->attr = buf[FATDIR_ATTR];
 1608+ de->crttimetenth = buf[FATDIR_CRTTIMETENTH];
 1609+ de->crtdate = BYTES2INT16(buf,FATDIR_CRTDATE);
 1610+ de->crttime = BYTES2INT16(buf,FATDIR_CRTTIME);
 1611+ de->wrtdate = BYTES2INT16(buf,FATDIR_WRTDATE);
 1612+ de->wrttime = BYTES2INT16(buf,FATDIR_WRTTIME);
 1613+ de->filesize = BYTES2INT32(buf,FATDIR_FILESIZE);
 1614+ de->firstcluster = ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSLO)) |
 1615+ ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16);
 1616+ /* The double cast is to prevent a sign-extension to be done on CalmRISC16.
 1617+ (the result of the shift is always considered signed) */
 1618+
 1619+ /* fix the name */
 1620+ lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME);
 1621+ c = buf[FATDIR_NAME];
 1622+ if (c == 0x05) /* special kanji char */
 1623+ c = 0xe5;
 1624+ i = 0;
 1625+ while (c != ' ') {
 1626+ de->name[j++] = lowercase ? tolower(c) : c;
 1627+ if (++i >= 8)
 1628+ break;
 1629+ c = buf[FATDIR_NAME+i];
 1630+ }
 1631+ if (buf[FATDIR_NAME+8] != ' ') {
 1632+ lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT);
 1633+ de->name[j++] = '.';
 1634+ for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++)
 1635+ de->name[j++] = lowercase ? tolower(c) : c;
 1636+ }
 1637+ return 1;
 1638+}
 1639+
 1640+int fat_open(IF_MV2(int volume,)
 1641+ long startcluster,
 1642+ struct fat_file *file,
 1643+ const struct fat_dir* dir)
 1644+{
 1645+ /* Remember where the file's dir entry is located
 1646+ * Do it before assigning other fields so that fat_open
 1647+ * can be called with file == &dir->file (see fat_opendir) */
 1648+ if ( dir ) {
 1649+ file->direntry = dir->entry - 1;
 1650+ file->direntries = dir->entrycount;
 1651+ file->dircluster = dir->file.firstcluster;
 1652+ }
 1653+
 1654+ file->firstcluster = startcluster;
 1655+ file->lastcluster = startcluster;
 1656+ file->lastsector = 0;
 1657+ file->clusternum = 0;
 1658+ file->sectornum = 0;
 1659+ file->eof = false;
 1660+#ifdef HAVE_MULTIVOLUME
 1661+ file->volume = volume;
 1662+ /* fixme: remove error check when done */
 1663+ if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted)
 1664+ {
 1665+ LDEBUGF("fat_open() illegal volume %d\n", volume);
 1666+ return -1;
 1667+ }
 1668+#endif
 1669+
 1670+ LDEBUGF("fat_open(%lx), entry %d\n",startcluster,file->direntry);
 1671+ return 0;
 1672+}
 1673+
 1674+int fat_create_file(const char* name,
 1675+ struct fat_file* file,
 1676+ struct fat_dir* dir)
 1677+{
 1678+ int rc;
 1679+
 1680+ LDEBUGF("fat_create_file(\"%s\",%lx,%lx)\n",name,(long)file,(long)dir);
 1681+ rc = add_dir_entry(dir, file, name, false, false);
 1682+ if (!rc) {
 1683+ file->firstcluster = 0;
 1684+ file->lastcluster = 0;
 1685+ file->lastsector = 0;
 1686+ file->clusternum = 0;
 1687+ file->sectornum = 0;
 1688+ file->eof = false;
 1689+ }
 1690+
 1691+ return rc;
 1692+}
 1693+
 1694+int fat_create_dir(const char* name,
 1695+ struct fat_dir* newdir,
 1696+ struct fat_dir* dir)
 1697+{
 1698+#ifdef HAVE_MULTIVOLUME
 1699+ struct bpb* fat_bpb = &fat_bpbs[dir->file.volume];
 1700+#else
 1701+ struct bpb* fat_bpb = &fat_bpbs[0];
 1702+#endif
 1703+ unsigned char buf[SECTOR_SIZE];
 1704+ int i;
 1705+ long sector;
 1706+ int rc;
 1707+ struct fat_file dummyfile;
 1708+
 1709+ LDEBUGF("fat_create_dir(\"%s\",%lx,%lx)\n",name,(long)newdir,(long)dir);
 1710+
 1711+ memset(newdir, 0, sizeof(struct fat_dir));
 1712+ memset(&dummyfile, 0, sizeof(struct fat_file));
 1713+
 1714+ /* First, add the entry in the parent directory */
 1715+ rc = add_dir_entry(dir, &newdir->file, name, true, false);
 1716+ if (rc < 0)
 1717+ return rc * 10 - 1;
 1718+
 1719+ /* Allocate a new cluster for the directory */
 1720+ newdir->file.firstcluster = find_free_cluster(IF_MV2(fat_bpb,)
 1721+ fat_bpb->fsinfo.nextfree);
 1722+ if(newdir->file.firstcluster == 0)
 1723+ return -1;
 1724+
 1725+ update_fat_entry(IF_MV2(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK);
 1726+
 1727+ /* Clear the entire cluster */
 1728+ memset(buf, 0, sizeof buf);
 1729+ sector = cluster2sec(IF_MV2(fat_bpb,) newdir->file.firstcluster);
 1730+ for(i = 0;i < (int)fat_bpb->bpb_secperclus;i++) {
 1731+ rc = transfer(IF_MV2(fat_bpb,) sector + i, 1, buf, true );
 1732+ if (rc < 0)
 1733+ return rc * 10 - 2;
 1734+ }
 1735+
 1736+ /* Then add the "." entry */
 1737+ rc = add_dir_entry(newdir, &dummyfile, ".", true, true);
 1738+ if (rc < 0)
 1739+ return rc * 10 - 3;
 1740+ dummyfile.firstcluster = newdir->file.firstcluster;
 1741+ update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY);
 1742+
 1743+ /* and the ".." entry */
 1744+ rc = add_dir_entry(newdir, &dummyfile, "..", true, true);
 1745+ if (rc < 0)
 1746+ return rc * 10 - 4;
 1747+
 1748+ /* The root cluster is cluster 0 in the ".." entry */
 1749+ if(dir->file.firstcluster == fat_bpb->bpb_rootclus)
 1750+ dummyfile.firstcluster = 0;
 1751+ else
 1752+ dummyfile.firstcluster = dir->file.firstcluster;
 1753+ update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY);
 1754+
 1755+ /* Set the firstcluster field in the direntry */
 1756+ update_short_entry(&newdir->file, 0, FAT_ATTR_DIRECTORY);
 1757+
 1758+ rc = flush_fat(IF_MV(fat_bpb));
 1759+ if (rc < 0)
 1760+ return rc * 10 - 5;
 1761+
 1762+ return rc;
 1763+}
 1764+
 1765+int fat_truncate(const struct fat_file *file)
 1766+{
 1767+ /* truncate trailing clusters */
 1768+ long next;
 1769+ long last = file->lastcluster;
 1770+#ifdef HAVE_MULTIVOLUME
 1771+ struct bpb* fat_bpb = &fat_bpbs[file->volume];
 1772+#endif
 1773+
 1774+ LDEBUGF("fat_truncate(%lx, %lx)\n", file->firstcluster, last);
 1775+
 1776+ for ( last = get_next_cluster(IF_MV2(fat_bpb,) last); last; last = next ) {
 1777+ next = get_next_cluster(IF_MV2(fat_bpb,) last);
 1778+ update_fat_entry(IF_MV2(fat_bpb,) last,0);
 1779+ }
 1780+ if (file->lastcluster)
 1781+ update_fat_entry(IF_MV2(fat_bpb,) file->lastcluster,FAT_EOF_MARK);
 1782+
 1783+ return 0;
 1784+}
 1785+
 1786+int fat_closewrite(struct fat_file *file, long size, int attr)
 1787+{
 1788+ int rc;
 1789+#ifdef HAVE_MULTIVOLUME
 1790+ struct bpb* fat_bpb = &fat_bpbs[file->volume];
 1791+#endif
 1792+ LDEBUGF("fat_closewrite(size=%ld)\n",size);
 1793+
 1794+ if (!size) {
 1795+ /* empty file */
 1796+ if ( file->firstcluster ) {
 1797+ update_fat_entry(IF_MV2(fat_bpb,) file->firstcluster, 0);
 1798+ file->firstcluster = 0;
 1799+ }
 1800+ }
 1801+
 1802+ if (file->dircluster) {
 1803+ rc = update_short_entry(file, size, attr);
 1804+ if (rc < 0)
 1805+ return rc * 10 - 1;
 1806+ }
 1807+
 1808+ flush_fat(IF_MV(fat_bpb));
 1809+
 1810+#ifdef TEST_FAT
 1811+ if ( file->firstcluster ) {
 1812+ /* debug */
 1813+#ifdef HAVE_MULTIVOLUME
 1814+ struct bpb* fat_bpb = &fat_bpbs[file->volume];
 1815+#else
 1816+ struct bpb* fat_bpb = &fat_bpbs[0];
 1817+#endif
 1818+ long count = 0;
 1819+ long len;
 1820+ long next;
 1821+ for ( next = file->firstcluster; next;
 1822+ next = get_next_cluster(IF_MV2(fat_bpb,) next) ) {
 1823+ LDEBUGF("cluster %ld: %lx\n", count, next);
 1824+ count++;
 1825+ }
 1826+ len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE;
 1827+ LDEBUGF("File is %ld clusters (chainlen=%ld, size=%ld)\n",
 1828+ count, len, size );
 1829+ if ( len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE)
 1830+ panicf("Cluster chain is too long\n");
 1831+ if ( len < size )
 1832+ panicf("Cluster chain is too short\n");
 1833+ }
 1834+#endif
 1835+
 1836+ return 0;
 1837+}
 1838+
 1839+static int free_direntries(struct fat_file* file)
 1840+{
 1841+ unsigned char buf[SECTOR_SIZE];
 1842+ struct fat_file dir;
 1843+ int numentries = file->direntries;
 1844+ unsigned int entry = file->direntry - numentries + 1;
 1845+ unsigned int sector = entry / DIR_ENTRIES_PER_SECTOR;
 1846+ int i;
 1847+ int rc;
 1848+
 1849+ /* create a temporary file handle for the dir holding this file */
 1850+ rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL);
 1851+ if (rc < 0)
 1852+ return rc * 10 - 1;
 1853+
 1854+ rc = fat_seek( &dir, sector );
 1855+ if (rc < 0)
 1856+ return rc * 10 - 2;
 1857+
 1858+ rc = fat_readwrite(&dir, 1, buf, false);
 1859+ if (rc < 1)
 1860+ return rc * 10 - 3;
 1861+
 1862+ for (i=0; i < numentries; i++) {
 1863+ LDEBUGF("Clearing dir entry %d (%d/%d)\n",
 1864+ entry, i+1, numentries);
 1865+ buf[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5;
 1866+ entry++;
 1867+
 1868+ if ( (entry % DIR_ENTRIES_PER_SECTOR) == 0 ) {
 1869+ /* flush this sector */
 1870+ rc = fat_seek(&dir, sector);
 1871+ if (rc < 0)
 1872+ return rc * 10 - 4;
 1873+
 1874+ rc = fat_readwrite(&dir, 1, buf, true);
 1875+ if (rc < 1)
 1876+ return rc * 10 - 5;
 1877+
 1878+ if ( i+1 < numentries ) {
 1879+ /* read next sector */
 1880+ rc = fat_readwrite(&dir, 1, buf, false);
 1881+ if (rc < 1)
 1882+ return rc * 10 - 6;
 1883+ }
 1884+ sector++;
 1885+ }
 1886+ }
 1887+
 1888+ if ( entry % DIR_ENTRIES_PER_SECTOR ) {
 1889+ /* flush this sector */
 1890+ rc = fat_seek(&dir, sector);
 1891+ if (rc < 0)
 1892+ return rc * 10 - 7;
 1893+
 1894+ rc = fat_readwrite(&dir, 1, buf, true);
 1895+ if (rc < 1)
 1896+ return rc * 10 - 8;
 1897+ }
 1898+
 1899+ return 0;
 1900+}
 1901+
 1902+int fat_remove(struct fat_file* file)
 1903+{
 1904+ long next, last = file->firstcluster;
 1905+ int rc;
 1906+#ifdef HAVE_MULTIVOLUME
 1907+ struct bpb* fat_bpb = &fat_bpbs[file->volume];
 1908+#endif
 1909+
 1910+ LDEBUGF("fat_remove(%lx)\n",last);
 1911+
 1912+ while ( last ) {
 1913+ next = get_next_cluster(IF_MV2(fat_bpb,) last);
 1914+ update_fat_entry(IF_MV2(fat_bpb,) last,0);
 1915+ last = next;
 1916+ }
 1917+
 1918+ if ( file->dircluster ) {
 1919+ rc = free_direntries(file);
 1920+ if (rc < 0)
 1921+ return rc * 10 - 1;
 1922+ }
 1923+
 1924+ file->firstcluster = 0;
 1925+ file->dircluster = 0;
 1926+
 1927+ rc = flush_fat(IF_MV(fat_bpb));
 1928+ if (rc < 0)
 1929+ return rc * 10 - 2;
 1930+
 1931+ return 0;
 1932+}
 1933+
 1934+int fat_rename(struct fat_file* file,
 1935+ struct fat_dir* dir,
 1936+ const unsigned char* newname,
 1937+ long size,
 1938+ int attr)
 1939+{
 1940+ int rc;
 1941+ struct fat_dir olddir;
 1942+ struct fat_file newfile = *file;
 1943+ unsigned char buf[SECTOR_SIZE];
 1944+ unsigned char* entry = NULL;
 1945+ unsigned short* clusptr = NULL;
 1946+ unsigned int parentcluster;
 1947+#ifdef HAVE_MULTIVOLUME
 1948+ struct bpb* fat_bpb = &fat_bpbs[file->volume];
 1949+
 1950+ if (file->volume != dir->file.volume) {
 1951+ DEBUGF("No rename across volumes!\n");
 1952+ return -1;
 1953+ }
 1954+#else
 1955+ struct bpb* fat_bpb = &fat_bpbs[0];
 1956+#endif
 1957+
 1958+ if ( !file->dircluster ) {
 1959+ DEBUGF("File has no dir cluster!\n");
 1960+ return -2;
 1961+ }
 1962+
 1963+ /* create a temporary file handle */
 1964+ rc = fat_opendir(IF_MV2(file->volume,) &olddir, file->dircluster, NULL);
 1965+ if (rc < 0)
 1966+ return rc * 10 - 1;
 1967+
 1968+ /* create new name */
 1969+ rc = add_dir_entry(dir, &newfile, newname, false, false);
 1970+ if (rc < 0)
 1971+ return rc * 10 - 2;
 1972+
 1973+ /* write size and cluster link */
 1974+ rc = update_short_entry(&newfile, size, attr);
 1975+ if (rc < 0)
 1976+ return rc * 10 - 3;
 1977+
 1978+ /* remove old name */
 1979+ rc = free_direntries(file);
 1980+ if (rc < 0)
 1981+ return rc * 10 - 4;
 1982+
 1983+ rc = flush_fat(IF_MV(fat_bpb));
 1984+ if (rc < 0)
 1985+ return rc * 10 - 5;
 1986+
 1987+ /* if renaming a directory, update the .. entry to make sure
 1988+ it points to its parent directory (we don't check if it was a move) */
 1989+ if(FAT_ATTR_DIRECTORY == attr) {
 1990+ /* open the dir that was renamed, we re-use the olddir struct */
 1991+ rc = fat_opendir(IF_MV2(file->volume,) &olddir, newfile.firstcluster,
 1992+ NULL);
 1993+ if (rc < 0)
 1994+ return rc * 10 - 6;
 1995+
 1996+ /* get the first sector of the dir */
 1997+ rc = fat_seek(&olddir.file, 0);
 1998+ if (rc < 0)
 1999+ return rc * 10 - 7;
 2000+
 2001+ rc = fat_readwrite(&olddir.file, 1, buf, false);
 2002+ if (rc < 0)
 2003+ return rc * 10 - 8;
 2004+
 2005+ /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */
 2006+ if(dir->file.firstcluster == fat_bpb->bpb_rootclus)
 2007+ parentcluster = 0;
 2008+ else
 2009+ parentcluster = dir->file.firstcluster;
 2010+
 2011+ entry = buf + DIR_ENTRY_SIZE;
 2012+ if(strncmp(".. ", entry, 11))
 2013+ {
 2014+ /* .. entry must be second entry according to FAT spec (p.29) */
 2015+ DEBUGF("Second dir entry is not double-dot!\n");
 2016+ return rc * 10 - 9;
 2017+ }
 2018+ clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
 2019+ *clusptr = htole16(parentcluster >> 16);
 2020+
 2021+ clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
 2022+ *clusptr = htole16(parentcluster & 0xffff);
 2023+
 2024+ /* write back this sector */
 2025+ rc = fat_seek(&olddir.file, 0);
 2026+ if (rc < 0)
 2027+ return rc * 10 - 7;
 2028+
 2029+ rc = fat_readwrite(&olddir.file, 1, buf, true);
 2030+ if (rc < 1)
 2031+ return rc * 10 - 8;
 2032+ }
 2033+
 2034+ return 0;
 2035+}
 2036+
 2037+static long next_write_cluster(struct fat_file* file,
 2038+ long oldcluster,
 2039+ long* newsector)
 2040+{
 2041+#ifdef HAVE_MULTIVOLUME
 2042+ struct bpb* fat_bpb = &fat_bpbs[file->volume];
 2043+#else
 2044+ struct bpb* fat_bpb = &fat_bpbs[0];
 2045+#endif
 2046+ long cluster = 0;
 2047+ long sector;
 2048+
 2049+ LDEBUGF("next_write_cluster(%lx,%lx)\n",file->firstcluster, oldcluster);
 2050+
 2051+ if (oldcluster)
 2052+ cluster = get_next_cluster(IF_MV2(fat_bpb,) oldcluster);
 2053+
 2054+ if (!cluster) {
 2055+ if (oldcluster > 0)
 2056+ cluster = find_free_cluster(IF_MV2(fat_bpb,) oldcluster+1);
 2057+ else if (oldcluster == 0)
 2058+ cluster = find_free_cluster(IF_MV2(fat_bpb,)
 2059+ fat_bpb->fsinfo.nextfree);
 2060+#ifdef HAVE_FAT16SUPPORT
 2061+ else /* negative, pseudo-cluster of the root dir */
 2062+ return 0; /* impossible to append something to the root */
 2063+#endif
 2064+
 2065+ if (cluster) {
 2066+ if (oldcluster)
 2067+ update_fat_entry(IF_MV2(fat_bpb,) oldcluster, cluster);
 2068+ else
 2069+ file->firstcluster = cluster;
 2070+ update_fat_entry(IF_MV2(fat_bpb,) cluster, FAT_EOF_MARK);
 2071+ }
 2072+ else {
 2073+#ifdef TEST_FAT
 2074+ if (fat_bpb->fsinfo.freecount>0)
 2075+ panicf("There is free space, but find_free_cluster() "
 2076+ "didn't find it!\n");
 2077+#endif
 2078+ DEBUGF("next_write_cluster(): Disk full!\n");
 2079+ return 0;
 2080+ }
 2081+ }
 2082+ sector = cluster2sec(IF_MV2(fat_bpb,) cluster);
 2083+ if (sector<0)
 2084+ return 0;
 2085+
 2086+ *newsector = sector;
 2087+ return cluster;
 2088+}
 2089+
 2090+static int transfer(IF_MV2(struct bpb* fat_bpb,)
 2091+ unsigned long start, long count, char* buf, bool write )
 2092+{
 2093+#ifndef HAVE_MULTIVOLUME
 2094+ struct bpb* fat_bpb = &fat_bpbs[0];
 2095+#endif
 2096+ int rc;
 2097+
 2098+ LDEBUGF("transfer(s=%lx, c=%lx, %s)\n",
 2099+ start+ fat_bpb->startsector, count, write?"write":"read");
 2100+ if (write) {
 2101+ unsigned long firstallowed;
 2102+#ifdef HAVE_FAT16SUPPORT
 2103+ if (fat_bpb->is_fat16)
 2104+ firstallowed = fat_bpb->rootdirsector;
 2105+ else
 2106+#endif
 2107+ firstallowed = fat_bpb->firstdatasector;
 2108+
 2109+ if (start < firstallowed)
 2110+ panicf("Write %ld before data\n", firstallowed - start);
 2111+ if (start + count > fat_bpb->totalsectors)
 2112+ panicf("Write %ld after data\n",
 2113+ start + count - fat_bpb->totalsectors);
 2114+ rc = storage_write_sectors(IF_MD2(fat_bpb->drive,)
 2115+ start + fat_bpb->startsector, count, buf);
 2116+ }
 2117+ else
 2118+ rc = storage_read_sectors(IF_MD2(fat_bpb->drive,)
 2119+ start + fat_bpb->startsector, count, buf);
 2120+ if (rc < 0) {
 2121+ DEBUGF( "transfer() - Couldn't %s sector %lx"
 2122+ " (error code %d)\n",
 2123+ write ? "write":"read", start, rc);
 2124+ return rc;
 2125+ }
 2126+ return 0;
 2127+}
 2128+
 2129+
 2130+long fat_readwrite( struct fat_file *file, long sectorcount,
 2131+ void* buf, bool write )
 2132+{
 2133+#ifdef HAVE_MULTIVOLUME
 2134+ struct bpb* fat_bpb = &fat_bpbs[file->volume];
 2135+#else
 2136+ struct bpb* fat_bpb = &fat_bpbs[0];
 2137+#endif
 2138+ long cluster = file->lastcluster;
 2139+ long sector = file->lastsector;
 2140+ long clusternum = file->clusternum;
 2141+ long numsec = file->sectornum;
 2142+ bool eof = file->eof;
 2143+ long first=0, last=0;
 2144+ long i;
 2145+ int rc;
 2146+
 2147+ LDEBUGF( "fat_readwrite(file:%lx,count:0x%lx,buf:%lx,%s)\n",
 2148+ file->firstcluster,sectorcount,(long)buf,write?"write":"read");
 2149+ LDEBUGF( "fat_readwrite: sec=%lx numsec=%ld eof=%d\n",
 2150+ sector,numsec, eof?1:0);
 2151+
 2152+ if ( eof && !write)
 2153+ return 0;
 2154+
 2155+ /* find sequential sectors and write them all at once */
 2156+ for (i=0; (i < sectorcount) && (sector > -1); i++ ) {
 2157+ numsec++;
 2158+ if ( numsec > (long)fat_bpb->bpb_secperclus || !cluster ) {
 2159+ long oldcluster = cluster;
 2160+ long oldsector = sector;
 2161+ long oldnumsec = numsec;
 2162+ if (write)
 2163+ cluster = next_write_cluster(file, cluster, &sector);
 2164+ else {
 2165+ cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster);
 2166+ sector = cluster2sec(IF_MV2(fat_bpb,) cluster);
 2167+ }
 2168+
 2169+ clusternum++;
 2170+ numsec=1;
 2171+
 2172+ if (!cluster) {
 2173+ eof = true;
 2174+ if ( write ) {
 2175+ /* remember last cluster, in case
 2176+ we want to append to the file */
 2177+ sector = oldsector;
 2178+ cluster = oldcluster;
 2179+ numsec = oldnumsec;
 2180+ clusternum--;
 2181+ i = -1; /* Error code */
 2182+ break;
 2183+ }
 2184+ }
 2185+ else
 2186+ eof = false;
 2187+ }
 2188+ else {
 2189+ if (sector)
 2190+ sector++;
 2191+ else {
 2192+ /* look up first sector of file */
 2193+ sector = cluster2sec(IF_MV2(fat_bpb,) file->firstcluster);
 2194+ numsec=1;
 2195+#ifdef HAVE_FAT16SUPPORT
 2196+ if (file->firstcluster < 0)
 2197+ { /* FAT16 root dir */
 2198+ sector += fat_bpb->rootdiroffset;
 2199+ numsec += fat_bpb->rootdiroffset;
 2200+ }
 2201+#endif
 2202+ }
 2203+ }
 2204+
 2205+ if (!first)
 2206+ first = sector;
 2207+
 2208+ if ( ((sector != first) && (sector != last+1)) || /* not sequential */
 2209+ (last-first+1 == 256) ) { /* max 256 sectors per ata request */
 2210+ long count = last - first + 1;
 2211+ rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write );
 2212+ if (rc < 0)
 2213+ return rc * 10 - 1;
 2214+
 2215+ buf = (char *)buf + count * SECTOR_SIZE;
 2216+ first = sector;
 2217+ }
 2218+
 2219+ if ((i == sectorcount-1) && /* last sector requested */
 2220+ (!eof))
 2221+ {
 2222+ long count = sector - first + 1;
 2223+ rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write );
 2224+ if (rc < 0)
 2225+ return rc * 10 - 2;
 2226+ }
 2227+
 2228+ last = sector;
 2229+ }
 2230+
 2231+ file->lastcluster = cluster;
 2232+ file->lastsector = sector;
 2233+ file->clusternum = clusternum;
 2234+ file->sectornum = numsec;
 2235+ file->eof = eof;
 2236+
 2237+ /* if eof, don't report last block as read/written */
 2238+ if (eof)
 2239+ i--;
 2240+
 2241+ DEBUGF("Sectors written: %ld\n", i);
 2242+ return i;
 2243+}
 2244+
 2245+int fat_seek(struct fat_file *file, unsigned long seeksector )
 2246+{
 2247+#ifdef HAVE_MULTIVOLUME
 2248+ struct bpb* fat_bpb = &fat_bpbs[file->volume];
 2249+#else
 2250+ struct bpb* fat_bpb = &fat_bpbs[0];
 2251+#endif
 2252+ long clusternum=0, numclusters=0, sectornum=0, sector=0;
 2253+ long cluster = file->firstcluster;
 2254+ long i;
 2255+
 2256+#ifdef HAVE_FAT16SUPPORT
 2257+ if (cluster < 0) /* FAT16 root dir */
 2258+ seeksector += fat_bpb->rootdiroffset;
 2259+#endif
 2260+
 2261+ file->eof = false;
 2262+ if (seeksector) {
 2263+ /* we need to find the sector BEFORE the requested, since
 2264+ the file struct stores the last accessed sector */
 2265+ seeksector--;
 2266+ numclusters = clusternum = seeksector / fat_bpb->bpb_secperclus;
 2267+ sectornum = seeksector % fat_bpb->bpb_secperclus;
 2268+
 2269+ if (file->clusternum && clusternum >= file->clusternum)
 2270+ {
 2271+ cluster = file->lastcluster;
 2272+ numclusters -= file->clusternum;
 2273+ }
 2274+
 2275+ for (i=0; i<numclusters; i++) {
 2276+ cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster);
 2277+ if (!cluster) {
 2278+ DEBUGF("Seeking beyond the end of the file! "
 2279+ "(sector %ld, cluster %ld)\n", seeksector, i);
 2280+ return -1;
 2281+ }
 2282+ }
 2283+
 2284+ sector = cluster2sec(IF_MV2(fat_bpb,) cluster) + sectornum;
 2285+ }
 2286+ else {
 2287+ sectornum = -1;
 2288+ }
 2289+
 2290+ LDEBUGF("fat_seek(%lx, %lx) == %lx, %lx, %lx\n",
 2291+ file->firstcluster, seeksector, cluster, sector, sectornum);
 2292+
 2293+ file->lastcluster = cluster;
 2294+ file->lastsector = sector;
 2295+ file->clusternum = clusternum;
 2296+ file->sectornum = sectornum + 1;
 2297+ return 0;
 2298+}
 2299+
 2300+int fat_opendir(IF_MV2(int volume,)
 2301+ struct fat_dir *dir, unsigned long startcluster,
 2302+ const struct fat_dir *parent_dir)
 2303+{
 2304+#ifdef HAVE_MULTIVOLUME
 2305+ struct bpb* fat_bpb = &fat_bpbs[volume];
 2306+ /* fixme: remove error check when done */
 2307+ if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted)
 2308+ {
 2309+ LDEBUGF("fat_open() illegal volume %d\n", volume);
 2310+ return -1;
 2311+ }
 2312+#else
 2313+ struct bpb* fat_bpb = &fat_bpbs[0];
 2314+#endif
 2315+ int rc;
 2316+
 2317+ if (startcluster == 0)
 2318+ startcluster = fat_bpb->bpb_rootclus;
 2319+
 2320+ rc = fat_open(IF_MV2(volume,) startcluster, &dir->file, parent_dir);
 2321+ if(rc)
 2322+ {
 2323+ DEBUGF( "fat_opendir() - Couldn't open dir"
 2324+ " (error code %d)\n", rc);
 2325+ return rc * 10 - 1;
 2326+ }
 2327+
 2328+ /* assign them after fat_open call so that fat_opendir can be called with the same
 2329+ * fat_dir as parent and result */
 2330+ dir->entry = 0;
 2331+ dir->sector = 0;
 2332+
 2333+ return 0;
 2334+}
 2335+
 2336+int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
 2337+{
 2338+ bool done = false;
 2339+ int i, j;
 2340+ int rc;
 2341+ int order;
 2342+ unsigned char firstbyte;
 2343+ /* Long file names are stored in special entries. Each entry holds
 2344+ up to 13 characters. Names can be max 255 chars (not bytes!) long */
 2345+ /* The number of long entries in the long name can be retrieve from the first
 2346+ * long entry because there are stored in reverse order and have an ordinal */
 2347+ int nb_longs = 0;
 2348+ /* The long entries are expected to be in order, so remember the last ordinal */
 2349+ int last_long_ord = 0;
 2350+
 2351+ dir->entrycount = 0;
 2352+
 2353+ while(!done)
 2354+ {
 2355+ if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector )
 2356+ {
 2357+ rc = fat_readwrite(&dir->file, 1, dir->sectorcache, false);
 2358+ if (rc == 0) {
 2359+ /* eof */
 2360+ entry->name[0] = 0;
 2361+ break;
 2362+ }
 2363+ if (rc < 0) {
 2364+ DEBUGF( "fat_getnext() - Couldn't read dir"
 2365+ " (error code %d)\n", rc);
 2366+ return rc * 10 - 1;
 2367+ }
 2368+ dir->sector = dir->file.lastsector;
 2369+ }
 2370+
 2371+ for (i = dir->entry % DIR_ENTRIES_PER_SECTOR;
 2372+ i < DIR_ENTRIES_PER_SECTOR; i++) {
 2373+ unsigned int entrypos = i * DIR_ENTRY_SIZE;
 2374+
 2375+ firstbyte = dir->sectorcache[entrypos];
 2376+ dir->entry++;
 2377+
 2378+ if (firstbyte == 0xe5) {
 2379+ /* free entry */
 2380+ dir->entrycount = 0;
 2381+ continue;
 2382+ }
 2383+
 2384+ if (firstbyte == 0) {
 2385+ /* last entry */
 2386+ entry->name[0] = 0;
 2387+ dir->entrycount = 0;
 2388+ return 0;
 2389+ }
 2390+
 2391+ dir->entrycount++;
 2392+
 2393+ /* LFN entry? */
 2394+ if ( ( dir->sectorcache[entrypos + FATDIR_ATTR] &
 2395+ FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) {
 2396+ /* extract ordinal */
 2397+ order = dir->sectorcache[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY;
 2398+ /* is this entry the first long entry ? (first in order but containing last part) */
 2399+ if (dir->sectorcache[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) {
 2400+ /* check that order is not too big ! (and non-zero) */
 2401+ if(order <= 0 || order > FATLONG_MAX_ORDER)
 2402+ continue; /* ignore the whole LFN, will trigger lots of warnings */
 2403+ nb_longs = order;
 2404+ last_long_ord = order;
 2405+ }
 2406+ else {
 2407+ /* check orphan entry */
 2408+ if (nb_longs == 0) {
 2409+ logf("fat warning: orphan LFN entry");
 2410+ /* ignore */
 2411+ continue;
 2412+ }
 2413+
 2414+ /* check order */
 2415+ if (order != (last_long_ord - 1)) {
 2416+ logf("fat warning: wrong LFN ordinal");
 2417+ /* ignore the whole LFN, will trigger lots of warnings */
 2418+ nb_longs = 0;
 2419+ }
 2420+
 2421+ last_long_ord = order;
 2422+ }
 2423+
 2424+ /* copy part, reuse [order] for another purpose :) */
 2425+ order = (order - 1) * FATLONG_NAME_BYTES_PER_ENTRY;
 2426+ for(j = 0; j < FATLONG_NAME_CHUNKS; j++) {
 2427+ memcpy(dir->longname + order,
 2428+ dir->sectorcache + entrypos + FATLONG_NAME_POS[j],
 2429+ FATLONG_NAME_SIZE[j]);
 2430+ order += FATLONG_NAME_SIZE[j];
 2431+ }
 2432+ }
 2433+ else {
 2434+ if ( parse_direntry(entry, dir->sectorcache + entrypos) ) {
 2435+
 2436+ /* don't return volume id entry */
 2437+ if ( (entry->attr &
 2438+ (FAT_ATTR_VOLUME_ID|FAT_ATTR_DIRECTORY))
 2439+ == FAT_ATTR_VOLUME_ID)
 2440+ continue;
 2441+
 2442+ /* replace shortname with longname? */
 2443+ /* check that the long name is complete */
 2444+ if (nb_longs != 0 && last_long_ord == 1) {
 2445+ /* hold a copy of the shortname in case the long one is too long */
 2446+ unsigned char shortname[13]; /* 8+3+dot+\0 */
 2447+ int longname_utf8len = 0;
 2448+ /* One character at a time, add 1 for trailing \0, 4 is the maximum size
 2449+ * of a UTF8 encoded character in rockbox */
 2450+ unsigned char longname_utf8segm[4 + 1];
 2451+ unsigned short ucs;
 2452+ int segm_utf8len;
 2453+ /* Temporarily store short name */
 2454+ strcpy(shortname, entry->name);
 2455+ entry->name[0] = 0;
 2456+
 2457+ /* Convert the FAT name to a utf8-encoded one.
 2458+ * The name is not necessary NUL-terminated ! */
 2459+ for (j = 0; j < nb_longs * FATLONG_NAME_BYTES_PER_ENTRY; j += 2) {
 2460+ ucs = dir->longname[j] | (dir->longname[j + 1] << 8);
 2461+ if(ucs == 0 || ucs == FAT_LONGNAME_PAD_UCS)
 2462+ break;
 2463+ /* utf8encode will return a pointer after the converted
 2464+ * string, subtract the pointer to the start to get the length of it */
 2465+ segm_utf8len = utf8encode(ucs, longname_utf8segm) - longname_utf8segm;
 2466+
 2467+ /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */
 2468+ if (longname_utf8len + segm_utf8len >= FAT_FILENAME_BYTES) {
 2469+ /* force use of short name */
 2470+ longname_utf8len = FAT_FILENAME_BYTES + 1;
 2471+ break; /* fallback later */
 2472+ }
 2473+ else {
 2474+ longname_utf8segm[segm_utf8len] = 0;
 2475+ strcat(entry->name + longname_utf8len, longname_utf8segm);
 2476+ longname_utf8len += segm_utf8len;
 2477+ }
 2478+ }
 2479+
 2480+ /* Does the utf8-encoded name fit into the entry? */
 2481+ /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */
 2482+ if (longname_utf8len >= FAT_FILENAME_BYTES) {
 2483+ /* Take the short DOS name. Need to utf8-encode it
 2484+ since it may contain chars from the upper half of
 2485+ the OEM code page which wouldn't be a valid utf8.
 2486+ Beware: this file will be shown with strange
 2487+ glyphs in file browser since unicode 0x80 to 0x9F
 2488+ are control characters. */
 2489+ logf("SN-DOS: %s", shortname);
 2490+ unsigned char *utf8;
 2491+ utf8 = iso_decode(shortname, entry->name, -1,
 2492+ strlen(shortname));
 2493+ *utf8 = 0;
 2494+ logf("SN: %s", entry->name);
 2495+ } else {
 2496+ logf("LN: %s", entry->name);
 2497+ logf("LNLen: %d", longname_utf8len);
 2498+ }
 2499+ }
 2500+ done = true;
 2501+ i++;
 2502+ break;
 2503+ }
 2504+ }
 2505+ }
 2506+ }
 2507+ return 0;
 2508+}
 2509+
 2510+unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume))
 2511+{
 2512+#ifndef HAVE_MULTIVOLUME
 2513+ const int volume = 0;
 2514+#endif
 2515+ struct bpb* fat_bpb = &fat_bpbs[volume];
 2516+ return fat_bpb->bpb_secperclus * SECTOR_SIZE;
 2517+}
 2518+
 2519+#ifdef HAVE_MULTIVOLUME
 2520+bool fat_ismounted(int volume)
 2521+{
 2522+ return (volume<NUM_VOLUMES && fat_bpbs[volume].mounted);
 2523+}
 2524+#endif
Index: embios/trunk/dir.c
@@ -0,0 +1,323 @@
 2+/***************************************************************************
 3+ * __________ __ ___.
 4+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
 5+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
 6+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
 7+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
 8+ * \/ \/ \/ \/ \/
 9+ * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $
 10+ *
 11+ * Copyright (C) 2002 by Björn Stenberg
 12+ *
 13+ * This program is free software; you can redistribute it and/or
 14+ * modify it under the terms of the GNU General Public License
 15+ * as published by the Free Software Foundation; either version 2
 16+ * of the License, or (at your option) any later version.
 17+ *
 18+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 19+ * KIND, either express or implied.
 20+ *
 21+ ****************************************************************************/
 22+#include <stdio.h>
 23+#include <errno.h>
 24+#include <string.h>
 25+#include <stdbool.h>
 26+#include <stdlib.h>
 27+#include "fat.h"
 28+#include "dir.h"
 29+#include "debug.h"
 30+#include "filefuncs.h"
 31+
 32+#if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8)
 33+#define MAX_OPEN_DIRS 12
 34+#else
 35+#define MAX_OPEN_DIRS 8
 36+#endif
 37+
 38+static DIR opendirs[MAX_OPEN_DIRS];
 39+
 40+#ifdef HAVE_HOTSWAP
 41+// release all dir handles on a given volume "by force", to avoid leaks
 42+int release_dirs(int volume)
 43+{
 44+ DIR* pdir = opendirs;
 45+ int dd;
 46+ int closed = 0;
 47+ for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
 48+ {
 49+#ifdef HAVE_MULTIVOLUME
 50+ if (pdir->fatdir.file.volume == volume)
 51+#else
 52+ (void)volume;
 53+#endif
 54+ {
 55+ pdir->busy = false; /* mark as available, no further action */
 56+ closed++;
 57+ }
 58+ }
 59+ return closed; /* return how many we did */
 60+}
 61+#endif /* #ifdef HAVE_HOTSWAP */
 62+
 63+DIR* opendir(const char* name)
 64+{
 65+ char namecopy[MAX_PATH];
 66+ char* part;
 67+ char* end;
 68+ struct fat_direntry entry;
 69+ int dd;
 70+ DIR* pdir = opendirs;
 71+#ifdef HAVE_MULTIVOLUME
 72+ int volume;
 73+#endif
 74+
 75+ if ( name[0] != '/' ) {
 76+ DEBUGF("Only absolute paths supported right now\n");
 77+ return NULL;
 78+ }
 79+
 80+ /* find a free dir descriptor */
 81+ for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
 82+ if ( !pdir->busy )
 83+ break;
 84+
 85+ if ( dd == MAX_OPEN_DIRS ) {
 86+ DEBUGF("Too many dirs open\n");
 87+ errno = EMFILE;
 88+ return NULL;
 89+ }
 90+
 91+ pdir->busy = true;
 92+
 93+#ifdef HAVE_MULTIVOLUME
 94+ /* try to extract a heading volume name, if present */
 95+ volume = strip_volume(name, namecopy);
 96+ pdir->volumecounter = 0;
 97+#else
 98+ strlcpy(namecopy, name, sizeof(namecopy)); /* just copy */
 99+#endif
 100+
 101+ if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
 102+ DEBUGF("Failed opening root dir\n");
 103+ pdir->busy = false;
 104+ return NULL;
 105+ }
 106+
 107+ for ( part = strtok_r(namecopy, "/", &end); part;
 108+ part = strtok_r(NULL, "/", &end)) {
 109+ /* scan dir for name */
 110+ while (1) {
 111+ if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
 112+ (!entry.name[0])) {
 113+ pdir->busy = false;
 114+ return NULL;
 115+ }
 116+ if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
 117+ (!strcasecmp(part, entry.name)) ) {
 118+ /* In reality, the parent_dir parameter of fat_opendir seems
 119+ * useless because it's sole purpose it to have a way to
 120+ * update the file metadata, but here we are only reading
 121+ * a directory so there's no need for that kind of stuff.
 122+ * However, the rmdir function uses a ugly hack to
 123+ * avoid opening a directory twice when deleting it and thus
 124+ * needs those information. That's why we pass pdir->fatdir both
 125+ * as the parent directory and the resulting one (this is safe,
 126+ * in doubt, check fat_open(dir) code) which will allow this kind of
 127+ * (ugly) things */
 128+ if ( fat_opendir(IF_MV2(volume,)
 129+ &pdir->fatdir,
 130+ entry.firstcluster,
 131+ &pdir->fatdir) < 0 ) {
 132+ DEBUGF("Failed opening dir '%s' (%ld)\n",
 133+ part, entry.firstcluster);
 134+ pdir->busy = false;
 135+ return NULL;
 136+ }
 137+#ifdef HAVE_MULTIVOLUME
 138+ pdir->volumecounter = -1; /* n.a. to subdirs */
 139+#endif
 140+ break;
 141+ }
 142+ }
 143+ }
 144+
 145+ return pdir;
 146+}
 147+
 148+int closedir(DIR* dir)
 149+{
 150+ dir->busy=false;
 151+ return 0;
 152+}
 153+
 154+struct dirent* readdir(DIR* dir)
 155+{
 156+ struct fat_direntry entry;
 157+ struct dirent* theent = &(dir->theent);
 158+
 159+ if (!dir->busy)
 160+ return NULL;
 161+
 162+#ifdef HAVE_MULTIVOLUME
 163+ /* Volumes (secondary file systems) get inserted into the root directory
 164+ of the first volume, since we have no separate top level. */
 165+ if (dir->volumecounter >= 0 /* on a root dir */
 166+ && dir->volumecounter < NUM_VOLUMES /* in range */
 167+ && dir->fatdir.file.volume == 0) /* at volume 0 */
 168+ { /* fake special directories, which don't really exist, but
 169+ will get redirected upon opendir() */
 170+ while (++dir->volumecounter < NUM_VOLUMES)
 171+ {
 172+ if (fat_ismounted(dir->volumecounter))
 173+ {
 174+ memset(theent, 0, sizeof(*theent));
 175+ theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
 176+ snprintf(theent->d_name, sizeof(theent->d_name),
 177+ VOL_NAMES, dir->volumecounter);
 178+ return theent;
 179+ }
 180+ }
 181+ }
 182+#endif
 183+ /* normal directory entry fetching follows here */
 184+ if (fat_getnext(&(dir->fatdir),&entry) < 0)
 185+ return NULL;
 186+
 187+ if ( !entry.name[0] )
 188+ return NULL;
 189+
 190+ strlcpy(theent->d_name, entry.name, sizeof(theent->d_name));
 191+ theent->attribute = entry.attr;
 192+ theent->size = entry.filesize;
 193+ theent->startcluster = entry.firstcluster;
 194+ theent->wrtdate = entry.wrtdate;
 195+ theent->wrttime = entry.wrttime;
 196+
 197+ return theent;
 198+}
 199+
 200+int mkdir(const char *name)
 201+{
 202+ DIR *dir;
 203+ char namecopy[MAX_PATH];
 204+ char* end;
 205+ char *basename;
 206+ char *parent;
 207+ struct dirent *entry;
 208+ struct fat_dir newdir;
 209+ int rc;
 210+
 211+ if ( name[0] != '/' ) {
 212+ DEBUGF("mkdir: Only absolute paths supported right now\n");
 213+ return -1;
 214+ }
 215+
 216+ strlcpy(namecopy, name, sizeof(namecopy));
 217+
 218+ /* Split the base name and the path */
 219+ end = strrchr(namecopy, '/');
 220+ *end = 0;
 221+ basename = end+1;
 222+
 223+ if(namecopy == end) /* Root dir? */
 224+ parent = "/";
 225+ else
 226+ parent = namecopy;
 227+
 228+ DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename);
 229+
 230+ dir = opendir(parent);
 231+
 232+ if(!dir) {
 233+ DEBUGF("mkdir: can't open parent dir\n");
 234+ return -2;
 235+ }
 236+
 237+ if(basename[0] == 0) {
 238+ DEBUGF("mkdir: Empty dir name\n");
 239+ errno = EINVAL;
 240+ return -3;
 241+ }
 242+
 243+ /* Now check if the name already exists */
 244+ while ((entry = readdir(dir))) {
 245+ if ( !strcasecmp(basename, entry->d_name) ) {
 246+ DEBUGF("mkdir error: file exists\n");
 247+ errno = EEXIST;
 248+ closedir(dir);
 249+ return - 4;
 250+ }
 251+ }
 252+
 253+ memset(&newdir, 0, sizeof(struct fat_dir));
 254+
 255+ rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
 256+ closedir(dir);
 257+
 258+ return rc;
 259+}
 260+
 261+int rmdir(const char* name)
 262+{
 263+ int rc;
 264+ DIR* dir;
 265+ struct dirent* entry;
 266+
 267+ dir = opendir(name);
 268+ if (!dir)
 269+ {
 270+ errno = ENOENT; /* open error */
 271+ return -1;
 272+ }
 273+
 274+ /* check if the directory is empty */
 275+ while ((entry = readdir(dir)))
 276+ {
 277+ if (strcmp(entry->d_name, ".") &&
 278+ strcmp(entry->d_name, ".."))
 279+ {
 280+ DEBUGF("rmdir error: not empty\n");
 281+ errno = ENOTEMPTY;
 282+ closedir(dir);
 283+ return -2;
 284+ }
 285+ }
 286+
 287+ rc = fat_remove(&(dir->fatdir.file));
 288+ if ( rc < 0 ) {
 289+ DEBUGF("Failed removing dir: %d\n", rc);
 290+ errno = EIO;
 291+ rc = rc * 10 - 3;
 292+ }
 293+
 294+ closedir(dir);
 295+ return rc;
 296+}
 297+
 298+#ifdef HAVE_MULTIVOLUME
 299+/* returns on which volume this is, and copies the reduced name
 300+ (sortof a preprocessor for volume-decorated pathnames) */
 301+int strip_volume(const char* name, char* namecopy)
 302+{
 303+ int volume = 0;
 304+ const char *temp = name;
 305+
 306+ while (*temp == '/') /* skip all leading slashes */
 307+ ++temp;
 308+
 309+ if (*temp && !strncmp(temp, VOL_NAMES, VOL_ENUM_POS))
 310+ {
 311+ temp += VOL_ENUM_POS; /* behind special name */
 312+ volume = atoi(temp); /* number is following */
 313+ temp = strchr(temp, '/'); /* search for slash behind */
 314+ if (temp != NULL)
 315+ name = temp; /* use the part behind the volume */
 316+ else
 317+ name = "/"; /* else this must be the root dir */
 318+ }
 319+
 320+ strlcpy(namecopy, name, MAX_PATH);
 321+
 322+ return volume;
 323+}
 324+#endif /* #ifdef HAVE_MULTIVOLUME */
Index: embios/trunk/fat.h
@@ -0,0 +1,136 @@
 2+/***************************************************************************
 3+ * __________ __ ___.
 4+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
 5+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
 6+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
 7+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
 8+ * \/ \/ \/ \/ \/
 9+ * $Id$
 10+ *
 11+ * Copyright (C) 2002 by Linus Nielsen Feltzing
 12+ *
 13+ * This program is free software; you can redistribute it and/or
 14+ * modify it under the terms of the GNU General Public License
 15+ * as published by the Free Software Foundation; either version 2
 16+ * of the License, or (at your option) any later version.
 17+ *
 18+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 19+ * KIND, either express or implied.
 20+ *
 21+ ****************************************************************************/
 22+
 23+#ifndef FAT_H
 24+#define FAT_H
 25+
 26+#include <stdbool.h>
 27+#include "mv.h" /* for volume definitions */
 28+#include "config.h"
 29+
 30+/* This value can be overwritten by a target in config-[target].h, but
 31+ that behaviour is still experimental */
 32+#ifndef SECTOR_SIZE
 33+#define SECTOR_SIZE 512
 34+#endif
 35+
 36+/* Number of bytes reserved for a file name (including the trailing \0).
 37+ Since names are stored in the entry as UTF-8, we won't be able to
 38+ store all names allowed by FAT. In FAT, a name can have max 255
 39+ characters (not bytes!). Since the UTF-8 encoding of a char may take
 40+ up to 4 bytes, there will be names that we won't be able to store
 41+ completely. For such names, the short DOS name is used. */
 42+#define FAT_FILENAME_BYTES 256
 43+
 44+struct fat_direntry
 45+{
 46+ unsigned char name[FAT_FILENAME_BYTES]; /* UTF-8 encoded name plus \0 */
 47+ unsigned short attr; /* Attributes */
 48+ unsigned char crttimetenth; /* Millisecond creation
 49+ time stamp (0-199) */
 50+ unsigned short crttime; /* Creation time */
 51+ unsigned short crtdate; /* Creation date */
 52+ unsigned short lstaccdate; /* Last access date */
 53+ unsigned short wrttime; /* Last write time */
 54+ unsigned short wrtdate; /* Last write date */
 55+ unsigned long filesize; /* File size in bytes */
 56+ long firstcluster; /* fstclusterhi<<16 + fstcluslo */
 57+};
 58+
 59+#define FAT_ATTR_READ_ONLY 0x01
 60+#define FAT_ATTR_HIDDEN 0x02
 61+#define FAT_ATTR_SYSTEM 0x04
 62+#define FAT_ATTR_VOLUME_ID 0x08
 63+#define FAT_ATTR_DIRECTORY 0x10
 64+#define FAT_ATTR_ARCHIVE 0x20
 65+#define FAT_ATTR_VOLUME 0x40 /* this is a volume, not a real directory */
 66+
 67+struct fat_file
 68+{
 69+ long firstcluster; /* first cluster in file */
 70+ long lastcluster; /* cluster of last access */
 71+ long lastsector; /* sector of last access */
 72+ long clusternum; /* current clusternum */
 73+ long sectornum; /* sector number in this cluster */
 74+ unsigned int direntry; /* short dir entry index from start of dir */
 75+ unsigned int direntries; /* number of dir entries used by this file */
 76+ long dircluster; /* first cluster of dir */
 77+ bool eof;
 78+#ifdef HAVE_MULTIVOLUME
 79+ int volume; /* file resides on which volume */
 80+#endif
 81+};
 82+
 83+struct fat_dir
 84+{
 85+ unsigned int entry;
 86+ unsigned int entrycount;
 87+ long sector;
 88+ struct fat_file file;
 89+ unsigned char sectorcache[SECTOR_SIZE];
 90+ /* There are 2-bytes per characters. We don't want to bother too much, as LFN entries are
 91+ * at much 255 characters longs, that's at most 20 LFN entries. Each entry hold at most
 92+ * 13 characters, that a total of 260 characters. So we keep a buffer of that size.
 93+ * Keep coherent with fat.c code. */
 94+ unsigned char longname[260 * 2];
 95+};
 96+
 97+#ifdef HAVE_HOTSWAP
 98+extern void fat_lock(void);
 99+extern void fat_unlock(void);
 100+#endif
 101+
 102+extern void fat_init(void);
 103+extern int fat_mount(IF_MV2(int volume,) IF_MD2(int drive,) long startsector);
 104+extern int fat_unmount(int volume, bool flush);
 105+extern void fat_size(IF_MV2(int volume,) /* public for info */
 106+ unsigned long* size,
 107+ unsigned long* free);
 108+extern void fat_recalc_free(IF_MV_NONVOID(int volume)); /* public for debug info screen */
 109+extern int fat_create_dir(const char* name,
 110+ struct fat_dir* newdir,
 111+ struct fat_dir* dir);
 112+extern int fat_open(IF_MV2(int volume,)
 113+ long cluster,
 114+ struct fat_file* ent,
 115+ const struct fat_dir* dir);
 116+extern int fat_create_file(const char* name,
 117+ struct fat_file* ent,
 118+ struct fat_dir* dir);
 119+extern long fat_readwrite(struct fat_file *ent, long sectorcount,
 120+ void* buf, bool write );
 121+extern int fat_closewrite(struct fat_file *ent, long size, int attr);
 122+extern int fat_seek(struct fat_file *ent, unsigned long sector );
 123+extern int fat_remove(struct fat_file *ent);
 124+extern int fat_truncate(const struct fat_file *ent);
 125+extern int fat_rename(struct fat_file* file,
 126+ struct fat_dir* dir,
 127+ const unsigned char* newname,
 128+ long size, int attr);
 129+
 130+extern int fat_opendir(IF_MV2(int volume,)
 131+ struct fat_dir *ent, unsigned long startcluster,
 132+ const struct fat_dir *parent_dir);
 133+extern int fat_getnext(struct fat_dir *ent, struct fat_direntry *entry);
 134+extern unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)); /* public for debug info screen */
 135+extern bool fat_ismounted(int volume);
 136+
 137+#endif
Index: embios/trunk/file.c
@@ -0,0 +1,816 @@
 2+/***************************************************************************
 3+ * __________ __ ___.
 4+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
 5+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
 6+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
 7+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
 8+ * \/ \/ \/ \/ \/
 9+ * $Id: file.c 26191 2010-05-20 12:59:12Z funman $
 10+ *
 11+ * Copyright (C) 2002 by Björn Stenberg
 12+ *
 13+ * This program is free software; you can redistribute it and/or
 14+ * modify it under the terms of the GNU General Public License
 15+ * as published by the Free Software Foundation; either version 2
 16+ * of the License, or (at your option) any later version.
 17+ *
 18+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 19+ * KIND, either express or implied.
 20+ *
 21+ ****************************************************************************/
 22+#include <string.h>
 23+#include <errno.h>
 24+#include <stdbool.h>
 25+#include "file.h"
 26+#include "fat.h"
 27+#include "dir.h"
 28+#include "debug.h"
 29+#include "dircache.h"
 30+#include "filefuncs.h"
 31+#include "system.h"
 32+
 33+/*
 34+ These functions provide a roughly POSIX-compatible file IO API.
 35+
 36+ Since the fat32 driver only manages sectors, we maintain a one-sector
 37+ cache for each open file. This way we can provide byte access without
 38+ having to re-read the sector each time.
 39+ The penalty is the RAM used for the cache and slightly more complex code.
 40+*/
 41+
 42+struct filedesc {
 43+ unsigned char cache[SECTOR_SIZE];
 44+ int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */
 45+ long fileoffset;
 46+ long size;
 47+ int attr;
 48+ struct fat_file fatfile;
 49+ bool busy;
 50+ bool write;
 51+ bool dirty;
 52+ bool trunc;
 53+};
 54+
 55+static struct filedesc openfiles[MAX_OPEN_FILES];
 56+
 57+static int flush_cache(int fd);
 58+
 59+int file_creat(const char *pathname)
 60+{
 61+ return open(pathname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
 62+}
 63+
 64+static int open_internal(const char* pathname, int flags, bool use_cache)
 65+{
 66+ DIR* dir;
 67+ struct dirent* entry;
 68+ int fd;
 69+ char pathnamecopy[MAX_PATH];
 70+ char* name;
 71+ struct filedesc* file = NULL;
 72+ int rc;
 73+#ifndef HAVE_DIRCACHE
 74+ (void)use_cache;
 75+#endif
 76+
 77+ LDEBUGF("open(\"%s\",%d)\n",pathname,flags);
 78+
 79+ if ( pathname[0] != '/' ) {
 80+ DEBUGF("'%s' is not an absolute path.\n",pathname);
 81+ DEBUGF("Only absolute pathnames supported at the moment\n");
 82+ errno = EINVAL;
 83+ return -1;
 84+ }
 85+
 86+ /* find a free file descriptor */
 87+ for ( fd=0; fd<MAX_OPEN_FILES; fd++ )
 88+ if ( !openfiles[fd].busy )
 89+ break;
 90+
 91+ if ( fd == MAX_OPEN_FILES ) {
 92+ DEBUGF("Too many files open\n");
 93+ errno = EMFILE;
 94+ return -2;
 95+ }
 96+
 97+ file = &openfiles[fd];
 98+ memset(file, 0, sizeof(struct filedesc));
 99+
 100+ if (flags & (O_RDWR | O_WRONLY)) {
 101+ file->write = true;
 102+
 103+ if (flags & O_TRUNC)
 104+ file->trunc = true;
 105+ }
 106+ file->busy = true;
 107+
 108+#ifdef HAVE_DIRCACHE
 109+ if (dircache_is_enabled() && !file->write && use_cache)
 110+ {
 111+ const struct dircache_entry *ce;
 112+# ifdef HAVE_MULTIVOLUME
 113+ int volume = strip_volume(pathname, pathnamecopy);
 114+# endif
 115+
 116+ ce = dircache_get_entry_ptr(pathname);
 117+ if (!ce)
 118+ {
 119+ errno = ENOENT;
 120+ file->busy = false;
 121+ return -7;
 122+ }
 123+
 124+ fat_open(IF_MV2(volume,)
 125+ ce->startcluster,
 126+ &(file->fatfile),
 127+ NULL);
 128+ file->size = ce->size;
 129+ file->attr = ce->attribute;
 130+ file->cacheoffset = -1;
 131+ file->fileoffset = 0;
 132+
 133+ return fd;
 134+ }
 135+#endif
 136+
 137+ strlcpy(pathnamecopy, pathname, sizeof(pathnamecopy));
 138+
 139+ /* locate filename */
 140+ name=strrchr(pathnamecopy+1,'/');
 141+ if ( name ) {
 142+ *name = 0;
 143+ dir = opendir(pathnamecopy);
 144+ *name = '/';
 145+ name++;
 146+ }
 147+ else {
 148+ dir = opendir("/");
 149+ name = pathnamecopy+1;
 150+ }
 151+ if (!dir) {
 152+ DEBUGF("Failed opening dir\n");
 153+ errno = EIO;
 154+ file->busy = false;
 155+ return -4;
 156+ }
 157+
 158+ if(name[0] == 0) {
 159+ DEBUGF("Empty file name\n");
 160+ errno = EINVAL;
 161+ file->busy = false;
 162+ closedir(dir);
 163+ return -5;
 164+ }
 165+
 166+ /* scan dir for name */
 167+ while ((entry = readdir(dir))) {
 168+ if ( !strcasecmp(name, entry->d_name) ) {
 169+ fat_open(IF_MV2(dir->fatdir.file.volume,)
 170+ entry->startcluster,
 171+ &(file->fatfile),
 172+ &(dir->fatdir));
 173+ file->size = file->trunc ? 0 : entry->size;
 174+ file->attr = entry->attribute;
 175+ break;
 176+ }
 177+ }
 178+
 179+ if ( !entry ) {
 180+ LDEBUGF("Didn't find file %s\n",name);
 181+ if ( file->write && (flags & O_CREAT) ) {
 182+ rc = fat_create_file(name,
 183+ &(file->fatfile),
 184+ &(dir->fatdir));
 185+ if (rc < 0) {
 186+ DEBUGF("Couldn't create %s in %s\n",name,pathnamecopy);
 187+ errno = EIO;
 188+ file->busy = false;
 189+ closedir(dir);
 190+ return rc * 10 - 6;
 191+ }
 192+#ifdef HAVE_DIRCACHE
 193+ dircache_add_file(pathname, file->fatfile.firstcluster);
 194+#endif
 195+ file->size = 0;
 196+ file->attr = 0;
 197+ }
 198+ else {
 199+ DEBUGF("Couldn't find %s in %s\n",name,pathnamecopy);
 200+ errno = ENOENT;
 201+ file->busy = false;
 202+ closedir(dir);
 203+ return -7;
 204+ }
 205+ } else {
 206+ if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
 207+ errno = EISDIR;
 208+ file->busy = false;
 209+ closedir(dir);
 210+ return -8;
 211+ }
 212+ }
 213+ closedir(dir);
 214+
 215+ file->cacheoffset = -1;
 216+ file->fileoffset = 0;
 217+
 218+ if (file->write && (flags & O_APPEND)) {
 219+ rc = lseek(fd,0,SEEK_END);
 220+ if (rc < 0 )
 221+ return rc * 10 - 9;
 222+ }
 223+
 224+#ifdef HAVE_DIRCACHE
 225+ if (file->write)
 226+ dircache_bind(fd, pathname);
 227+#endif
 228+
 229+ return fd;
 230+}
 231+
 232+int file_open(const char* pathname, int flags)
 233+{
 234+ /* By default, use the dircache if available. */
 235+ return open_internal(pathname, flags, true);
 236+}
 237+
 238+int close(int fd)
 239+{
 240+ struct filedesc* file = &openfiles[fd];
 241+ int rc = 0;
 242+
 243+ LDEBUGF("close(%d)\n", fd);
 244+
 245+ if (fd < 0 || fd > MAX_OPEN_FILES-1) {
 246+ errno = EINVAL;
 247+ return -1;
 248+ }
 249+ if (!file->busy) {
 250+ errno = EBADF;
 251+ return -2;
 252+ }
 253+ if (file->write) {
 254+ rc = fsync(fd);
 255+ if (rc < 0)
 256+ return rc * 10 - 3;
 257+#ifdef HAVE_DIRCACHE
 258+ dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
 259+ dircache_update_filetime(fd);
 260+#endif
 261+ }
 262+
 263+ file->busy = false;
 264+ return 0;
 265+}
 266+
 267+int fsync(int fd)
 268+{
 269+ struct filedesc* file = &openfiles[fd];
 270+ int rc = 0;
 271+
 272+ LDEBUGF("fsync(%d)\n", fd);
 273+
 274+ if (fd < 0 || fd > MAX_OPEN_FILES-1) {
 275+ errno = EINVAL;
 276+ return -1;
 277+ }
 278+ if (!file->busy) {
 279+ errno = EBADF;
 280+ return -2;
 281+ }
 282+ if (file->write) {
 283+ /* flush sector cache */
 284+ if ( file->dirty ) {
 285+ rc = flush_cache(fd);
 286+ if (rc < 0)
 287+ {
 288+ /* when failing, try to close the file anyway */
 289+ fat_closewrite(&(file->fatfile), file->size, file->attr);
 290+ return rc * 10 - 3;
 291+ }
 292+ }
 293+
 294+ /* truncate? */
 295+ if (file->trunc) {
 296+ rc = ftruncate(fd, file->size);
 297+ if (rc < 0)
 298+ {
 299+ /* when failing, try to close the file anyway */
 300+ fat_closewrite(&(file->fatfile), file->size, file->attr);
 301+ return rc * 10 - 4;
 302+ }
 303+ }
 304+
 305+ /* tie up all loose ends */
 306+ rc = fat_closewrite(&(file->fatfile), file->size, file->attr);
 307+ if (rc < 0)
 308+ return rc * 10 - 5;
 309+ }
 310+ return 0;
 311+}
 312+
 313+int remove(const char* name)
 314+{
 315+ int rc;
 316+ struct filedesc* file;
 317+ /* Can't use dircache now, because we need to access the fat structures. */
 318+ int fd = open_internal(name, O_WRONLY, false);
 319+ if ( fd < 0 )
 320+ return fd * 10 - 1;
 321+
 322+ file = &openfiles[fd];
 323+#ifdef HAVE_DIRCACHE
 324+ dircache_remove(name);
 325+#endif
 326+ rc = fat_remove(&(file->fatfile));
 327+ if ( rc < 0 ) {
 328+ DEBUGF("Failed removing file: %d\n", rc);
 329+ errno = EIO;
 330+ return rc * 10 - 3;
 331+ }
 332+
 333+ file->size = 0;
 334+
 335+ rc = close(fd);
 336+ if (rc<0)
 337+ return rc * 10 - 4;
 338+
 339+ return 0;
 340+}
 341+
 342+int rename(const char* path, const char* newpath)
 343+{
 344+ int rc, fd;
 345+ DIR* dir;
 346+ char* nameptr;
 347+ char* dirptr;
 348+ struct filedesc* file;
 349+ char newpath2[MAX_PATH];
 350+
 351+ /* verify new path does not already exist */
 352+ /* If it is a directory, errno == EISDIR if the name exists */
 353+ fd = open(newpath, O_RDONLY);
 354+ if ( fd >= 0 || errno == EISDIR) {
 355+ close(fd);
 356+ errno = EBUSY;
 357+ return -1;
 358+ }
 359+ close(fd);
 360+
 361+ fd = open_internal(path, O_RDONLY, false);
 362+ if ( fd < 0 ) {
 363+ errno = EIO;
 364+ return fd * 10 - 2;
 365+ }
 366+
 367+ /* extract new file name */
 368+ nameptr = strrchr(newpath,'/');
 369+ if (nameptr)
 370+ nameptr++;
 371+ else {
 372+ close(fd);
 373+ return - 3;
 374+ }
 375+
 376+ /* Extract new path */
 377+ strcpy(newpath2, newpath);
 378+
 379+ dirptr = strrchr(newpath2,'/');
 380+ if(dirptr)
 381+ *dirptr = 0;
 382+ else {
 383+ close(fd);
 384+ return - 4;
 385+ }
 386+
 387+ dirptr = newpath2;
 388+
 389+ if(strlen(dirptr) == 0) {
 390+ dirptr = "/";
 391+ }
 392+
 393+ dir = opendir(dirptr);
 394+ if(!dir) {
 395+ close(fd);
 396+ return - 5;
 397+ }
 398+
 399+ file = &openfiles[fd];
 400+
 401+ rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr,
 402+ file->size, file->attr);
 403+#ifdef HAVE_MULTIVOLUME
 404+ if ( rc == -1) {
 405+ close(fd);
 406+ closedir(dir);
 407+ DEBUGF("Failed renaming file across volumnes: %d\n", rc);
 408+ errno = EXDEV;
 409+ return -6;
 410+ }
 411+#endif
 412+ if ( rc < 0 ) {
 413+ close(fd);
 414+ closedir(dir);
 415+ DEBUGF("Failed renaming file: %d\n", rc);
 416+ errno = EIO;
 417+ return rc * 10 - 7;
 418+ }
 419+
 420+#ifdef HAVE_DIRCACHE
 421+ dircache_rename(path, newpath);
 422+#endif
 423+
 424+ rc = close(fd);
 425+ if (rc<0) {
 426+ closedir(dir);
 427+ errno = EIO;
 428+ return rc * 10 - 8;
 429+ }
 430+
 431+ rc = closedir(dir);
 432+ if (rc<0) {
 433+ errno = EIO;
 434+ return rc * 10 - 9;
 435+ }
 436+
 437+ return 0;
 438+}
 439+
 440+int ftruncate(int fd, off_t size)
 441+{
 442+ int rc, sector;
 443+ struct filedesc* file = &openfiles[fd];
 444+
 445+ sector = size / SECTOR_SIZE;
 446+ if (size % SECTOR_SIZE)
 447+ sector++;
 448+
 449+ rc = fat_seek(&(file->fatfile), sector);
 450+ if (rc < 0) {
 451+ errno = EIO;
 452+ return rc * 10 - 1;
 453+ }
 454+
 455+ rc = fat_truncate(&(file->fatfile));
 456+ if (rc < 0) {
 457+ errno = EIO;
 458+ return rc * 10 - 2;
 459+ }
 460+
 461+ file->size = size;
 462+#ifdef HAVE_DIRCACHE
 463+ dircache_update_filesize(fd, size, file->fatfile.firstcluster);
 464+#endif
 465+
 466+ return 0;
 467+}
 468+
 469+static int flush_cache(int fd)
 470+{
 471+ int rc;
 472+ struct filedesc* file = &openfiles[fd];
 473+ long sector = file->fileoffset / SECTOR_SIZE;
 474+
 475+ DEBUGF("Flushing dirty sector cache\n");
 476+
 477+ /* make sure we are on correct sector */
 478+ rc = fat_seek(&(file->fatfile), sector);
 479+ if ( rc < 0 )
 480+ return rc * 10 - 3;
 481+
 482+ rc = fat_readwrite(&(file->fatfile), 1, file->cache, true );
 483+
 484+ if ( rc < 0 ) {
 485+ if(file->fatfile.eof)
 486+ errno = ENOSPC;
 487+
 488+ return rc * 10 - 2;
 489+ }
 490+
 491+ file->dirty = false;
 492+
 493+ return 0;
 494+}
 495+
 496+static int readwrite(int fd, void* buf, long count, bool write)
 497+{
 498+ long sectors;
 499+ long nread=0;
 500+ struct filedesc* file;
 501+ int rc;
 502+
 503+ if (fd < 0 || fd > MAX_OPEN_FILES-1) {
 504+ errno = EINVAL;
 505+ return -1;
 506+ }
 507+
 508+ file = &openfiles[fd];
 509+
 510+ if ( !file->busy ) {
 511+ errno = EBADF;
 512+ return -1;
 513+ }
 514+
 515+ if(file->attr & FAT_ATTR_DIRECTORY) {
 516+ errno = EISDIR;
 517+ return -1;
 518+ }
 519+
 520+ LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n",
 521+ fd,(long)buf,count,write?"write":"read");
 522+
 523+ /* attempt to read past EOF? */
 524+ if (!write && count > file->size - file->fileoffset)
 525+ count = file->size - file->fileoffset;
 526+
 527+ /* any head bytes? */
 528+ if ( file->cacheoffset != -1 ) {
 529+ int offs = file->cacheoffset;
 530+ int headbytes = MIN(count, SECTOR_SIZE - offs);
 531+
 532+ if (write) {
 533+ memcpy( file->cache + offs, buf, headbytes );
 534+ file->dirty = true;
 535+ }
 536+ else {
 537+ memcpy( buf, file->cache + offs, headbytes );
 538+ }
 539+
 540+ if (offs + headbytes == SECTOR_SIZE) {
 541+ if (file->dirty) {
 542+ rc = flush_cache(fd);
 543+ if ( rc < 0 ) {
 544+ errno = EIO;
 545+ return rc * 10 - 2;
 546+ }
 547+ }
 548+ file->cacheoffset = -1;
 549+ }
 550+ else {
 551+ file->cacheoffset += headbytes;
 552+ }
 553+
 554+ nread = headbytes;
 555+ count -= headbytes;
 556+ }
 557+
 558+ /* If the buffer has been modified, either it has been flushed already
 559+ * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no
 560+ * more data to follow in this call). Do NOT flush here. */
 561+
 562+ /* read/write whole sectors right into/from the supplied buffer */
 563+ sectors = count / SECTOR_SIZE;
 564+ if ( sectors ) {
 565+ rc = fat_readwrite(&(file->fatfile), sectors,
 566+ (unsigned char*)buf+nread, write );
 567+ if ( rc < 0 ) {
 568+ DEBUGF("Failed read/writing %ld sectors\n",sectors);
 569+ errno = EIO;
 570+ if(write && file->fatfile.eof) {
 571+ DEBUGF("No space left on device\n");
 572+ errno = ENOSPC;
 573+ } else {
 574+ file->fileoffset += nread;
 575+ }
 576+ file->cacheoffset = -1;
 577+ /* adjust file size to length written */
 578+ if ( write && file->fileoffset > file->size )
 579+ {
 580+ file->size = file->fileoffset;
 581+#ifdef HAVE_DIRCACHE
 582+ dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
 583+#endif
 584+ }
 585+ return nread ? nread : rc * 10 - 4;
 586+ }
 587+ else {
 588+ if ( rc > 0 ) {
 589+ nread += rc * SECTOR_SIZE;
 590+ count -= sectors * SECTOR_SIZE;
 591+
 592+ /* if eof, skip tail bytes */
 593+ if ( rc < sectors )
 594+ count = 0;
 595+ }
 596+ else {
 597+ /* eof */
 598+ count=0;
 599+ }
 600+
 601+ file->cacheoffset = -1;
 602+ }
 603+ }
 604+
 605+ /* any tail bytes? */
 606+ if ( count ) {
 607+ if (write) {
 608+ if ( file->fileoffset + nread < file->size ) {
 609+ /* sector is only partially filled. copy-back from disk */
 610+ LDEBUGF("Copy-back tail cache\n");
 611+ rc = fat_readwrite(&(file->fatfile), 1, file->cache, false );
 612+ if ( rc < 0 ) {
 613+ DEBUGF("Failed writing\n");
 614+ errno = EIO;
 615+ file->fileoffset += nread;
 616+ file->cacheoffset = -1;
 617+ /* adjust file size to length written */
 618+ if ( file->fileoffset > file->size )
 619+ {
 620+ file->size = file->fileoffset;
 621+#ifdef HAVE_DIRCACHE
 622+ dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
 623+#endif
 624+ }
 625+ return nread ? nread : rc * 10 - 5;
 626+ }
 627+ /* seek back one sector to put file position right */
 628+ rc = fat_seek(&(file->fatfile),
 629+ (file->fileoffset + nread) /
 630+ SECTOR_SIZE);
 631+ if ( rc < 0 ) {
 632+ DEBUGF("fat_seek() failed\n");
 633+ errno = EIO;
 634+ file->fileoffset += nread;
 635+ file->cacheoffset = -1;
 636+ /* adjust file size to length written */
 637+ if ( file->fileoffset > file->size )
 638+ {
 639+ file->size = file->fileoffset;
 640+#ifdef HAVE_DIRCACHE
 641+ dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
 642+#endif
 643+ }
 644+ return nread ? nread : rc * 10 - 6;
 645+ }
 646+ }
 647+ memcpy( file->cache, (unsigned char*)buf + nread, count );
 648+ file->dirty = true;
 649+ }
 650+ else {
 651+ rc = fat_readwrite(&(file->fatfile), 1, file->cache,false);
 652+ if (rc < 1 ) {
 653+ DEBUGF("Failed caching sector\n");
 654+ errno = EIO;
 655+ file->fileoffset += nread;
 656+ file->cacheoffset = -1;
 657+ return nread ? nread : rc * 10 - 7;
 658+ }
 659+ memcpy( (unsigned char*)buf + nread, file->cache, count );
 660+ }
 661+
 662+ nread += count;
 663+ file->cacheoffset = count;
 664+ }
 665+
 666+ file->fileoffset += nread;
 667+ LDEBUGF("fileoffset: %ld\n", file->fileoffset);
 668+
 669+ /* adjust file size to length written */
 670+ if ( write && file->fileoffset > file->size )
 671+ {
 672+ file->size = file->fileoffset;
 673+#ifdef HAVE_DIRCACHE
 674+ dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
 675+#endif
 676+ }
 677+
 678+ return nread;
 679+}
 680+
 681+ssize_t write(int fd, const void* buf, size_t count)
 682+{
 683+ if (!openfiles[fd].write) {
 684+ errno = EACCES;
 685+ return -1;
 686+ }
 687+ return readwrite(fd, (void *)buf, count, true);
 688+}
 689+
 690+ssize_t read(int fd, void* buf, size_t count)
 691+{
 692+ return readwrite(fd, buf, count, false);
 693+}
 694+
 695+
 696+off_t lseek(int fd, off_t offset, int whence)
 697+{
 698+ off_t pos;
 699+ long newsector;
 700+ long oldsector;
 701+ int sectoroffset;
 702+ int rc;
 703+ struct filedesc* file = &openfiles[fd];
 704+
 705+ LDEBUGF("lseek(%d,%ld,%d)\n",fd,offset,whence);
 706+
 707+ if (fd < 0 || fd > MAX_OPEN_FILES-1) {
 708+ errno = EINVAL;
 709+ return -1;
 710+ }
 711+ if ( !file->busy ) {
 712+ errno = EBADF;
 713+ return -1;
 714+ }
 715+
 716+ switch ( whence ) {
 717+ case SEEK_SET:
 718+ pos = offset;
 719+ break;
 720+
 721+ case SEEK_CUR:
 722+ pos = file->fileoffset + offset;
 723+ break;
 724+
 725+ case SEEK_END:
 726+ pos = file->size + offset;
 727+ break;
 728+
 729+ default:
 730+ errno = EINVAL;
 731+ return -2;
 732+ }
 733+ if ((pos < 0) || (pos > file->size)) {
 734+ errno = EINVAL;
 735+ return -3;
 736+ }
 737+
 738+ /* new sector? */
 739+ newsector = pos / SECTOR_SIZE;
 740+ oldsector = file->fileoffset / SECTOR_SIZE;
 741+ sectoroffset = pos % SECTOR_SIZE;
 742+
 743+ if ( (newsector != oldsector) ||
 744+ ((file->cacheoffset==-1) && sectoroffset) ) {
 745+
 746+ if ( newsector != oldsector ) {
 747+ if (file->dirty) {
 748+ rc = flush_cache(fd);
 749+ if (rc < 0)
 750+ return rc * 10 - 5;
 751+ }
 752+
 753+ rc = fat_seek(&(file->fatfile), newsector);
 754+ if ( rc < 0 ) {
 755+ errno = EIO;
 756+ return rc * 10 - 4;
 757+ }
 758+ }
 759+ if ( sectoroffset ) {
 760+ rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false);
 761+ if ( rc < 0 ) {
 762+ errno = EIO;
 763+ return rc * 10 - 6;
 764+ }
 765+ file->cacheoffset = sectoroffset;
 766+ }
 767+ else
 768+ file->cacheoffset = -1;
 769+ }
 770+ else
 771+ if ( file->cacheoffset != -1 )
 772+ file->cacheoffset = sectoroffset;
 773+
 774+ file->fileoffset = pos;
 775+
 776+ return pos;
 777+}
 778+
 779+off_t filesize(int fd)
 780+{
 781+ struct filedesc* file = &openfiles[fd];
 782+
 783+ if (fd < 0 || fd > MAX_OPEN_FILES-1) {
 784+ errno = EINVAL;
 785+ return -1;
 786+ }
 787+ if ( !file->busy ) {
 788+ errno = EBADF;
 789+ return -1;
 790+ }
 791+
 792+ return file->size;
 793+}
 794+
 795+
 796+#ifdef HAVE_HOTSWAP
 797+/* release all file handles on a given volume "by force", to avoid leaks */
 798+int release_files(int volume)
 799+{
 800+ struct filedesc* pfile = openfiles;
 801+ int fd;
 802+ int closed = 0;
 803+ for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++)
 804+ {
 805+#ifdef HAVE_MULTIVOLUME
 806+ if (pfile->fatfile.volume == volume)
 807+#else
 808+ (void)volume;
 809+#endif
 810+ {
 811+ pfile->busy = false; /* mark as available, no further action */
 812+ closed++;
 813+ }
 814+ }
 815+ return closed; /* return how many we did */
 816+}
 817+#endif /* #ifdef HAVE_HOTSWAP */
Index: embios/trunk/dir.h
@@ -0,0 +1,76 @@
 2+/***************************************************************************
 3+ * __________ __ ___.
 4+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
 5+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
 6+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
 7+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
 8+ * \/ \/ \/ \/ \/
 9+ * $Id: dir.h 13741 2007-06-30 02:08:27Z jethead71 $
 10+ *
 11+ * Copyright (C) 2002 by Björn Stenberg
 12+ *
 13+ * This program is free software; you can redistribute it and/or
 14+ * modify it under the terms of the GNU General Public License
 15+ * as published by the Free Software Foundation; either version 2
 16+ * of the License, or (at your option) any later version.
 17+ *
 18+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 19+ * KIND, either express or implied.
 20+ *
 21+ ****************************************************************************/
 22+#ifndef __DIR_H__
 23+#define __DIR_H__
 24+
 25+#include <stdbool.h>
 26+#include "file.h"
 27+
 28+#define ATTR_READ_ONLY 0x01
 29+#define ATTR_HIDDEN 0x02
 30+#define ATTR_SYSTEM 0x04
 31+#define ATTR_VOLUME_ID 0x08
 32+#define ATTR_DIRECTORY 0x10
 33+#define ATTR_ARCHIVE 0x20
 34+#define ATTR_VOLUME 0x40 /* this is a volume, not a real directory */
 35+
 36+struct dirent {
 37+ unsigned char d_name[MAX_PATH];
 38+ int attribute;
 39+ long size;
 40+ long startcluster;
 41+ unsigned short wrtdate; /* Last write date */
 42+ unsigned short wrttime; /* Last write time */
 43+};
 44+#endif
 45+
 46+#include "fat.h"
 47+
 48+typedef struct {
 49+ bool busy;
 50+ long startcluster;
 51+ struct fat_dir fatdir;
 52+ struct dirent_uncached theent;
 53+#ifdef HAVE_MULTIVOLUME
 54+ int volumecounter; /* running counter for faked volume entries */
 55+#endif
 56+} DIR;
 57+
 58+#ifdef HAVE_HOTSWAP
 59+char *get_volume_name(int volume);
 60+#endif
 61+
 62+#ifdef HAVE_MULTIVOLUME
 63+ int strip_volume(const char*, char*);
 64+#endif
 65+
 66+extern DIR* opendir(const char* name);
 67+extern int closedir(DIR* dir);
 68+extern int mkdir(const char* name);
 69+extern int rmdir(const char* name);
 70+
 71+extern struct dirent* readdir(DIR* dir);
 72+
 73+extern int release_dirs(int volume);
 74+
 75+#endif /* DIRFUNCTIONS_DEFINED */
 76+
 77+#endif
Index: embios/trunk/file.h
@@ -0,0 +1,81 @@
 2+/***************************************************************************
 3+ * __________ __ ___.
 4+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
 5+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
 6+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
 7+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
 8+ * \/ \/ \/ \/ \/
 9+ * $Id: file.h 25856 2010-05-06 22:17:34Z kugel $
 10+ *
 11+ * Copyright (C) 2002 by Björn Stenberg
 12+ *
 13+ * This program is free software; you can redistribute it and/or
 14+ * modify it under the terms of the GNU General Public License
 15+ * as published by the Free Software Foundation; either version 2
 16+ * of the License, or (at your option) any later version.
 17+ *
 18+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 19+ * KIND, either express or implied.
 20+ *
 21+ ****************************************************************************/
 22+
 23+#ifndef _FILE_H_
 24+#define _FILE_H_
 25+
 26+#define MAX_PATH 260
 27+
 28+#include <sys/types.h>
 29+#include "_ansi.h"
 30+
 31+#define MAX_OPEN_FILES 11
 32+
 33+#ifndef SEEK_SET
 34+#define SEEK_SET 0
 35+#endif
 36+#ifndef SEEK_CUR
 37+#define SEEK_CUR 1
 38+#endif
 39+#ifndef SEEK_END
 40+#define SEEK_END 2
 41+#endif
 42+
 43+#ifndef O_RDONLY
 44+#define O_RDONLY 0
 45+#define O_WRONLY 1
 46+#define O_RDWR 2
 47+#define O_CREAT 4
 48+#define O_APPEND 8
 49+#define O_TRUNC 0x10
 50+#endif
 51+
 52+typedef int (*open_func)(const char* pathname, int flags, ...);
 53+typedef ssize_t (*read_func)(int fd, void *buf, size_t count);
 54+typedef int (*creat_func)(const char *pathname, mode_t mode);
 55+typedef ssize_t (*write_func)(int fd, const void *buf, size_t count);
 56+typedef void (*qsort_func)(void *base, size_t nmemb, size_t size,
 57+ int(*_compar)(const void *, const void *));
 58+
 59+extern int file_open(const char* pathname, int flags);
 60+extern int close(int fd);
 61+extern int fsync(int fd);
 62+extern ssize_t read(int fd, void *buf, size_t count);
 63+extern off_t lseek(int fildes, off_t offset, int whence);
 64+extern int file_creat(const char *pathname);
 65+
 66+/* posix compatibility function */
 67+static inline int creat(const char *pathname, mode_t mode)
 68+{
 69+ (void)mode;
 70+ return file_creat(pathname);
 71+}
 72+
 73+#define open(x, y, ...) file_open(x,y)
 74+
 75+extern ssize_t write(int fd, const void *buf, size_t count);
 76+extern int remove(const char* pathname);
 77+extern int rename(const char* path, const char* newname);
 78+extern int ftruncate(int fd, off_t length);
 79+extern off_t filesize(int fd);
 80+extern int release_files(int volume);
 81+int fdprintf (int fd, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
 82+#endif