Index: embios/trunk/target/ipodnano2g/nand.c |
— | — | @@ -377,11 +377,11 @@ |
378 | 378 | uint8_t* data = (uint8_t*)databuffer;
|
379 | 379 | uint8_t* spare = nand_spare;
|
380 | 380 | if (sparebuffer) spare = (uint8_t*)sparebuffer;
|
381 | | - if ((uint32_t)databuffer & 0xf)
|
| 381 | + if ((uint32_t)databuffer & (CACHEALIGN_SIZE - 1))
|
382 | 382 | panicf(PANIC_KILLUSERTHREADS,
|
383 | 383 | "nand_read_page: Misaligned data buffer at %08X (bank %lu, page %lu)",
|
384 | 384 | (unsigned int)databuffer, bank, page);
|
385 | | - if ((uint32_t)sparebuffer & 0xf)
|
| 385 | + if ((uint32_t)sparebuffer & (CACHEALIGN_SIZE - 1))
|
386 | 386 | panicf(PANIC_KILLUSERTHREADS,
|
387 | 387 | "nand_read_page: Misaligned spare buffer at %08X (bank %lu, page %lu)",
|
388 | 388 | (unsigned int)sparebuffer, bank, page);
|
Index: embios/trunk/target/ipodnano2g/ftl.c |
— | — | @@ -1287,7 +1287,7 @@ |
1288 | 1288 | DEBUGF("FTL: Reading %d sectors starting at %d", count, sector);
|
1289 | 1289 | #endif
|
1290 | 1290 |
|
1291 | | - if ((uint32_t)buffer & 0xf)
|
| 1291 | + if ((uint32_t)buffer & (CACHEALIGN_SIZE - 1))
|
1292 | 1292 | panicf(PANIC_KILLTHREAD,
|
1293 | 1293 | "ftl_read: Misaligned data buffer at %08X (sector %lu, count %lu)",
|
1294 | 1294 | (unsigned int)buffer, sector, count);
|
— | — | @@ -1947,7 +1947,7 @@ |
1948 | 1948 | DEBUGF("FTL: Writing %d sectors starting at %d", count, sector);
|
1949 | 1949 | #endif
|
1950 | 1950 |
|
1951 | | - if ((uint32_t)buffer & 0xf)
|
| 1951 | + if ((uint32_t)buffer & (CACHEALIGN_SIZE - 1))
|
1952 | 1952 | panicf(PANIC_KILLTHREAD,
|
1953 | 1953 | "ftl_write: Misaligned data buffer at %08X (sector %lu, count %lu)",
|
1954 | 1954 | (unsigned int)buffer, sector, count);
|
Index: embios/trunk/file.c |
— | — | @@ -1,767 +1,772 @@ |
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 "global.h" |
23 | | -#include <string.h> |
24 | | -#include <errno.h> |
25 | | -#include "file.h" |
26 | | -#include "fat.h" |
27 | | -#include "dir.h" |
28 | | -#include "util.h" |
29 | | - |
30 | | -/* |
31 | | - These functions provide a roughly POSIX-compatible file IO API. |
32 | | - |
33 | | - Since the fat32 driver only manages sectors, we maintain a one-sector |
34 | | - cache for each open file. This way we can provide byte access without |
35 | | - having to re-read the sector each time. |
36 | | - The penalty is the RAM used for the cache and slightly more complex code. |
37 | | -*/ |
38 | | - |
| 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 "global.h"
|
| 23 | +#include <string.h>
|
| 24 | +#include <errno.h>
|
| 25 | +#include "file.h"
|
| 26 | +#include "fat.h"
|
| 27 | +#include "dir.h"
|
| 28 | +#include "util.h"
|
| 29 | +
|
| 30 | +/*
|
| 31 | + These functions provide a roughly POSIX-compatible file IO API.
|
| 32 | +
|
| 33 | + Since the fat32 driver only manages sectors, we maintain a one-sector
|
| 34 | + cache for each open file. This way we can provide byte access without
|
| 35 | + having to re-read the sector each time.
|
| 36 | + The penalty is the RAM used for the cache and slightly more complex code.
|
| 37 | +*/
|
| 38 | +
|
39 | 39 | extern struct scheduler_thread* current_thread;
|
40 | | - |
41 | | -struct filedesc { |
42 | | - unsigned char cache[SECTOR_SIZE] CACHEALIGN_ATTR; |
43 | | - int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */ |
44 | | - long fileoffset; |
45 | | - long size; |
46 | | - int attr; |
47 | | - struct fat_file fatfile; |
48 | | - bool busy; |
49 | | - bool write; |
50 | | - bool dirty; |
51 | | - bool trunc; |
52 | | - struct scheduler_thread* process; |
53 | | -} CACHEALIGN_ATTR; |
54 | | - |
55 | | -static struct filedesc openfiles[MAX_OPEN_FILES] CACHEALIGN_ATTR; |
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 | | - |
74 | | - DEBUGF("open(\"%s\",%d)",pathname,flags); |
75 | | - |
76 | | - if ( pathname[0] != '/' ) { |
77 | | - DEBUGF("'%s' is not an absolute path.",pathname); |
78 | | - DEBUGF("Only absolute pathnames supported at the moment"); |
79 | | - errno = EINVAL; |
80 | | - return -1; |
81 | | - } |
82 | | - |
83 | | - /* find a free file descriptor */ |
84 | | - for ( fd=0; fd<MAX_OPEN_FILES; fd++ ) |
85 | | - if ( !openfiles[fd].busy ) |
86 | | - break; |
87 | | - |
88 | | - if ( fd == MAX_OPEN_FILES ) { |
89 | | - DEBUGF("Too many files open"); |
90 | | - errno = EMFILE; |
91 | | - return -2; |
92 | | - } |
93 | | - |
94 | | - file = &openfiles[fd]; |
95 | | - memset(file, 0, sizeof(struct filedesc)); |
96 | | - |
97 | | - if (flags & (O_RDWR | O_WRONLY)) { |
98 | | - file->write = true; |
99 | | - |
100 | | - if (flags & O_TRUNC) |
101 | | - file->trunc = true; |
102 | | - } |
103 | | - file->busy = true; |
104 | | - file->process = current_thread; |
105 | | - |
106 | | - strlcpy(pathnamecopy, pathname, sizeof(pathnamecopy)); |
107 | | - |
108 | | - /* locate filename */ |
109 | | - name=strrchr(pathnamecopy+1,'/'); |
110 | | - if ( name ) { |
111 | | - *name = 0; |
112 | | - dir = opendir(pathnamecopy); |
113 | | - *name = '/'; |
114 | | - name++; |
115 | | - } |
116 | | - else { |
117 | | - dir = opendir("/"); |
118 | | - name = pathnamecopy+1; |
119 | | - } |
120 | | - if (!dir) { |
121 | | - DEBUGF("Failed opening dir"); |
122 | | - errno = EIO; |
123 | | - file->busy = false; |
124 | | - return -4; |
125 | | - } |
126 | | - |
127 | | - if(name[0] == 0) { |
128 | | - DEBUGF("Empty file name"); |
129 | | - errno = EINVAL; |
130 | | - file->busy = false; |
131 | | - closedir(dir); |
132 | | - return -5; |
133 | | - } |
134 | | - |
135 | | - /* scan dir for name */ |
136 | | - while ((entry = readdir(dir))) { |
137 | | - if ( !strcasecmp(name, entry->d_name) ) { |
138 | | - fat_open(IF_MV2(dir->fatdir.file.volume,) |
139 | | - entry->startcluster, |
140 | | - &(file->fatfile), |
141 | | - &(dir->fatdir)); |
142 | | - file->size = file->trunc ? 0 : entry->size; |
143 | | - file->attr = entry->attribute; |
144 | | - break; |
145 | | - } |
146 | | - } |
147 | | - |
148 | | - if ( !entry ) { |
149 | | - DEBUGF("Didn't find file %s",name); |
150 | | - if ( file->write && (flags & O_CREAT) ) { |
151 | | - rc = fat_create_file(name, |
152 | | - &(file->fatfile), |
153 | | - &(dir->fatdir)); |
154 | | - if (rc < 0) { |
155 | | - DEBUGF("Couldn't create %s in %s",name,pathnamecopy); |
156 | | - errno = EIO; |
157 | | - file->busy = false; |
158 | | - closedir(dir); |
159 | | - return rc * 10 - 6; |
160 | | - } |
161 | | - file->size = 0; |
162 | | - file->attr = 0; |
163 | | - } |
164 | | - else { |
165 | | - DEBUGF("Couldn't find %s in %s",name,pathnamecopy); |
166 | | - errno = ENOENT; |
167 | | - file->busy = false; |
168 | | - closedir(dir); |
169 | | - return -7; |
170 | | - } |
171 | | - } else { |
172 | | - if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) { |
173 | | - errno = EISDIR; |
174 | | - file->busy = false; |
175 | | - closedir(dir); |
176 | | - return -8; |
177 | | - } |
178 | | - } |
179 | | - closedir(dir); |
180 | | - |
181 | | - file->cacheoffset = -1; |
182 | | - file->fileoffset = 0; |
183 | | - |
184 | | - if (file->write && (flags & O_APPEND)) { |
185 | | - rc = lseek(fd,0,SEEK_END); |
186 | | - if (rc < 0 ) |
187 | | - return rc * 10 - 9; |
188 | | - } |
189 | | - |
190 | | - return fd; |
191 | | -} |
192 | | - |
193 | | -int file_open(const char* pathname, int flags) |
194 | | -{ |
195 | | - /* By default, use the dircache if available. */ |
196 | | - return open_internal(pathname, flags, true); |
197 | | -} |
198 | | - |
199 | | -int close(int fd) |
200 | | -{ |
201 | | - struct filedesc* file = &openfiles[fd]; |
202 | | - int rc = 0; |
203 | | - |
204 | | - DEBUGF("close(%d)", fd); |
205 | | - |
206 | | - if (fd < 0 || fd > MAX_OPEN_FILES-1) { |
207 | | - errno = EINVAL; |
208 | | - return -1; |
209 | | - } |
210 | | - if (!file->busy) { |
211 | | - errno = EBADF; |
212 | | - return -2; |
213 | | - } |
214 | | - if (file->write) { |
215 | | - rc = fsync(fd); |
216 | | - if (rc < 0) |
217 | | - return rc * 10 - 3; |
218 | | - } |
219 | | - |
220 | | - file->busy = false; |
221 | | - return 0; |
222 | | -} |
223 | | - |
224 | | -int close_all_of_process(struct scheduler_thread* process) |
225 | | -{ |
226 | | - struct filedesc* pfile = openfiles; |
227 | | - int fd; |
228 | | - int closed = 0; |
229 | | - for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++) |
230 | | - { |
231 | | - if (pfile->process == process) |
232 | | - { |
233 | | - pfile->busy = false; /* mark as available, no further action */ |
234 | | - closed++; |
235 | | - } |
236 | | - } |
237 | | - return closed; /* return how many we did */ |
238 | | -} |
239 | | - |
240 | | -int fsync(int fd) |
241 | | -{ |
242 | | - struct filedesc* file = &openfiles[fd]; |
243 | | - int rc = 0; |
244 | | - |
245 | | - DEBUGF("fsync(%d)", fd); |
246 | | - |
247 | | - if (fd < 0 || fd > MAX_OPEN_FILES-1) { |
248 | | - errno = EINVAL; |
249 | | - return -1; |
250 | | - } |
251 | | - if (!file->busy) { |
252 | | - errno = EBADF; |
253 | | - return -2; |
254 | | - } |
255 | | - if (file->write) { |
256 | | - /* flush sector cache */ |
257 | | - if ( file->dirty ) { |
258 | | - rc = flush_cache(fd); |
259 | | - if (rc < 0) |
260 | | - { |
261 | | - /* when failing, try to close the file anyway */ |
262 | | - fat_closewrite(&(file->fatfile), file->size, file->attr); |
263 | | - return rc * 10 - 3; |
264 | | - } |
265 | | - } |
266 | | - |
267 | | - /* truncate? */ |
268 | | - if (file->trunc) { |
269 | | - rc = ftruncate(fd, file->size); |
270 | | - if (rc < 0) |
271 | | - { |
272 | | - /* when failing, try to close the file anyway */ |
273 | | - fat_closewrite(&(file->fatfile), file->size, file->attr); |
274 | | - return rc * 10 - 4; |
275 | | - } |
276 | | - } |
277 | | - |
278 | | - /* tie up all loose ends */ |
279 | | - rc = fat_closewrite(&(file->fatfile), file->size, file->attr); |
280 | | - if (rc < 0) |
281 | | - return rc * 10 - 5; |
282 | | - } |
283 | | - return 0; |
284 | | -} |
285 | | - |
286 | | -int remove(const char* name) |
287 | | -{ |
288 | | - int rc; |
289 | | - struct filedesc* file; |
290 | | - /* Can't use dircache now, because we need to access the fat structures. */ |
291 | | - int fd = open_internal(name, O_WRONLY, false); |
292 | | - if ( fd < 0 ) |
293 | | - return fd * 10 - 1; |
294 | | - |
295 | | - file = &openfiles[fd]; |
296 | | - rc = fat_remove(&(file->fatfile)); |
297 | | - if ( rc < 0 ) { |
298 | | - DEBUGF("Failed removing file: %d", rc); |
299 | | - errno = EIO; |
300 | | - return rc * 10 - 3; |
301 | | - } |
302 | | - |
303 | | - file->size = 0; |
304 | | - |
305 | | - rc = close(fd); |
306 | | - if (rc<0) |
307 | | - return rc * 10 - 4; |
308 | | - |
309 | | - return 0; |
310 | | -} |
311 | | - |
312 | | -int rename(const char* path, const char* newpath) |
313 | | -{ |
314 | | - int rc, fd; |
315 | | - DIR* dir; |
316 | | - char* nameptr; |
317 | | - char* dirptr; |
318 | | - struct filedesc* file; |
319 | | - char newpath2[MAX_PATH]; |
320 | | - |
321 | | - /* verify new path does not already exist */ |
322 | | - /* If it is a directory, errno == EISDIR if the name exists */ |
323 | | - fd = open(newpath, O_RDONLY); |
324 | | - if ( fd >= 0 || errno == EISDIR) { |
325 | | - close(fd); |
326 | | - errno = EBUSY; |
327 | | - return -1; |
328 | | - } |
329 | | - close(fd); |
330 | | - |
331 | | - fd = open_internal(path, O_RDONLY, false); |
332 | | - if ( fd < 0 ) { |
333 | | - errno = EIO; |
334 | | - return fd * 10 - 2; |
335 | | - } |
336 | | - |
337 | | - /* extract new file name */ |
338 | | - nameptr = strrchr(newpath,'/'); |
339 | | - if (nameptr) |
340 | | - nameptr++; |
341 | | - else { |
342 | | - close(fd); |
343 | | - return - 3; |
344 | | - } |
345 | | - |
346 | | - /* Extract new path */ |
347 | | - strcpy(newpath2, newpath); |
348 | | - |
349 | | - dirptr = strrchr(newpath2,'/'); |
350 | | - if(dirptr) |
351 | | - *dirptr = 0; |
352 | | - else { |
353 | | - close(fd); |
354 | | - return - 4; |
355 | | - } |
356 | | - |
357 | | - dirptr = newpath2; |
358 | | - |
359 | | - if(strlen(dirptr) == 0) { |
360 | | - dirptr = "/"; |
361 | | - } |
362 | | - |
363 | | - dir = opendir(dirptr); |
364 | | - if(!dir) { |
365 | | - close(fd); |
366 | | - return - 5; |
367 | | - } |
368 | | - |
369 | | - file = &openfiles[fd]; |
370 | | - |
371 | | - rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr, |
372 | | - file->size, file->attr); |
373 | | -#ifdef HAVE_MULTIVOLUME |
374 | | - if ( rc == -1) { |
375 | | - close(fd); |
376 | | - closedir(dir); |
377 | | - DEBUGF("Failed renaming file across volumnes: %d", rc); |
378 | | - errno = EXDEV; |
379 | | - return -6; |
380 | | - } |
381 | | -#endif |
382 | | - if ( rc < 0 ) { |
383 | | - close(fd); |
384 | | - closedir(dir); |
385 | | - DEBUGF("Failed renaming file: %d", rc); |
386 | | - errno = EIO; |
387 | | - return rc * 10 - 7; |
388 | | - } |
389 | | - |
390 | | - rc = close(fd); |
391 | | - if (rc<0) { |
392 | | - closedir(dir); |
393 | | - errno = EIO; |
394 | | - return rc * 10 - 8; |
395 | | - } |
396 | | - |
397 | | - rc = closedir(dir); |
398 | | - if (rc<0) { |
399 | | - errno = EIO; |
400 | | - return rc * 10 - 9; |
401 | | - } |
402 | | - |
403 | | - return 0; |
404 | | -} |
405 | | - |
406 | | -int ftruncate(int fd, off_t size) |
407 | | -{ |
408 | | - int rc, sector; |
409 | | - struct filedesc* file = &openfiles[fd]; |
410 | | - |
411 | | - sector = size / SECTOR_SIZE; |
412 | | - if (size % SECTOR_SIZE) |
413 | | - sector++; |
414 | | - |
415 | | - rc = fat_seek(&(file->fatfile), sector); |
416 | | - if (rc < 0) { |
417 | | - errno = EIO; |
418 | | - return rc * 10 - 1; |
419 | | - } |
420 | | - |
421 | | - rc = fat_truncate(&(file->fatfile)); |
422 | | - if (rc < 0) { |
423 | | - errno = EIO; |
424 | | - return rc * 10 - 2; |
425 | | - } |
426 | | - |
427 | | - file->size = size; |
428 | | - |
429 | | - return 0; |
430 | | -} |
431 | | - |
432 | | -static int flush_cache(int fd) |
433 | | -{ |
434 | | - int rc; |
435 | | - struct filedesc* file = &openfiles[fd]; |
436 | | - long sector = file->fileoffset / SECTOR_SIZE; |
437 | | - |
438 | | - DEBUGF("Flushing dirty sector cache"); |
439 | | - |
440 | | - /* make sure we are on correct sector */ |
441 | | - rc = fat_seek(&(file->fatfile), sector); |
442 | | - if ( rc < 0 ) |
443 | | - return rc * 10 - 3; |
444 | | - |
445 | | - rc = fat_readwrite(&(file->fatfile), 1, file->cache, true ); |
446 | | - |
447 | | - if ( rc < 0 ) { |
448 | | - if(file->fatfile.eof) |
449 | | - errno = ENOSPC; |
450 | | - |
451 | | - return rc * 10 - 2; |
452 | | - } |
453 | | - |
454 | | - file->dirty = false; |
455 | | - |
456 | | - return 0; |
457 | | -} |
458 | | - |
459 | | -static int readwrite(int fd, void* buf, long count, bool write) |
460 | | -{ |
461 | | - long sectors; |
462 | | - long nread=0; |
463 | | - struct filedesc* file; |
464 | | - int rc; |
465 | | - |
466 | | - if (fd < 0 || fd > MAX_OPEN_FILES-1) { |
467 | | - errno = EINVAL; |
468 | | - return -1; |
469 | | - } |
470 | | - |
471 | | - file = &openfiles[fd]; |
472 | | - |
473 | | - if ( !file->busy ) { |
474 | | - errno = EBADF; |
475 | | - return -1; |
476 | | - } |
477 | | - |
478 | | - if(file->attr & FAT_ATTR_DIRECTORY) { |
479 | | - errno = EISDIR; |
480 | | - return -1; |
481 | | - } |
482 | | - |
483 | | - DEBUGF( "readwrite(%d,%lx,%ld,%s)", |
484 | | - fd,(long)buf,count,write?"write":"read"); |
485 | | - |
486 | | - /* attempt to read past EOF? */ |
487 | | - if (!write && count > file->size - file->fileoffset) |
488 | | - count = file->size - file->fileoffset; |
489 | | - |
490 | | - /* any head bytes? */ |
491 | | - if ( file->cacheoffset != -1 ) { |
492 | | - int offs = file->cacheoffset; |
493 | | - int headbytes = MIN(count, SECTOR_SIZE - offs); |
494 | | - |
495 | | - if (write) { |
496 | | - memcpy( file->cache + offs, buf, headbytes ); |
497 | | - file->dirty = true; |
498 | | - } |
499 | | - else { |
500 | | - memcpy( buf, file->cache + offs, headbytes ); |
501 | | - } |
502 | | - |
503 | | - if (offs + headbytes == SECTOR_SIZE) { |
504 | | - if (file->dirty) { |
505 | | - rc = flush_cache(fd); |
506 | | - if ( rc < 0 ) { |
507 | | - errno = EIO; |
508 | | - return rc * 10 - 2; |
509 | | - } |
510 | | - } |
511 | | - file->cacheoffset = -1; |
512 | | - } |
513 | | - else { |
514 | | - file->cacheoffset += headbytes; |
515 | | - } |
516 | | - |
517 | | - nread = headbytes; |
518 | | - count -= headbytes; |
519 | | - } |
520 | | - |
521 | | - /* If the buffer has been modified, either it has been flushed already |
522 | | - * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no |
523 | | - * more data to follow in this call). Do NOT flush here. */ |
524 | | - |
525 | | - /* read/write whole sectors right into/from the supplied buffer */ |
526 | | - sectors = count / SECTOR_SIZE; |
527 | | - if ( sectors ) { |
528 | | - rc = fat_readwrite(&(file->fatfile), sectors, |
529 | | - (unsigned char*)buf+nread, write ); |
530 | | - if ( rc < 0 ) { |
531 | | - DEBUGF("Failed read/writing %ld sectors",sectors); |
532 | | - errno = EIO; |
533 | | - if(write && file->fatfile.eof) { |
534 | | - DEBUGF("No space left on device"); |
535 | | - errno = ENOSPC; |
536 | | - } else { |
537 | | - file->fileoffset += nread; |
538 | | - } |
539 | | - file->cacheoffset = -1; |
540 | | - /* adjust file size to length written */ |
541 | | - if ( write && file->fileoffset > file->size ) |
542 | | - { |
543 | | - file->size = file->fileoffset; |
544 | | - } |
545 | | - return nread ? nread : rc * 10 - 4; |
546 | | - } |
547 | | - else { |
548 | | - if ( rc > 0 ) { |
549 | | - nread += rc * SECTOR_SIZE; |
550 | | - count -= sectors * SECTOR_SIZE; |
551 | | - |
552 | | - /* if eof, skip tail bytes */ |
553 | | - if ( rc < sectors ) |
554 | | - count = 0; |
555 | | - } |
556 | | - else { |
557 | | - /* eof */ |
558 | | - count=0; |
559 | | - } |
560 | | - |
561 | | - file->cacheoffset = -1; |
562 | | - } |
563 | | - } |
564 | | - |
565 | | - /* any tail bytes? */ |
566 | | - if ( count ) { |
567 | | - if (write) { |
568 | | - if ( file->fileoffset + nread < file->size ) { |
569 | | - /* sector is only partially filled. copy-back from disk */ |
570 | | - DEBUGF("Copy-back tail cache"); |
571 | | - rc = fat_readwrite(&(file->fatfile), 1, file->cache, false ); |
572 | | - if ( rc < 0 ) { |
573 | | - DEBUGF("Failed writing"); |
574 | | - errno = EIO; |
575 | | - file->fileoffset += nread; |
576 | | - file->cacheoffset = -1; |
577 | | - /* adjust file size to length written */ |
578 | | - if ( file->fileoffset > file->size ) |
579 | | - { |
580 | | - file->size = file->fileoffset; |
581 | | - } |
582 | | - return nread ? nread : rc * 10 - 5; |
583 | | - } |
584 | | - /* seek back one sector to put file position right */ |
585 | | - rc = fat_seek(&(file->fatfile), |
586 | | - (file->fileoffset + nread) / |
587 | | - SECTOR_SIZE); |
588 | | - if ( rc < 0 ) { |
589 | | - DEBUGF("fat_seek() failed"); |
590 | | - errno = EIO; |
591 | | - file->fileoffset += nread; |
592 | | - file->cacheoffset = -1; |
593 | | - /* adjust file size to length written */ |
594 | | - if ( file->fileoffset > file->size ) |
595 | | - { |
596 | | - file->size = file->fileoffset; |
597 | | - } |
598 | | - return nread ? nread : rc * 10 - 6; |
599 | | - } |
600 | | - } |
601 | | - memcpy( file->cache, (unsigned char*)buf + nread, count ); |
602 | | - file->dirty = true; |
603 | | - } |
604 | | - else { |
605 | | - rc = fat_readwrite(&(file->fatfile), 1, file->cache,false); |
606 | | - if (rc < 1 ) { |
607 | | - DEBUGF("Failed caching sector"); |
608 | | - errno = EIO; |
609 | | - file->fileoffset += nread; |
610 | | - file->cacheoffset = -1; |
611 | | - return nread ? nread : rc * 10 - 7; |
612 | | - } |
613 | | - memcpy( (unsigned char*)buf + nread, file->cache, count ); |
614 | | - } |
615 | | - |
616 | | - nread += count; |
617 | | - file->cacheoffset = count; |
618 | | - } |
619 | | - |
620 | | - file->fileoffset += nread; |
621 | | - DEBUGF("fileoffset: %ld", file->fileoffset); |
622 | | - |
623 | | - /* adjust file size to length written */ |
624 | | - if ( write && file->fileoffset > file->size ) |
625 | | - { |
626 | | - file->size = file->fileoffset; |
627 | | - } |
628 | | - |
629 | | - return nread; |
630 | | -} |
631 | | - |
632 | | -ssize_t write(int fd, const void* buf, size_t count) |
633 | | -{ |
634 | | - if (!openfiles[fd].write) { |
635 | | - errno = EACCES; |
636 | | - return -1; |
637 | | - } |
638 | | - return readwrite(fd, (void *)buf, count, true); |
639 | | -} |
640 | | - |
641 | | -ssize_t read(int fd, void* buf, size_t count) |
642 | | -{ |
643 | | - return readwrite(fd, buf, count, false); |
644 | | -} |
645 | | - |
646 | | - |
647 | | -off_t lseek(int fd, off_t offset, int whence) |
648 | | -{ |
649 | | - off_t pos; |
650 | | - long newsector; |
651 | | - long oldsector; |
652 | | - int sectoroffset; |
653 | | - int rc; |
654 | | - struct filedesc* file = &openfiles[fd]; |
655 | | - |
656 | | - DEBUGF("lseek(%d,%ld,%d)",fd,offset,whence); |
657 | | - |
658 | | - if (fd < 0 || fd > MAX_OPEN_FILES-1) { |
659 | | - errno = EINVAL; |
660 | | - return -1; |
661 | | - } |
662 | | - if ( !file->busy ) { |
663 | | - errno = EBADF; |
664 | | - return -1; |
665 | | - } |
666 | | - |
667 | | - switch ( whence ) { |
668 | | - case SEEK_SET: |
669 | | - pos = offset; |
670 | | - break; |
671 | | - |
672 | | - case SEEK_CUR: |
673 | | - pos = file->fileoffset + offset; |
674 | | - break; |
675 | | - |
676 | | - case SEEK_END: |
677 | | - pos = file->size + offset; |
678 | | - break; |
679 | | - |
680 | | - default: |
681 | | - errno = EINVAL; |
682 | | - return -2; |
683 | | - } |
684 | | - if ((pos < 0) || (pos > file->size)) { |
685 | | - errno = EINVAL; |
686 | | - return -3; |
687 | | - } |
688 | | - |
689 | | - /* new sector? */ |
690 | | - newsector = pos / SECTOR_SIZE; |
691 | | - oldsector = file->fileoffset / SECTOR_SIZE; |
692 | | - sectoroffset = pos % SECTOR_SIZE; |
693 | | - |
694 | | - if ( (newsector != oldsector) || |
695 | | - ((file->cacheoffset==-1) && sectoroffset) ) { |
696 | | - |
697 | | - if ( newsector != oldsector ) { |
698 | | - if (file->dirty) { |
699 | | - rc = flush_cache(fd); |
700 | | - if (rc < 0) |
701 | | - return rc * 10 - 5; |
702 | | - } |
703 | | - |
704 | | - rc = fat_seek(&(file->fatfile), newsector); |
705 | | - if ( rc < 0 ) { |
706 | | - errno = EIO; |
707 | | - return rc * 10 - 4; |
708 | | - } |
709 | | - } |
710 | | - if ( sectoroffset ) { |
711 | | - rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false); |
712 | | - if ( rc < 0 ) { |
713 | | - errno = EIO; |
714 | | - return rc * 10 - 6; |
715 | | - } |
716 | | - file->cacheoffset = sectoroffset; |
717 | | - } |
718 | | - else |
719 | | - file->cacheoffset = -1; |
720 | | - } |
721 | | - else |
722 | | - if ( file->cacheoffset != -1 ) |
723 | | - file->cacheoffset = sectoroffset; |
724 | | - |
725 | | - file->fileoffset = pos; |
726 | | - |
727 | | - return pos; |
728 | | -} |
729 | | - |
730 | | -off_t filesize(int fd) |
731 | | -{ |
732 | | - struct filedesc* file = &openfiles[fd]; |
733 | | - |
734 | | - if (fd < 0 || fd > MAX_OPEN_FILES-1) { |
735 | | - errno = EINVAL; |
736 | | - return -1; |
737 | | - } |
738 | | - if ( !file->busy ) { |
739 | | - errno = EBADF; |
740 | | - return -1; |
741 | | - } |
742 | | - |
743 | | - return file->size; |
744 | | -} |
745 | | - |
746 | | - |
747 | | -#ifdef HAVE_HOTSWAP |
748 | | -/* release all file handles on a given volume "by force", to avoid leaks */ |
749 | | -int release_files(int volume) |
750 | | -{ |
751 | | - struct filedesc* pfile = openfiles; |
752 | | - int fd; |
753 | | - int closed = 0; |
754 | | - for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++) |
755 | | - { |
756 | | -#ifdef HAVE_MULTIVOLUME |
757 | | - if (pfile->fatfile.volume == volume) |
758 | | -#else |
759 | | - (void)volume; |
760 | | -#endif |
761 | | - { |
762 | | - pfile->busy = false; /* mark as available, no further action */ |
763 | | - closed++; |
764 | | - } |
765 | | - } |
766 | | - return closed; /* return how many we did */ |
767 | | -} |
768 | | -#endif /* #ifdef HAVE_HOTSWAP */ |
| 40 | +
|
| 41 | +struct filedesc {
|
| 42 | + unsigned char cache[SECTOR_SIZE] CACHEALIGN_ATTR;
|
| 43 | + int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */
|
| 44 | + long fileoffset;
|
| 45 | + long size;
|
| 46 | + int attr;
|
| 47 | + struct fat_file fatfile;
|
| 48 | + bool busy;
|
| 49 | + bool write;
|
| 50 | + bool dirty;
|
| 51 | + bool trunc;
|
| 52 | + struct scheduler_thread* process;
|
| 53 | +} CACHEALIGN_ATTR;
|
| 54 | +
|
| 55 | +static struct filedesc openfiles[MAX_OPEN_FILES] CACHEALIGN_ATTR;
|
| 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 | +
|
| 74 | + DEBUGF("open(\"%s\",%d)",pathname,flags);
|
| 75 | +
|
| 76 | + if ( pathname[0] != '/' ) {
|
| 77 | + DEBUGF("'%s' is not an absolute path.", pathname);
|
| 78 | + DEBUGF("Only absolute pathnames supported at the moment");
|
| 79 | + errno = EINVAL;
|
| 80 | + return -1;
|
| 81 | + }
|
| 82 | +
|
| 83 | + /* find a free file descriptor */
|
| 84 | + for ( fd=0; fd<MAX_OPEN_FILES; fd++ )
|
| 85 | + if ( !openfiles[fd].busy )
|
| 86 | + break;
|
| 87 | +
|
| 88 | + if ( fd == MAX_OPEN_FILES ) {
|
| 89 | + DEBUGF("Too many files open");
|
| 90 | + errno = EMFILE;
|
| 91 | + return -2;
|
| 92 | + }
|
| 93 | +
|
| 94 | + file = &openfiles[fd];
|
| 95 | + memset(file, 0, sizeof(struct filedesc));
|
| 96 | +
|
| 97 | + if (flags & (O_RDWR | O_WRONLY)) {
|
| 98 | + file->write = true;
|
| 99 | +
|
| 100 | + if (flags & O_TRUNC)
|
| 101 | + file->trunc = true;
|
| 102 | + }
|
| 103 | + file->busy = true;
|
| 104 | + file->process = current_thread;
|
| 105 | +
|
| 106 | + strlcpy(pathnamecopy, pathname, sizeof(pathnamecopy));
|
| 107 | +
|
| 108 | + /* locate filename */
|
| 109 | + name=strrchr(pathnamecopy+1,'/');
|
| 110 | + if ( name ) {
|
| 111 | + *name = 0;
|
| 112 | + dir = opendir(pathnamecopy);
|
| 113 | + *name = '/';
|
| 114 | + name++;
|
| 115 | + }
|
| 116 | + else {
|
| 117 | + dir = opendir("/");
|
| 118 | + name = pathnamecopy+1;
|
| 119 | + }
|
| 120 | + if (!dir) {
|
| 121 | + DEBUGF("Failed opening dir");
|
| 122 | + errno = EIO;
|
| 123 | + file->busy = false;
|
| 124 | + return -4;
|
| 125 | + }
|
| 126 | +
|
| 127 | + if(name[0] == 0) {
|
| 128 | + DEBUGF("Empty file name");
|
| 129 | + errno = EINVAL;
|
| 130 | + file->busy = false;
|
| 131 | + closedir(dir);
|
| 132 | + return -5;
|
| 133 | + }
|
| 134 | +
|
| 135 | + /* scan dir for name */
|
| 136 | + while ((entry = readdir(dir))) {
|
| 137 | + if ( !strcasecmp(name, entry->d_name) ) {
|
| 138 | + fat_open(IF_MV2(dir->fatdir.file.volume,)
|
| 139 | + entry->startcluster,
|
| 140 | + &(file->fatfile),
|
| 141 | + &(dir->fatdir));
|
| 142 | + file->size = file->trunc ? 0 : entry->size;
|
| 143 | + file->attr = entry->attribute;
|
| 144 | + break;
|
| 145 | + }
|
| 146 | + }
|
| 147 | +
|
| 148 | + if ( !entry ) {
|
| 149 | + DEBUGF("Didn't find file %s",name);
|
| 150 | + if ( file->write && (flags & O_CREAT) ) {
|
| 151 | + rc = fat_create_file(name,
|
| 152 | + &(file->fatfile),
|
| 153 | + &(dir->fatdir));
|
| 154 | + if (rc < 0) {
|
| 155 | + DEBUGF("Couldn't create %s in %s",name,pathnamecopy);
|
| 156 | + errno = EIO;
|
| 157 | + file->busy = false;
|
| 158 | + closedir(dir);
|
| 159 | + return rc * 10 - 6;
|
| 160 | + }
|
| 161 | + file->size = 0;
|
| 162 | + file->attr = 0;
|
| 163 | + }
|
| 164 | + else {
|
| 165 | + DEBUGF("Couldn't find %s in %s",name,pathnamecopy);
|
| 166 | + errno = ENOENT;
|
| 167 | + file->busy = false;
|
| 168 | + closedir(dir);
|
| 169 | + return -7;
|
| 170 | + }
|
| 171 | + } else {
|
| 172 | + if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
|
| 173 | + errno = EISDIR;
|
| 174 | + file->busy = false;
|
| 175 | + closedir(dir);
|
| 176 | + return -8;
|
| 177 | + }
|
| 178 | + }
|
| 179 | + closedir(dir);
|
| 180 | +
|
| 181 | + file->cacheoffset = -1;
|
| 182 | + file->fileoffset = 0;
|
| 183 | +
|
| 184 | + if (file->write && (flags & O_APPEND)) {
|
| 185 | + rc = lseek(fd,0,SEEK_END);
|
| 186 | + if (rc < 0 )
|
| 187 | + return rc * 10 - 9;
|
| 188 | + }
|
| 189 | +
|
| 190 | + return fd;
|
| 191 | +}
|
| 192 | +
|
| 193 | +int file_open(const char* pathname, int flags)
|
| 194 | +{
|
| 195 | + /* By default, use the dircache if available. */
|
| 196 | + return open_internal(pathname, flags, true);
|
| 197 | +}
|
| 198 | +
|
| 199 | +int close(int fd)
|
| 200 | +{
|
| 201 | + struct filedesc* file = &openfiles[fd];
|
| 202 | + int rc = 0;
|
| 203 | +
|
| 204 | + DEBUGF("close(%d)", fd);
|
| 205 | +
|
| 206 | + if (fd < 0 || fd > MAX_OPEN_FILES-1) {
|
| 207 | + errno = EINVAL;
|
| 208 | + return -1;
|
| 209 | + }
|
| 210 | + if (!file->busy) {
|
| 211 | + errno = EBADF;
|
| 212 | + return -2;
|
| 213 | + }
|
| 214 | + if (file->write) {
|
| 215 | + rc = fsync(fd);
|
| 216 | + if (rc < 0)
|
| 217 | + return rc * 10 - 3;
|
| 218 | + }
|
| 219 | +
|
| 220 | + file->busy = false;
|
| 221 | + return 0;
|
| 222 | +}
|
| 223 | +
|
| 224 | +int close_all_of_process(struct scheduler_thread* process)
|
| 225 | +{
|
| 226 | + struct filedesc* pfile = openfiles;
|
| 227 | + int fd;
|
| 228 | + int closed = 0;
|
| 229 | + for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++)
|
| 230 | + {
|
| 231 | + if (pfile->process == process)
|
| 232 | + {
|
| 233 | + pfile->busy = false; /* mark as available, no further action */
|
| 234 | + closed++;
|
| 235 | + }
|
| 236 | + }
|
| 237 | + return closed; /* return how many we did */
|
| 238 | +}
|
| 239 | +
|
| 240 | +int fsync(int fd)
|
| 241 | +{
|
| 242 | + struct filedesc* file = &openfiles[fd];
|
| 243 | + int rc = 0;
|
| 244 | +
|
| 245 | + DEBUGF("fsync(%d)", fd);
|
| 246 | +
|
| 247 | + if (fd < 0 || fd > MAX_OPEN_FILES-1) {
|
| 248 | + errno = EINVAL;
|
| 249 | + return -1;
|
| 250 | + }
|
| 251 | + if (!file->busy) {
|
| 252 | + errno = EBADF;
|
| 253 | + return -2;
|
| 254 | + }
|
| 255 | + if (file->write) {
|
| 256 | + /* flush sector cache */
|
| 257 | + if ( file->dirty ) {
|
| 258 | + rc = flush_cache(fd);
|
| 259 | + if (rc < 0)
|
| 260 | + {
|
| 261 | + /* when failing, try to close the file anyway */
|
| 262 | + fat_closewrite(&(file->fatfile), file->size, file->attr);
|
| 263 | + return rc * 10 - 3;
|
| 264 | + }
|
| 265 | + }
|
| 266 | +
|
| 267 | + /* truncate? */
|
| 268 | + if (file->trunc) {
|
| 269 | + rc = ftruncate(fd, file->size);
|
| 270 | + if (rc < 0)
|
| 271 | + {
|
| 272 | + /* when failing, try to close the file anyway */
|
| 273 | + fat_closewrite(&(file->fatfile), file->size, file->attr);
|
| 274 | + return rc * 10 - 4;
|
| 275 | + }
|
| 276 | + }
|
| 277 | +
|
| 278 | + /* tie up all loose ends */
|
| 279 | + rc = fat_closewrite(&(file->fatfile), file->size, file->attr);
|
| 280 | + if (rc < 0)
|
| 281 | + return rc * 10 - 5;
|
| 282 | + }
|
| 283 | + return 0;
|
| 284 | +}
|
| 285 | +
|
| 286 | +int remove(const char* name)
|
| 287 | +{
|
| 288 | + int rc;
|
| 289 | + struct filedesc* file;
|
| 290 | + /* Can't use dircache now, because we need to access the fat structures. */
|
| 291 | + int fd = open_internal(name, O_WRONLY, false);
|
| 292 | + if ( fd < 0 )
|
| 293 | + return fd * 10 - 1;
|
| 294 | +
|
| 295 | + file = &openfiles[fd];
|
| 296 | + rc = fat_remove(&(file->fatfile));
|
| 297 | + if ( rc < 0 ) {
|
| 298 | + DEBUGF("Failed removing file: %d", rc);
|
| 299 | + errno = EIO;
|
| 300 | + return rc * 10 - 3;
|
| 301 | + }
|
| 302 | +
|
| 303 | + file->size = 0;
|
| 304 | +
|
| 305 | + rc = close(fd);
|
| 306 | + if (rc<0)
|
| 307 | + return rc * 10 - 4;
|
| 308 | +
|
| 309 | + return 0;
|
| 310 | +}
|
| 311 | +
|
| 312 | +int rename(const char* path, const char* newpath)
|
| 313 | +{
|
| 314 | + int rc, fd;
|
| 315 | + DIR* dir;
|
| 316 | + char* nameptr;
|
| 317 | + char* dirptr;
|
| 318 | + struct filedesc* file;
|
| 319 | + char newpath2[MAX_PATH];
|
| 320 | +
|
| 321 | + /* verify new path does not already exist */
|
| 322 | + /* If it is a directory, errno == EISDIR if the name exists */
|
| 323 | + fd = open(newpath, O_RDONLY);
|
| 324 | + if ( fd >= 0 || errno == EISDIR) {
|
| 325 | + close(fd);
|
| 326 | + errno = EBUSY;
|
| 327 | + return -1;
|
| 328 | + }
|
| 329 | + close(fd);
|
| 330 | +
|
| 331 | + fd = open_internal(path, O_RDONLY, false);
|
| 332 | + if ( fd < 0 ) {
|
| 333 | + errno = EIO;
|
| 334 | + return fd * 10 - 2;
|
| 335 | + }
|
| 336 | +
|
| 337 | + /* extract new file name */
|
| 338 | + nameptr = strrchr(newpath,'/');
|
| 339 | + if (nameptr)
|
| 340 | + nameptr++;
|
| 341 | + else {
|
| 342 | + close(fd);
|
| 343 | + return - 3;
|
| 344 | + }
|
| 345 | +
|
| 346 | + /* Extract new path */
|
| 347 | + strcpy(newpath2, newpath);
|
| 348 | +
|
| 349 | + dirptr = strrchr(newpath2,'/');
|
| 350 | + if(dirptr)
|
| 351 | + *dirptr = 0;
|
| 352 | + else {
|
| 353 | + close(fd);
|
| 354 | + return - 4;
|
| 355 | + }
|
| 356 | +
|
| 357 | + dirptr = newpath2;
|
| 358 | +
|
| 359 | + if(strlen(dirptr) == 0) {
|
| 360 | + dirptr = "/";
|
| 361 | + }
|
| 362 | +
|
| 363 | + dir = opendir(dirptr);
|
| 364 | + if(!dir) {
|
| 365 | + close(fd);
|
| 366 | + return - 5;
|
| 367 | + }
|
| 368 | +
|
| 369 | + file = &openfiles[fd];
|
| 370 | +
|
| 371 | + rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr,
|
| 372 | + file->size, file->attr);
|
| 373 | +#ifdef HAVE_MULTIVOLUME
|
| 374 | + if ( rc == -1) {
|
| 375 | + close(fd);
|
| 376 | + closedir(dir);
|
| 377 | + DEBUGF("Failed renaming file across volumnes: %d", rc);
|
| 378 | + errno = EXDEV;
|
| 379 | + return -6;
|
| 380 | + }
|
| 381 | +#endif
|
| 382 | + if ( rc < 0 ) {
|
| 383 | + close(fd);
|
| 384 | + closedir(dir);
|
| 385 | + DEBUGF("Failed renaming file: %d", rc);
|
| 386 | + errno = EIO;
|
| 387 | + return rc * 10 - 7;
|
| 388 | + }
|
| 389 | +
|
| 390 | + rc = close(fd);
|
| 391 | + if (rc<0) {
|
| 392 | + closedir(dir);
|
| 393 | + errno = EIO;
|
| 394 | + return rc * 10 - 8;
|
| 395 | + }
|
| 396 | +
|
| 397 | + rc = closedir(dir);
|
| 398 | + if (rc<0) {
|
| 399 | + errno = EIO;
|
| 400 | + return rc * 10 - 9;
|
| 401 | + }
|
| 402 | +
|
| 403 | + return 0;
|
| 404 | +}
|
| 405 | +
|
| 406 | +int ftruncate(int fd, off_t size)
|
| 407 | +{
|
| 408 | + int rc, sector;
|
| 409 | + struct filedesc* file = &openfiles[fd];
|
| 410 | +
|
| 411 | + sector = size / SECTOR_SIZE;
|
| 412 | + if (size % SECTOR_SIZE)
|
| 413 | + sector++;
|
| 414 | +
|
| 415 | + rc = fat_seek(&(file->fatfile), sector);
|
| 416 | + if (rc < 0) {
|
| 417 | + errno = EIO;
|
| 418 | + return rc * 10 - 1;
|
| 419 | + }
|
| 420 | +
|
| 421 | + rc = fat_truncate(&(file->fatfile));
|
| 422 | + if (rc < 0) {
|
| 423 | + errno = EIO;
|
| 424 | + return rc * 10 - 2;
|
| 425 | + }
|
| 426 | +
|
| 427 | + file->size = size;
|
| 428 | +
|
| 429 | + return 0;
|
| 430 | +}
|
| 431 | +
|
| 432 | +static int flush_cache(int fd)
|
| 433 | +{
|
| 434 | + int rc;
|
| 435 | + struct filedesc* file = &openfiles[fd];
|
| 436 | + long sector = file->fileoffset / SECTOR_SIZE;
|
| 437 | +
|
| 438 | + DEBUGF("Flushing dirty sector cache");
|
| 439 | +
|
| 440 | + /* make sure we are on correct sector */
|
| 441 | + rc = fat_seek(&(file->fatfile), sector);
|
| 442 | + if ( rc < 0 )
|
| 443 | + return rc * 10 - 3;
|
| 444 | +
|
| 445 | + rc = fat_readwrite(&(file->fatfile), 1, file->cache, true );
|
| 446 | +
|
| 447 | + if ( rc < 0 ) {
|
| 448 | + if(file->fatfile.eof)
|
| 449 | + errno = ENOSPC;
|
| 450 | +
|
| 451 | + return rc * 10 - 2;
|
| 452 | + }
|
| 453 | +
|
| 454 | + file->dirty = false;
|
| 455 | +
|
| 456 | + return 0;
|
| 457 | +}
|
| 458 | +
|
| 459 | +static int readwrite(int fd, void* buf, long count, bool write)
|
| 460 | +{
|
| 461 | + long sectors;
|
| 462 | + long nread=0;
|
| 463 | + struct filedesc* file;
|
| 464 | + int rc;
|
| 465 | +
|
| 466 | + if (fd < 0 || fd > MAX_OPEN_FILES-1) {
|
| 467 | + errno = EINVAL;
|
| 468 | + return -1;
|
| 469 | + }
|
| 470 | +
|
| 471 | + file = &openfiles[fd];
|
| 472 | +
|
| 473 | + if ( !file->busy ) {
|
| 474 | + errno = EBADF;
|
| 475 | + return -1;
|
| 476 | + }
|
| 477 | +
|
| 478 | + if(file->attr & FAT_ATTR_DIRECTORY) {
|
| 479 | + errno = EISDIR;
|
| 480 | + return -1;
|
| 481 | + }
|
| 482 | +
|
| 483 | + DEBUGF( "readwrite(%d,%lx,%ld,%s)",
|
| 484 | + fd,(long)buf,count,write?"write":"read");
|
| 485 | +
|
| 486 | + /* attempt to read past EOF? */
|
| 487 | + if (!write && count > file->size - file->fileoffset)
|
| 488 | + count = file->size - file->fileoffset;
|
| 489 | +
|
| 490 | + /* any head bytes? */
|
| 491 | + if ( file->cacheoffset != -1 ) {
|
| 492 | + int offs = file->cacheoffset;
|
| 493 | + int headbytes = MIN(count, SECTOR_SIZE - offs);
|
| 494 | +
|
| 495 | + if (write) {
|
| 496 | + memcpy( file->cache + offs, buf, headbytes );
|
| 497 | + file->dirty = true;
|
| 498 | + }
|
| 499 | + else {
|
| 500 | + memcpy( buf, file->cache + offs, headbytes );
|
| 501 | + }
|
| 502 | +
|
| 503 | + if (offs + headbytes == SECTOR_SIZE) {
|
| 504 | + if (file->dirty) {
|
| 505 | + rc = flush_cache(fd);
|
| 506 | + if ( rc < 0 ) {
|
| 507 | + errno = EIO;
|
| 508 | + return rc * 10 - 2;
|
| 509 | + }
|
| 510 | + }
|
| 511 | + file->cacheoffset = -1;
|
| 512 | + }
|
| 513 | + else {
|
| 514 | + file->cacheoffset += headbytes;
|
| 515 | + }
|
| 516 | +
|
| 517 | + nread = headbytes;
|
| 518 | + count -= headbytes;
|
| 519 | + }
|
| 520 | +
|
| 521 | + /* If the buffer has been modified, either it has been flushed already
|
| 522 | + * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no
|
| 523 | + * more data to follow in this call). Do NOT flush here. */
|
| 524 | +
|
| 525 | + /* read/write whole sectors right into/from the supplied buffer */
|
| 526 | + sectors = count / SECTOR_SIZE;
|
| 527 | + if ( sectors ) {
|
| 528 | + if (buf+nread & (CACHEALIGN_SIZE - 1))
|
| 529 | + {
|
| 530 | + if (write) memcpy(file->cache, buf+nread, SECTOR_SIZE);
|
| 531 | + rc = fat_readwrite(&(file->fatfile), sectors, file->cache, write );
|
| 532 | + if (!write) memcpy(buf+nread, file->cache, SECTOR_SIZE);
|
| 533 | + }
|
| 534 | + else rc = fat_readwrite(&(file->fatfile), sectors, (unsigned char*)buf+nread, write );
|
| 535 | + if ( rc < 0 ) {
|
| 536 | + DEBUGF("Failed read/writing %ld sectors",sectors);
|
| 537 | + errno = EIO;
|
| 538 | + if(write && file->fatfile.eof) {
|
| 539 | + DEBUGF("No space left on device");
|
| 540 | + errno = ENOSPC;
|
| 541 | + } else {
|
| 542 | + file->fileoffset += nread;
|
| 543 | + }
|
| 544 | + file->cacheoffset = -1;
|
| 545 | + /* adjust file size to length written */
|
| 546 | + if ( write && file->fileoffset > file->size )
|
| 547 | + {
|
| 548 | + file->size = file->fileoffset;
|
| 549 | + }
|
| 550 | + return nread ? nread : rc * 10 - 4;
|
| 551 | + }
|
| 552 | + else {
|
| 553 | + if ( rc > 0 ) {
|
| 554 | + nread += rc * SECTOR_SIZE;
|
| 555 | + count -= sectors * SECTOR_SIZE;
|
| 556 | +
|
| 557 | + /* if eof, skip tail bytes */
|
| 558 | + if ( rc < sectors )
|
| 559 | + count = 0;
|
| 560 | + }
|
| 561 | + else {
|
| 562 | + /* eof */
|
| 563 | + count=0;
|
| 564 | + }
|
| 565 | +
|
| 566 | + file->cacheoffset = -1;
|
| 567 | + }
|
| 568 | + }
|
| 569 | +
|
| 570 | + /* any tail bytes? */
|
| 571 | + if ( count ) {
|
| 572 | + if (write) {
|
| 573 | + if ( file->fileoffset + nread < file->size ) {
|
| 574 | + /* sector is only partially filled. copy-back from disk */
|
| 575 | + DEBUGF("Copy-back tail cache");
|
| 576 | + rc = fat_readwrite(&(file->fatfile), 1, file->cache, false );
|
| 577 | + if ( rc < 0 ) {
|
| 578 | + DEBUGF("Failed writing");
|
| 579 | + errno = EIO;
|
| 580 | + file->fileoffset += nread;
|
| 581 | + file->cacheoffset = -1;
|
| 582 | + /* adjust file size to length written */
|
| 583 | + if ( file->fileoffset > file->size )
|
| 584 | + {
|
| 585 | + file->size = file->fileoffset;
|
| 586 | + }
|
| 587 | + return nread ? nread : rc * 10 - 5;
|
| 588 | + }
|
| 589 | + /* seek back one sector to put file position right */
|
| 590 | + rc = fat_seek(&(file->fatfile),
|
| 591 | + (file->fileoffset + nread) /
|
| 592 | + SECTOR_SIZE);
|
| 593 | + if ( rc < 0 ) {
|
| 594 | + DEBUGF("fat_seek() failed");
|
| 595 | + errno = EIO;
|
| 596 | + file->fileoffset += nread;
|
| 597 | + file->cacheoffset = -1;
|
| 598 | + /* adjust file size to length written */
|
| 599 | + if ( file->fileoffset > file->size )
|
| 600 | + {
|
| 601 | + file->size = file->fileoffset;
|
| 602 | + }
|
| 603 | + return nread ? nread : rc * 10 - 6;
|
| 604 | + }
|
| 605 | + }
|
| 606 | + memcpy( file->cache, (unsigned char*)buf + nread, count );
|
| 607 | + file->dirty = true;
|
| 608 | + }
|
| 609 | + else {
|
| 610 | + rc = fat_readwrite(&(file->fatfile), 1, file->cache,false);
|
| 611 | + if (rc < 1 ) {
|
| 612 | + DEBUGF("Failed caching sector");
|
| 613 | + errno = EIO;
|
| 614 | + file->fileoffset += nread;
|
| 615 | + file->cacheoffset = -1;
|
| 616 | + return nread ? nread : rc * 10 - 7;
|
| 617 | + }
|
| 618 | + memcpy( (unsigned char*)buf + nread, file->cache, count );
|
| 619 | + }
|
| 620 | +
|
| 621 | + nread += count;
|
| 622 | + file->cacheoffset = count;
|
| 623 | + }
|
| 624 | +
|
| 625 | + file->fileoffset += nread;
|
| 626 | + DEBUGF("fileoffset: %ld", file->fileoffset);
|
| 627 | +
|
| 628 | + /* adjust file size to length written */
|
| 629 | + if ( write && file->fileoffset > file->size )
|
| 630 | + {
|
| 631 | + file->size = file->fileoffset;
|
| 632 | + }
|
| 633 | +
|
| 634 | + return nread;
|
| 635 | +}
|
| 636 | +
|
| 637 | +ssize_t write(int fd, const void* buf, size_t count)
|
| 638 | +{
|
| 639 | + if (!openfiles[fd].write) {
|
| 640 | + errno = EACCES;
|
| 641 | + return -1;
|
| 642 | + }
|
| 643 | + return readwrite(fd, (void *)buf, count, true);
|
| 644 | +}
|
| 645 | +
|
| 646 | +ssize_t read(int fd, void* buf, size_t count)
|
| 647 | +{
|
| 648 | + return readwrite(fd, buf, count, false);
|
| 649 | +}
|
| 650 | +
|
| 651 | +
|
| 652 | +off_t lseek(int fd, off_t offset, int whence)
|
| 653 | +{
|
| 654 | + off_t pos;
|
| 655 | + long newsector;
|
| 656 | + long oldsector;
|
| 657 | + int sectoroffset;
|
| 658 | + int rc;
|
| 659 | + struct filedesc* file = &openfiles[fd];
|
| 660 | +
|
| 661 | + DEBUGF("lseek(%d,%ld,%d)",fd,offset,whence);
|
| 662 | +
|
| 663 | + if (fd < 0 || fd > MAX_OPEN_FILES-1) {
|
| 664 | + errno = EINVAL;
|
| 665 | + return -1;
|
| 666 | + }
|
| 667 | + if ( !file->busy ) {
|
| 668 | + errno = EBADF;
|
| 669 | + return -1;
|
| 670 | + }
|
| 671 | +
|
| 672 | + switch ( whence ) {
|
| 673 | + case SEEK_SET:
|
| 674 | + pos = offset;
|
| 675 | + break;
|
| 676 | +
|
| 677 | + case SEEK_CUR:
|
| 678 | + pos = file->fileoffset + offset;
|
| 679 | + break;
|
| 680 | +
|
| 681 | + case SEEK_END:
|
| 682 | + pos = file->size + offset;
|
| 683 | + break;
|
| 684 | +
|
| 685 | + default:
|
| 686 | + errno = EINVAL;
|
| 687 | + return -2;
|
| 688 | + }
|
| 689 | + if ((pos < 0) || (pos > file->size)) {
|
| 690 | + errno = EINVAL;
|
| 691 | + return -3;
|
| 692 | + }
|
| 693 | +
|
| 694 | + /* new sector? */
|
| 695 | + newsector = pos / SECTOR_SIZE;
|
| 696 | + oldsector = file->fileoffset / SECTOR_SIZE;
|
| 697 | + sectoroffset = pos % SECTOR_SIZE;
|
| 698 | +
|
| 699 | + if ( (newsector != oldsector) ||
|
| 700 | + ((file->cacheoffset==-1) && sectoroffset) ) {
|
| 701 | +
|
| 702 | + if ( newsector != oldsector ) {
|
| 703 | + if (file->dirty) {
|
| 704 | + rc = flush_cache(fd);
|
| 705 | + if (rc < 0)
|
| 706 | + return rc * 10 - 5;
|
| 707 | + }
|
| 708 | +
|
| 709 | + rc = fat_seek(&(file->fatfile), newsector);
|
| 710 | + if ( rc < 0 ) {
|
| 711 | + errno = EIO;
|
| 712 | + return rc * 10 - 4;
|
| 713 | + }
|
| 714 | + }
|
| 715 | + if ( sectoroffset ) {
|
| 716 | + rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false);
|
| 717 | + if ( rc < 0 ) {
|
| 718 | + errno = EIO;
|
| 719 | + return rc * 10 - 6;
|
| 720 | + }
|
| 721 | + file->cacheoffset = sectoroffset;
|
| 722 | + }
|
| 723 | + else
|
| 724 | + file->cacheoffset = -1;
|
| 725 | + }
|
| 726 | + else
|
| 727 | + if ( file->cacheoffset != -1 )
|
| 728 | + file->cacheoffset = sectoroffset;
|
| 729 | +
|
| 730 | + file->fileoffset = pos;
|
| 731 | +
|
| 732 | + return pos;
|
| 733 | +}
|
| 734 | +
|
| 735 | +off_t filesize(int fd)
|
| 736 | +{
|
| 737 | + struct filedesc* file = &openfiles[fd];
|
| 738 | +
|
| 739 | + if (fd < 0 || fd > MAX_OPEN_FILES-1) {
|
| 740 | + errno = EINVAL;
|
| 741 | + return -1;
|
| 742 | + }
|
| 743 | + if ( !file->busy ) {
|
| 744 | + errno = EBADF;
|
| 745 | + return -1;
|
| 746 | + }
|
| 747 | +
|
| 748 | + return file->size;
|
| 749 | +}
|
| 750 | +
|
| 751 | +
|
| 752 | +#ifdef HAVE_HOTSWAP
|
| 753 | +/* release all file handles on a given volume "by force", to avoid leaks */
|
| 754 | +int release_files(int volume)
|
| 755 | +{
|
| 756 | + struct filedesc* pfile = openfiles;
|
| 757 | + int fd;
|
| 758 | + int closed = 0;
|
| 759 | + for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++)
|
| 760 | + {
|
| 761 | +#ifdef HAVE_MULTIVOLUME
|
| 762 | + if (pfile->fatfile.volume == volume)
|
| 763 | +#else
|
| 764 | + (void)volume;
|
| 765 | +#endif
|
| 766 | + {
|
| 767 | + pfile->busy = false; /* mark as available, no further action */
|
| 768 | + closed++;
|
| 769 | + }
|
| 770 | + }
|
| 771 | + return closed; /* return how many we did */
|
| 772 | +}
|
| 773 | +#endif /* #ifdef HAVE_HOTSWAP */
|