summaryrefslogtreecommitdiff
path: root/firmware/common/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/common/file.c')
-rw-r--r--firmware/common/file.c1637
1 files changed, 1010 insertions, 627 deletions
diff --git a/firmware/common/file.c b/firmware/common/file.c
index 920eada84e..7d3b5092ae 100644
--- a/firmware/common/file.c
+++ b/firmware/common/file.c
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2002 by Björn Stenberg 10 * Copyright (C) 2002 by Björn Stenberg
11 * Copyright (C) 2014 by Michael Sevakis
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -18,819 +19,1201 @@
18 * KIND, either express or implied. 19 * KIND, either express or implied.
19 * 20 *
20 ****************************************************************************/ 21 ****************************************************************************/
22#define RB_FILESYSTEM_OS
23#include "config.h"
24#include "system.h"
21#include <string.h> 25#include <string.h>
22#include <errno.h> 26#include <errno.h>
23#include <stdbool.h>
24#include "file.h"
25#include "fat.h"
26#include "dir_uncached.h"
27#include "debug.h" 27#include "debug.h"
28#include "dircache.h" 28#include "file.h"
29#include "filefuncs.h" 29#include "fileobj_mgr.h"
30#include "system.h" 30#include "disk_cache.h"
31#include "dircache_redirect.h"
32#include "string-extra.h"
33
34/**
35 * These functions provide a roughly POSIX-compatible file I/O API.
36 */
37
38/* structure used for open file descriptors */
39static struct filestr_desc
40{
41 struct filestr_base stream; /* basic stream info (first!) */
42 file_size_t offset; /* current offset for stream */
43 file_size_t *sizep; /* shortcut to file size in fileobj */
44} open_streams[MAX_OPEN_FILES];
31 45
32/* 46/* check and return a struct filestr_desc* from a file descriptor number */
33 These functions provide a roughly POSIX-compatible file IO API. 47static struct filestr_desc * get_filestr(int fildes)
48{
49 struct filestr_desc *file = &open_streams[fildes];
34 50
35 Since the fat32 driver only manages sectors, we maintain a one-sector 51 if ((unsigned int)fildes >= MAX_OPEN_FILES)
36 cache for each open file. This way we can provide byte access without 52 file = NULL;
37 having to re-read the sector each time. 53 else if (file->stream.flags & FDO_BUSY)
38 The penalty is the RAM used for the cache and slightly more complex code. 54 return file;
39*/
40 55
41struct filedesc { 56 DEBUGF("fildes %d: bad file number\n", fildes);
42 unsigned char cache[SECTOR_SIZE] CACHEALIGN_ATTR; 57 errno = (file && file->stream.flags == FV_NONEXIST) ? ENXIO : EBADF;
43 int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */ 58 return NULL;
44 long fileoffset; 59}
45 long size;
46 int attr;
47 struct fat_file fatfile;
48 bool busy;
49 bool write;
50 bool dirty;
51 bool trunc;
52} CACHEALIGN_ATTR;
53 60
54static struct filedesc openfiles[MAX_OPEN_FILES] CACHEALIGN_ATTR; 61#define GET_FILESTR(type, fildes) \
62 ({ \
63 file_internal_lock_##type(); \
64 struct filestr_desc * _file = get_filestr(fildes); \
65 if (_file) \
66 FILESTR_LOCK(type, &_file->stream); \
67 else \
68 file_internal_unlock_##type(); \
69 _file; \
70 })
71
72/* release the lock on the filestr_desc* */
73#define RELEASE_FILESTR(type, file) \
74 ({ \
75 FILESTR_UNLOCK(type, &(file)->stream); \
76 file_internal_unlock_##type(); \
77 })
78
79/* find a free file descriptor */
80static int alloc_filestr(struct filestr_desc **filep)
81{
82 for (int fildes = 0; fildes < MAX_OPEN_FILES; fildes++)
83 {
84 struct filestr_desc *file = &open_streams[fildes];
85 if (!file->stream.flags)
86 {
87 *filep = file;
88 return fildes;
89 }
90 }
55 91
56static int flush_cache(int fd); 92 DEBUGF("Too many files open\n");
93 return -1;
94}
57 95
58int file_creat(const char *pathname) 96/* return the file size in sectors */
97static inline unsigned long filesize_sectors(file_size_t size)
59{ 98{
60 return open(pathname, O_WRONLY|O_CREAT|O_TRUNC, 0666); 99 /* overflow proof whereas "(x + y - 1) / y" is not */
100 unsigned long numsectors = size / SECTOR_SIZE;
101
102 if (size % SECTOR_SIZE)
103 numsectors++;
104
105 return numsectors;
61} 106}
62 107
63static int open_internal(const char* pathname, int flags, bool use_cache) 108/* flush a dirty cache buffer */
109static int flush_cache(struct filestr_desc *file)
64{ 110{
65 DIR_UNCACHED* dir;
66 struct dirent_uncached* entry;
67 int fd;
68 int pathnamesize = strlen(pathname) + 1;
69 char pathnamecopy[pathnamesize];
70 char* name;
71 struct filedesc* file = NULL;
72 int rc; 111 int rc;
73#ifndef HAVE_DIRCACHE 112 struct filestr_cache *cachep = file->stream.cachep;
74 (void)use_cache;
75#endif
76 113
77 LDEBUGF("open(\"%s\",%d)\n",pathname,flags); 114 DEBUGF("Flushing dirty sector cache (%lu)\n", cachep->sector);
78 115
79 if ( pathname[0] != '/' ) { 116 if (fat_query_sectornum(&file->stream.fatstr) != cachep->sector)
80 DEBUGF("'%s' is not an absolute path.\n",pathname); 117 {
81 DEBUGF("Only absolute pathnames supported at the moment\n"); 118 /* get on the correct sector */
82 errno = EINVAL; 119 rc = fat_seek(&file->stream.fatstr, cachep->sector);
83 return -1; 120 if (rc < 0)
121 FILE_ERROR(EIO, rc * 10 - 1);
84 } 122 }
85 123
86 /* find a free file descriptor */ 124 rc = fat_readwrite(&file->stream.fatstr, 1, cachep->buffer, true);
87 for ( fd=0; fd<MAX_OPEN_FILES; fd++ ) 125 if (rc < 0)
88 if ( !openfiles[fd].busy ) 126 {
89 break; 127 if (rc == FAT_RC_ENOSPC)
90 128 FILE_ERROR(ENOSPC, RC);
91 if ( fd == MAX_OPEN_FILES ) { 129 else
92 DEBUGF("Too many files open\n"); 130 FILE_ERROR(EIO, rc * 10 - 2);
93 errno = EMFILE;
94 return -2;
95 } 131 }
96 132
97 file = &openfiles[fd]; 133 cachep->flags = 0;
98 memset(file, 0, sizeof(struct filedesc)); 134 return 1;
135file_error:
136 DEBUGF("Failed flushing cache: %d\n", rc);
137 return rc;
138}
139
140static void discard_cache(struct filestr_desc *file)
141{
142 struct filestr_cache *const cachep = file->stream.cachep;
143 cachep->flags = 0;
144}
99 145
100 if (flags & (O_RDWR | O_WRONLY)) { 146/* set the file pointer */
101 file->write = true; 147static off_t lseek_internal(struct filestr_desc *file, off_t offset,
148 int whence)
149{
150 off_t rc;
151 file_size_t pos;
102 152
103 if (flags & O_TRUNC) 153 file_size_t size = MIN(*file->sizep, FILE_SIZE_MAX);
104 file->trunc = true;
105 }
106 file->busy = true;
107 154
108#ifdef HAVE_DIRCACHE 155 switch (whence)
109 if (dircache_is_enabled() && !file->write && use_cache)
110 { 156 {
111# ifdef HAVE_MULTIVOLUME 157 case SEEK_SET:
112 int volume = strip_volume(pathname, pathnamecopy); 158 if (offset < 0 || (file_size_t)offset > size)
113# endif 159 FILE_ERROR(EINVAL, -1);
114 160
115 int ce = dircache_get_entry_id(pathname); 161 pos = offset;
116 if (ce < 0) 162 break;
117 {
118 errno = ENOENT;
119 file->busy = false;
120 return -7;
121 }
122 163
123 long startcluster = _dircache_get_entry_startcluster(ce); 164 case SEEK_CUR:
124 fat_open(IF_MV(volume,) 165 if ((offset < 0 && (file_size_t)-offset > file->offset) ||
125 startcluster, 166 (offset > 0 && (file_size_t)offset > size - file->offset))
126 &(file->fatfile), 167 FILE_ERROR(EINVAL, -1);
127 NULL);
128 struct dirinfo *info = _dircache_get_entry_dirinfo(ce);
129 file->size = info->size;
130 file->attr = info->attribute;
131 file->cacheoffset = -1;
132 file->fileoffset = 0;
133 168
134 return fd; 169 pos = file->offset + offset;
135 } 170 break;
136#endif
137 171
138 strlcpy(pathnamecopy, pathname, pathnamesize); 172 case SEEK_END:
173 if (offset > 0 || (file_size_t)-offset > size)
174 FILE_ERROR(EINVAL, -1);
139 175
140 /* locate filename */ 176 pos = size + offset;
141 name=strrchr(pathnamecopy+1,'/'); 177 break;
142 if ( name ) { 178
143 *name = 0; 179 default:
144 dir = opendir_uncached(pathnamecopy); 180 FILE_ERROR(EINVAL, -1);
145 *name = '/';
146 name++;
147 }
148 else {
149 dir = opendir_uncached("/");
150 name = pathnamecopy+1;
151 }
152 if (!dir) {
153 DEBUGF("Failed opening dir\n");
154 errno = EIO;
155 file->busy = false;
156 return -4;
157 }
158
159 if(name[0] == 0) {
160 DEBUGF("Empty file name\n");
161 errno = EINVAL;
162 file->busy = false;
163 closedir_uncached(dir);
164 return -5;
165 }
166
167 /* scan dir for name */
168 while ((entry = readdir_uncached(dir))) {
169 if ( !strcasecmp(name, entry->d_name) ) {
170 fat_open(IF_MV(dir->fatdir.file.volume,)
171 entry->startcluster,
172 &(file->fatfile),
173 &(dir->fatdir));
174 file->size = file->trunc ? 0 : entry->info.size;
175 file->attr = entry->info.attribute;
176 break;
177 }
178 } 181 }
179 182
180 if ( !entry ) { 183 file->offset = pos;
181 LDEBUGF("Didn't find file %s\n",name); 184
182 if ( file->write && (flags & O_CREAT) ) { 185 return pos;
183 rc = fat_create_file(name, 186file_error:
184 &(file->fatfile), 187 return rc;
185 &(dir->fatdir)); 188}
186 if (rc < 0) { 189
187 DEBUGF("Couldn't create %s in %s\n",name,pathnamecopy); 190/* callback for each file stream to make sure all data is in sync with new
188 errno = EIO; 191 size */
189 file->busy = false; 192void ftruncate_internal_callback(struct filestr_base *stream,
190 closedir_uncached(dir); 193 struct filestr_base *s)
191 return rc * 10 - 6; 194{
195 struct filestr_desc *file = (struct filestr_desc *)s;
196 file_size_t size = *file->sizep;
197
198 /* caches with data beyond new extents are invalid */
199 unsigned long sector = file->stream.cachep->sector;
200 if (sector != INVALID_SECNUM && sector >= filesize_sectors(size))
201 filestr_discard_cache(&file->stream);
202
203 /* keep all positions within bounds */
204 if (file->offset > size)
205 file->offset = size;
206
207 (void)stream;
208}
209
210/* truncate the file to the specified length */
211static int ftruncate_internal(struct filestr_desc *file, file_size_t size,
212 bool write_now)
213{
214 int rc = 0, rc2 = 1;
215
216 file_size_t cursize = *file->sizep;
217 file_size_t truncsize = MIN(size, cursize);
218
219 if (write_now)
220 {
221 unsigned long sector = filesize_sectors(truncsize);
222 struct filestr_cache *const cachep = file->stream.cachep;
223
224 if (cachep->flags == (FSC_NEW|FSC_DIRTY) &&
225 cachep->sector + 1 == sector)
226 {
227 /* sector created but may have never been added to the cluster
228 chain; flush it now or the subsequent may fail */
229 rc2 = flush_cache(file);
230 if (rc2 == FAT_RC_ENOSPC)
231 {
232 /* no space left on device; further truncation needed */
233 discard_cache(file);
234 truncsize = ALIGN_DOWN(truncsize - 1, SECTOR_SIZE);
235 sector--;
236 rc = rc2;
192 } 237 }
193#ifdef HAVE_DIRCACHE 238 else if (rc2 < 0)
194 dircache_add_file(pathname, file->fatfile.firstcluster); 239 FILE_ERROR(ERRNO, rc2 * 10 - 1);
195#endif
196 file->size = 0;
197 file->attr = 0;
198 }
199 else {
200 DEBUGF("Couldn't find %s in %s\n",name,pathnamecopy);
201 errno = ENOENT;
202 file->busy = false;
203 closedir_uncached(dir);
204 return -7;
205 }
206 } else {
207 if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
208 errno = EISDIR;
209 file->busy = false;
210 closedir_uncached(dir);
211 return -8;
212 } 240 }
213 }
214 closedir_uncached(dir);
215 241
216 file->cacheoffset = -1; 242 rc2 = fat_seek(&file->stream.fatstr, sector);
217 file->fileoffset = 0; 243 if (rc2 < 0)
244 FILE_ERROR(EIO, rc2 * 10 - 2);
218 245
219 if (file->write && (flags & O_APPEND)) { 246 rc2 = fat_truncate(&file->stream.fatstr);
220 rc = lseek(fd,0,SEEK_END); 247 if (rc2 < 0)
221 if (rc < 0 ) 248 FILE_ERROR(EIO, rc2 * 10 - 3);
222 return rc * 10 - 9;
223 } 249 }
250 /* else just change the cached file size */
224 251
225#ifdef HAVE_DIRCACHE 252 if (truncsize < cursize)
226 if (file->write) 253 {
227 dircache_bind(fd, pathname); 254 *file->sizep = truncsize;
228#endif 255 fileop_ontruncate_internal(&file->stream);
256 }
229 257
230 return fd; 258 /* if truncation was partially successful, it effectively destroyed
231} 259 everything after the truncation point; still, indicate failure
260 after adjusting size */
261 if (rc2 == 0)
262 FILE_ERROR(EIO, -4);
263 else if (rc2 < 0)
264 FILE_ERROR(ERRNO, rc2);
232 265
233int file_open(const char* pathname, int flags) 266file_error:
234{ 267 return rc;
235 /* By default, use the dircache if available. */
236 return open_internal(pathname, flags, true);
237} 268}
238 269
239int close(int fd) 270/* flush back all outstanding writes to the file */
271static int fsync_internal(struct filestr_desc *file)
240{ 272{
241 struct filedesc* file = &openfiles[fd]; 273 /* call only when holding WRITER lock (updates directory entries) */
242 int rc = 0; 274 int rc = 0;
243 275
244 LDEBUGF("close(%d)\n", fd); 276 file_size_t size = *file->sizep;
277 unsigned int foflags = fileobj_get_flags(&file->stream);
278
279 /* flush sector cache? */
280 struct filestr_cache *const cachep = file->stream.cachep;
281 if (cachep->flags & FSC_DIRTY)
282 {
283 int rc2 = flush_cache(file);
284 if (rc2 == FAT_RC_ENOSPC && (cachep->flags & FSC_NEW))
285 {
286 /* no space left on device so this must be dropped */
287 discard_cache(file);
288 size = ALIGN_DOWN(size - 1, SECTOR_SIZE);
289 foflags |= FO_TRUNC;
290 rc = rc2;
291 }
292 else if (rc2 < 0)
293 FILE_ERROR(ERRNO, rc2 * 10 - 1);
294 }
295
296 /* truncate? */
297 if (foflags & FO_TRUNC)
298 {
299 int rc2 = ftruncate_internal(file, size, rc == 0);
300 if (rc2 < 0)
301 FILE_ERROR(ERRNO, rc2 * 10 - 2);
245 302
246 if (fd < 0 || fd > MAX_OPEN_FILES-1) { 303 /* never needs to be done this way again since any data beyond the
247 errno = EINVAL; 304 cached size is now gone */
248 return -1; 305 fileobj_change_flags(&file->stream, 0, FO_TRUNC);
249 } 306 }
250 if (!file->busy) { 307
251 errno = EBADF; 308file_error:;
252 return -2; 309 /* tie up all loose ends (try to close the file even if failing) */
310 int rc2 = fat_closewrite(&file->stream.fatstr, size,
311 get_dir_fatent_dircache());
312 if (rc2 >= 0)
313 fileop_onsync_internal(&file->stream); /* dir_fatent is implicit arg */
314
315 if (rc2 < 0 && rc >= 0)
316 {
317 errno = EIO;
318 rc = rc2 * 10 - 3;
253 } 319 }
254 if (file->write) { 320
255 rc = fsync(fd); 321 return rc;
322}
323
324/* finish with the file and free resources */
325static int close_internal(struct filestr_desc *file)
326{
327 /* call only when holding WRITER lock (updates directory entries) */
328 int rc;
329
330 if ((file->stream.flags & FD_WRITE) &&
331 !(fileobj_get_flags(&file->stream) & FO_REMOVED))
332 {
333 rc = fsync_internal(file);
256 if (rc < 0) 334 if (rc < 0)
257 return rc * 10 - 3; 335 FILE_ERROR(ERRNO, rc * 10 - 1);
258#ifdef HAVE_DIRCACHE
259 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
260 dircache_update_filetime(fd);
261#endif
262 } 336 }
263 337
264 file->busy = false; 338 rc = 0;
265 return 0; 339file_error:;
340 int rc2 = close_stream_internal(&file->stream);
341 if (rc2 < 0 && rc >= 0)
342 rc = rc2 * 10 - 2;
343 return rc;
266} 344}
267 345
268int fsync(int fd) 346/* actually do the open gruntwork */
347static int open_internal_inner2(const char *path,
348 struct filestr_desc *file,
349 unsigned int callflags)
269{ 350{
270 struct filedesc* file = &openfiles[fd]; 351 int rc;
271 int rc = 0;
272
273 LDEBUGF("fsync(%d)\n", fd);
274 352
275 if (fd < 0 || fd > MAX_OPEN_FILES-1) { 353 struct path_component_info compinfo;
276 errno = EINVAL; 354 rc = open_stream_internal(path, callflags, &file->stream, &compinfo);
277 return -1; 355 if (rc < 0)
278 } 356 {
279 if (!file->busy) { 357 DEBUGF("Open failed: %d\n", rc);
280 errno = EBADF; 358 FILE_ERROR_RETURN(ERRNO, rc * 10 - 1);
281 return -2;
282 } 359 }
283 if (file->write) { 360
284 /* flush sector cache */ 361 bool created = false;
285 if ( file->dirty ) { 362
286 rc = flush_cache(fd); 363 if (rc > 0)
287 if (rc < 0) 364 {
288 { 365 if (callflags & FF_EXCL)
289 /* when failing, try to close the file anyway */ 366 {
290 fat_closewrite(&(file->fatfile), file->size, file->attr); 367 DEBUGF("File exists\n");
291 return rc * 10 - 3; 368 FILE_ERROR(EEXIST, -2);
292 }
293 } 369 }
294 370
295 /* truncate? */ 371 if (compinfo.attr & ATTR_DIRECTORY)
296 if (file->trunc) { 372 {
297 rc = ftruncate(fd, file->size); 373 if ((callflags & FD_WRITE) || !(callflags & FF_ANYTYPE))
298 if (rc < 0)
299 { 374 {
300 /* when failing, try to close the file anyway */ 375 DEBUGF("File is a directory\n");
301 fat_closewrite(&(file->fatfile), file->size, file->attr); 376 FILE_ERROR(EISDIR, -3);
302 return rc * 10 - 4;
303 } 377 }
378
379 compinfo.filesize = MAX_DIRECTORY_SIZE; /* allow file ops */
380 }
381 }
382 else if (callflags & FF_CREAT)
383 {
384 if (compinfo.attr & ATTR_DIRECTORY)
385 {
386 DEBUGF("File is a directory\n");
387 FILE_ERROR(EISDIR, -5);
304 } 388 }
305 389
306 /* tie up all loose ends */ 390 /* not found; try to create it */
307 rc = fat_closewrite(&(file->fatfile), file->size, file->attr); 391
392 callflags &= ~FO_TRUNC;
393 rc = create_stream_internal(&compinfo.parentinfo, compinfo.name,
394 compinfo.length, ATTR_NEW_FILE, callflags,
395 &file->stream);
308 if (rc < 0) 396 if (rc < 0)
309 return rc * 10 - 5; 397 FILE_ERROR(ERRNO, rc * 10 - 6);
310 }
311 return 0;
312}
313 398
314int remove(const char* name) 399 created = true;
315{
316 int rc;
317 struct filedesc* file;
318 /* Can't use dircache now, because we need to access the fat structures. */
319 int fd = open_internal(name, O_WRONLY, false);
320 if ( fd < 0 )
321 return fd * 10 - 1;
322
323 file = &openfiles[fd];
324#ifdef HAVE_DIRCACHE
325 dircache_remove(name);
326#endif
327 rc = fat_remove(&(file->fatfile));
328 if ( rc < 0 ) {
329 DEBUGF("Failed removing file: %d\n", rc);
330 errno = EIO;
331 return rc * 10 - 3;
332 } 400 }
401 else
402 {
403 DEBUGF("File not found\n");
404 FILE_ERROR(ENOENT, -7);
405 }
406
407 fat_rewind(&file->stream.fatstr);
408 file->sizep = fileobj_get_sizep(&file->stream);
409 file->offset = 0;
410
411 if (!created)
412 {
413 /* size from storage applies to first stream only otherwise it's
414 already up to date */
415 const bool first = fileobj_get_flags(&file->stream) & FO_SINGLE;
416 if (first)
417 *file->sizep = compinfo.filesize;
333 418
334 file->size = 0; 419 if (callflags & FO_TRUNC)
420 {
421 /* if the file is kind of "big" then free some space now */
422 rc = ftruncate_internal(file, 0, *file->sizep >= O_TRUNC_THRESH);
423 if (rc < 0)
424 {
425 DEBUGF("O_TRUNC failed: %d\n", rc);
426 FILE_ERROR(ERRNO, rc * 10 - 4);
427 }
428 }
429 }
335 430
336 rc = close(fd); 431 rc = 0;
337 if (rc<0) 432file_error:
338 return rc * 10 - 4; 433 if (rc < 0)
434 close_stream_internal(&file->stream);
339 435
340 return 0; 436 return rc;
341} 437}
342 438
343int rename(const char* path, const char* newpath) 439/* allocate a file descriptor, if needed, assemble stream flags and open
440 a new stream */
441static int open_internal_inner1(const char *path, int oflag,
442 unsigned int callflags)
344{ 443{
345 int rc, fd; 444 DEBUGF("%s(path=\"%s\",oflag=%X,callflags=%X)\n", __func__,
346 DIR_UNCACHED* dir; 445 path, oflag, callflags);
347 char* nameptr;
348 char* dirptr;
349 struct filedesc* file;
350 char newpath2[MAX_PATH];
351 446
352 /* verify new path does not already exist */ 447 int rc;
353 /* If it is a directory, errno == EISDIR if the name exists */
354 fd = open(newpath, O_RDONLY);
355 if ( fd >= 0 || errno == EISDIR) {
356 close(fd);
357 errno = EBUSY;
358 return -1;
359 }
360 close(fd);
361 448
362 fd = open_internal(path, O_RDONLY, false); 449 struct filestr_desc *file;
363 if ( fd < 0 ) { 450 int fildes = alloc_filestr(&file);
364 errno = EIO; 451 if (fildes < 0)
365 return fd * 10 - 2; 452 FILE_ERROR(EMFILE, -1);
366 }
367 453
368 /* extract new file name */ 454 callflags &= ~FDO_MASK;
369 nameptr = strrchr(newpath,'/');
370 if (nameptr)
371 nameptr++;
372 else {
373 close(fd);
374 return - 3;
375 }
376 455
377 /* Extract new path */ 456 if (oflag & O_ACCMODE)
378 strcpy(newpath2, newpath); 457 {
458 callflags |= FD_WRITE;
379 459
380 dirptr = strrchr(newpath2,'/'); 460 if ((oflag & O_ACCMODE) == O_WRONLY)
381 if(dirptr) 461 callflags |= FD_WRONLY;
382 *dirptr = 0;
383 else {
384 close(fd);
385 return - 4;
386 }
387 462
388 dirptr = newpath2; 463 if (oflag & O_APPEND)
464 callflags |= FD_APPEND;
389 465
390 if(strlen(dirptr) == 0) { 466 if (oflag & O_TRUNC)
391 dirptr = "/"; 467 callflags |= FO_TRUNC;
392 } 468 }
393 469 else if (oflag & O_TRUNC)
394 dir = opendir_uncached(dirptr); 470 {
395 if(!dir) { 471 /* O_TRUNC requires write mode */
396 close(fd); 472 DEBUGF("No write mode but have O_TRUNC\n");
397 return - 5; 473 FILE_ERROR(EINVAL, -2);
398 } 474 }
399 475
400 file = &openfiles[fd]; 476 /* O_CREAT and O_APPEND are fine without write mode
477 * for the former, an empty file is created but no data may be written
478 * for the latter, no append will be allowed anyway */
479 if (oflag & O_CREAT)
480 {
481 callflags |= FF_CREAT;
401 482
402 rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr, 483 if (oflag & O_EXCL)
403 file->size, file->attr); 484 callflags |= FF_EXCL;
404#ifdef HAVE_MULTIVOLUME
405 if ( rc == -1) {
406 close(fd);
407 closedir_uncached(dir);
408 DEBUGF("Failed renaming file across volumnes: %d\n", rc);
409 errno = EXDEV;
410 return -6;
411 }
412#endif
413 if ( rc < 0 ) {
414 close(fd);
415 closedir_uncached(dir);
416 DEBUGF("Failed renaming file: %d\n", rc);
417 errno = EIO;
418 return rc * 10 - 7;
419 } 485 }
420 486
421#ifdef HAVE_DIRCACHE 487 rc = open_internal_inner2(path, file, callflags);
422 dircache_rename(path, newpath); 488 if (rc < 0)
423#endif 489 FILE_ERROR(ERRNO, rc * 10 - 3);
424 490
425 rc = close(fd); 491 return fildes;
426 if (rc<0) {
427 closedir_uncached(dir);
428 errno = EIO;
429 return rc * 10 - 8;
430 }
431 492
432 rc = closedir_uncached(dir); 493file_error:
433 if (rc<0) { 494 return rc;
434 errno = EIO; 495}
435 return rc * 10 - 9;
436 }
437 496
438 return 0; 497static int open_internal_locked(const char *path, int oflag,
498 unsigned int callflags)
499{
500 file_internal_lock_WRITER();
501 int rc = open_internal_inner1(path, oflag, callflags);
502 file_internal_unlock_WRITER();
503 return rc;
439} 504}
440 505
441int ftruncate(int fd, off_t size) 506/* fill a cache buffer with a new sector */
507static int readwrite_fill_cache(struct filestr_desc *file, unsigned long sector,
508 unsigned long filesectors, bool write)
442{ 509{
443 int rc, sector; 510 /* sector != cachep->sector should have been checked by now */
444 struct filedesc* file = &openfiles[fd];
445 511
446 sector = size / SECTOR_SIZE; 512 int rc;
447 if (size % SECTOR_SIZE) 513 struct filestr_cache *cachep = filestr_get_cache(&file->stream);
448 sector++;
449 514
450 rc = fat_seek(&(file->fatfile), sector); 515 if (cachep->flags & FSC_DIRTY)
451 if (rc < 0) { 516 {
452 errno = EIO; 517 rc = flush_cache(file);
453 return rc * 10 - 1; 518 if (rc < 0)
519 FILE_ERROR(ERRNO, rc * 10 - 1);
454 } 520 }
455 521
456 rc = fat_truncate(&(file->fatfile)); 522 if (fat_query_sectornum(&file->stream.fatstr) != sector)
457 if (rc < 0) { 523 {
458 errno = EIO; 524 /* get on the correct sector */
459 return rc * 10 - 2; 525 rc = fat_seek(&file->stream.fatstr, sector);
526 if (rc < 0)
527 FILE_ERROR(EIO, rc * 10 - 2);
460 } 528 }
461 529
462 file->size = size; 530 if (!write || sector < filesectors)
463#ifdef HAVE_DIRCACHE 531 {
464 dircache_update_filesize(fd, size, file->fatfile.firstcluster); 532 /* only reading or this sector would have been flushed if the cache
465#endif 533 was previously needed for a different sector */
534 rc = fat_readwrite(&file->stream.fatstr, 1, cachep->buffer, false);
535 if (rc < 0)
536 FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO, rc * 10 - 3);
537 }
538 else
539 {
540 /* create a fresh, shiny, new sector with that new sector smell */
541 cachep->flags = FSC_NEW;
542 }
466 543
467 return 0; 544 cachep->sector = sector;
545 return 1;
546file_error:
547 DEBUGF("Failed caching sector: %d\n", rc);
548 return rc;
468} 549}
469 550
470static int flush_cache(int fd) 551/* read or write to part or all of the cache buffer */
552static inline void readwrite_cache(struct filestr_cache *cachep, void *buf,
553 unsigned long secoffset, size_t nbyte,
554 bool write)
471{ 555{
472 int rc; 556 void *dst, *cbufp = cachep->buffer + secoffset;
473 struct filedesc* file = &openfiles[fd];
474 long sector = file->fileoffset / SECTOR_SIZE;
475
476 DEBUGF("Flushing dirty sector cache\n");
477 557
478 /* make sure we are on correct sector */ 558 if (write)
479 rc = fat_seek(&(file->fatfile), sector); 559 {
480 if ( rc < 0 ) 560 dst = cbufp;
481 return rc * 10 - 3; 561 cachep->flags |= FSC_DIRTY;
482 562 }
483 rc = fat_readwrite(&(file->fatfile), 1, file->cache, true ); 563 else
564 {
565 dst = buf;
566 buf = cbufp;
567 }
484 568
485 if ( rc < 0 ) { 569 memcpy(dst, buf, nbyte);
486 if(file->fatfile.eof) 570}
487 errno = ENOSPC;
488 571
489 return rc * 10 - 2; 572/* read or write a partial sector using the file's cache */
573static inline ssize_t readwrite_partial(struct filestr_desc *file,
574 struct filestr_cache *cachep,
575 unsigned long sector,
576 unsigned long secoffset,
577 void *buf,
578 size_t nbyte,
579 unsigned long filesectors,
580 unsigned int flags)
581{
582 if (sector != cachep->sector)
583 {
584 /* wrong sector in buffer */
585 int rc = readwrite_fill_cache(file, sector, filesectors, flags);
586 if (rc <= 0)
587 return rc;
490 } 588 }
491 589
492 file->dirty = false; 590 readwrite_cache(cachep, buf, secoffset, nbyte, flags);
493 591 return nbyte;
494 return 0;
495} 592}
496 593
497static int readwrite(int fd, void* buf, long count, bool write) 594/* read from or write to the file; back end to read() and write() */
595static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
596 bool write)
498{ 597{
499 long sectors; 598 DEBUGF("readwrite(%p,%lx,%ld,%s)\n",
500 long nread=0; 599 file, (long)buf, nbyte, write ? "write" : "read");
501 struct filedesc* file;
502 int rc;
503#ifdef STORAGE_NEEDS_ALIGN
504 long i;
505 int rc2;
506#endif
507 600
508 if (fd < 0 || fd > MAX_OPEN_FILES-1) { 601 const file_size_t size = *file->sizep;
509 errno = EINVAL; 602 file_size_t filerem;
510 return -1;
511 }
512 603
513 file = &openfiles[fd]; 604 if (write)
605 {
606 /* if opened in append mode, move pointer to end */
607 if (file->stream.flags & FD_APPEND)
608 file->offset = MIN(size, FILE_SIZE_MAX);
514 609
515 if ( !file->busy ) { 610 filerem = FILE_SIZE_MAX - file->offset;
516 errno = EBADF; 611 }
517 return -1; 612 else
613 {
614 /* limit to maximum possible offset (EOF or FILE_SIZE_MAX) */
615 filerem = MIN(size, FILE_SIZE_MAX) - file->offset;
518 } 616 }
519 617
520 if(file->attr & FAT_ATTR_DIRECTORY) { 618 if (nbyte > filerem)
521 errno = EISDIR; 619 {
522 return -1; 620 nbyte = filerem;
621 if (nbyte > 0)
622 {}
623 else if (write)
624 FILE_ERROR_RETURN(EFBIG, -1); /* would get too large */
625 else if (file->offset >= FILE_SIZE_MAX)
626 FILE_ERROR_RETURN(EOVERFLOW, -2); /* can't read here */
523 } 627 }
524 628
525 LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n", 629 if (nbyte == 0)
526 fd,(long)buf,count,write?"write":"read"); 630 return 0;
527 631
528 /* attempt to read past EOF? */ 632 int rc = 0;
529 if (!write && count > file->size - file->fileoffset) 633
530 count = file->size - file->fileoffset; 634 struct filestr_cache * const cachep = file->stream.cachep;
635 void * const bufstart = buf;
636
637 const unsigned long filesectors = filesize_sectors(size);
638 unsigned long sector = file->offset / SECTOR_SIZE;
639 unsigned long sectoroffs = file->offset % SECTOR_SIZE;
531 640
532 /* any head bytes? */ 641 /* any head bytes? */
533 if ( file->cacheoffset != -1 ) { 642 if (sectoroffs)
534 int offs = file->cacheoffset; 643 {
535 int headbytes = MIN(count, SECTOR_SIZE - offs); 644 size_t headbytes = MIN(nbyte, SECTOR_SIZE - sectoroffs);
645 rc = readwrite_partial(file, cachep, sector, sectoroffs, buf, headbytes,
646 filesectors, write);
647 if (rc <= 0)
648 {
649 if (rc < 0)
650 FILE_ERROR(ERRNO, rc * 10 - 3);
536 651
537 if (write) { 652 nbyte = 0; /* eof, skip the rest */
538 memcpy( file->cache + offs, buf, headbytes );
539 file->dirty = true;
540 } 653 }
541 else { 654 else
542 memcpy( buf, file->cache + offs, headbytes ); 655 {
656 buf += rc;
657 nbyte -= rc;
658 sector++; /* if nbyte goes to 0, the rest is skipped anyway */
543 } 659 }
660 }
544 661
545 if (offs + headbytes == SECTOR_SIZE) { 662 /* read/write whole sectors right into/from the supplied buffer */
546 if (file->dirty) { 663 unsigned long sectorcount = nbyte / SECTOR_SIZE;
547 rc = flush_cache(fd);
548 if ( rc < 0 ) {
549 errno = EIO;
550 return rc * 10 - 2;
551 }
552 }
553 file->cacheoffset = -1;
554 }
555 else {
556 file->cacheoffset += headbytes;
557 }
558 664
559 nread = headbytes; 665 while (sectorcount)
560 count -= headbytes; 666 {
561 } 667 unsigned long runlen = sectorcount;
562 668
563 /* If the buffer has been modified, either it has been flushed already 669 /* if a cached sector is inside the transfer range, split the transfer
564 * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no 670 into two parts and use the cache for that sector to keep it coherent
565 * more data to follow in this call). Do NOT flush here. */ 671 without writeback */
672 if (UNLIKELY(cachep->sector >= sector &&
673 cachep->sector < sector + sectorcount))
674 {
675 runlen = cachep->sector - sector;
676 }
566 677
567 /* read/write whole sectors right into/from the supplied buffer */ 678 if (runlen)
568 sectors = count / SECTOR_SIZE; 679 {
569 rc = 0; 680 if (fat_query_sectornum(&file->stream.fatstr) != sector)
570 if ( sectors ) {
571#ifdef STORAGE_NEEDS_ALIGN
572 if (((uint32_t)buf + nread) & (CACHEALIGN_SIZE - 1))
573 for (i = 0; i < sectors; i++)
574 { 681 {
575 if (write) memcpy(file->cache, buf+nread+i*SECTOR_SIZE, SECTOR_SIZE); 682 /* get on the correct sector */
576 rc2 = fat_readwrite(&(file->fatfile), 1, file->cache, write ); 683 rc = 0;
577 if (rc2 < 0) 684
685 /* If the dirty bit isn't set, we're somehow beyond the file
686 size and you can't explain _that_ */
687 if (sector >= filesectors && cachep->flags == (FSC_NEW|FSC_DIRTY))
578 { 688 {
579 rc = rc2; 689 rc = flush_cache(file);
580 break; 690 if (rc < 0)
691 FILE_ERROR(ERRNO, rc * 10 - 4);
692
693 if (cachep->sector + 1 == sector)
694 rc = 1; /* if now ok, don't seek */
695 }
696
697 if (rc == 0)
698 {
699 rc = fat_seek(&file->stream.fatstr, sector);
700 if (rc < 0)
701 FILE_ERROR(EIO, rc * 10 - 5);
581 } 702 }
582 else rc += rc2;
583 if (!write) memcpy(buf+nread+i*SECTOR_SIZE, file->cache, SECTOR_SIZE);
584 }
585 else
586#endif
587 rc = fat_readwrite(&(file->fatfile), sectors, (unsigned char*)buf+nread, write );
588 if ( rc < 0 ) {
589 DEBUGF("Failed read/writing %ld sectors\n",sectors);
590 errno = EIO;
591 if(write && file->fatfile.eof) {
592 DEBUGF("No space left on device\n");
593 errno = ENOSPC;
594 } else {
595 file->fileoffset += nread;
596 } 703 }
597 file->cacheoffset = -1; 704
598 /* adjust file size to length written */ 705 rc = fat_readwrite(&file->stream.fatstr, runlen, buf, write);
599 if ( write && file->fileoffset > file->size ) 706 if (rc < 0)
600 { 707 {
601 file->size = file->fileoffset; 708 DEBUGF("I/O error %sing %ld sectors\n", sectors,
602#ifdef HAVE_DIRCACHE 709 write ? "writ" : "read");
603 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); 710 FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO,
604#endif 711 rc * 10 - 6);
605 } 712 }
606 return nread ? nread : rc * 10 - 4; 713 else
607 } 714 {
608 else { 715 buf += rc * SECTOR_SIZE;
609 if ( rc > 0 ) { 716 nbyte -= rc * SECTOR_SIZE;
610 nread += rc * SECTOR_SIZE; 717 sector += rc;
611 count -= sectors * SECTOR_SIZE; 718 sectorcount -= rc;
612 719
613 /* if eof, skip tail bytes */ 720 /* if eof, skip tail bytes */
614 if ( rc < sectors ) 721 if ((unsigned long)rc < runlen)
615 count = 0; 722 nbyte = 0;
616 } 723
617 else { 724 if (!nbyte)
618 /* eof */ 725 break;
619 count=0;
620 } 726 }
727 }
621 728
622 file->cacheoffset = -1; 729 if (UNLIKELY(sectorcount && sector == cachep->sector))
730 {
731 /* do this one sector with the cache */
732 readwrite_cache(cachep, buf, 0, SECTOR_SIZE, write);
733 buf += SECTOR_SIZE;
734 nbyte -= SECTOR_SIZE;
735 sector++;
736 sectorcount--;
623 } 737 }
624 } 738 }
625 739
626 /* any tail bytes? */ 740 /* any tail bytes? */
627 if ( count ) { 741 if (nbyte)
628 if (write) { 742 {
629 if ( file->fileoffset + nread < file->size ) { 743 /* tail bytes always start at sector offset 0 */
630 /* sector is only partially filled. copy-back from disk */ 744 rc = readwrite_partial(file, cachep, sector, 0, buf, nbyte,
631 LDEBUGF("Copy-back tail cache\n"); 745 filesectors, write);
632 rc = fat_readwrite(&(file->fatfile), 1, file->cache, false ); 746 if (rc < 0)
633 if ( rc < 0 ) { 747 FILE_ERROR(ERRNO, rc * 10 - 7);
634 DEBUGF("Failed writing\n");
635 errno = EIO;
636 file->fileoffset += nread;
637 file->cacheoffset = -1;
638 /* adjust file size to length written */
639 if ( file->fileoffset > file->size )
640 {
641 file->size = file->fileoffset;
642#ifdef HAVE_DIRCACHE
643 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
644#endif
645 }
646 return nread ? nread : rc * 10 - 5;
647 }
648 /* seek back one sector to put file position right */
649 rc = fat_seek(&(file->fatfile),
650 (file->fileoffset + nread) /
651 SECTOR_SIZE);
652 if ( rc < 0 ) {
653 DEBUGF("fat_seek() failed\n");
654 errno = EIO;
655 file->fileoffset += nread;
656 file->cacheoffset = -1;
657 /* adjust file size to length written */
658 if ( file->fileoffset > file->size )
659 {
660 file->size = file->fileoffset;
661#ifdef HAVE_DIRCACHE
662 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
663#endif
664 }
665 return nread ? nread : rc * 10 - 6;
666 }
667 }
668 memcpy( file->cache, (unsigned char*)buf + nread, count );
669 file->dirty = true;
670 }
671 else {
672 rc = fat_readwrite(&(file->fatfile), 1, file->cache,false);
673 if (rc < 1 ) {
674 DEBUGF("Failed caching sector\n");
675 errno = EIO;
676 file->fileoffset += nread;
677 file->cacheoffset = -1;
678 return nread ? nread : rc * 10 - 7;
679 }
680 memcpy( (unsigned char*)buf + nread, file->cache, count );
681 }
682 748
683 nread += count; 749 buf += rc;
684 file->cacheoffset = count;
685 } 750 }
686 751
687 file->fileoffset += nread; 752file_error:;
688 LDEBUGF("fileoffset: %ld\n", file->fileoffset); 753#ifdef DEBUG
754 if (errno == ENOSPC)
755 DEBUGF("No space left on device\n");
756#endif
689 757
690 /* adjust file size to length written */ 758 size_t done = buf - bufstart;
691 if ( write && file->fileoffset > file->size ) 759 if (done)
692 { 760 {
693 file->size = file->fileoffset; 761 /* error or not, update the file offset and size if anything was
694#ifdef HAVE_DIRCACHE 762 transferred */
695 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); 763 file->offset += done;
696#endif 764 DEBUGF("file offset: %ld\n", file->offset);
765
766 /* adjust file size to length written */
767 if (write && file->offset > size)
768 *file->sizep = file->offset;
769
770 if (rc > 0)
771 return done;
697 } 772 }
698 773
699 return nread; 774 return rc;
700} 775}
701 776
702ssize_t write(int fd, const void* buf, size_t count) 777
778/** Internal interface **/
779
780/* open a file without codepage conversion during the directory search;
781 required to avoid any reentrancy when opening codepages and when scanning
782 directories internally, which could infinitely recurse and would corrupt
783 the static data */
784int open_noiso_internal(const char *path, int oflag)
703{ 785{
704 if (!openfiles[fd].write) { 786 return open_internal_locked(path, oflag, FF_ANYTYPE | FF_NOISO);
705 errno = EACCES;
706 return -1;
707 }
708 return readwrite(fd, (void *)buf, count, true);
709} 787}
710 788
711ssize_t read(int fd, void* buf, size_t count) 789
790/** POSIX **/
791
792/* open a file */
793int open(const char *path, int oflag)
712{ 794{
713 return readwrite(fd, buf, count, false); 795 DEBUGF("open(path=\"%s\",oflag=%X)\n", path, (unsigned)oflag);
796 return open_internal_locked(path, oflag, FF_ANYTYPE);
714} 797}
715 798
799/* create a new file or rewrite an existing one */
800int creat(const char *path)
801{
802 DEBUGF("creat(path=\"%s\")\n", path);
803 return open_internal_locked(path, O_WRONLY|O_CREAT|O_TRUNC, FF_ANYTYPE);
804}
716 805
717off_t lseek(int fd, off_t offset, int whence) 806/* close a file descriptor */
807int close(int fildes)
718{ 808{
719 off_t pos; 809 DEBUGF("close(fd=%d)\n", fildes);
720 long newsector; 810
721 long oldsector;
722 int sectoroffset;
723 int rc; 811 int rc;
724 struct filedesc* file = &openfiles[fd];
725 812
726 LDEBUGF("lseek(%d,%ld,%d)\n",fd,offset,whence); 813 file_internal_lock_WRITER();
727 814
728 if (fd < 0 || fd > MAX_OPEN_FILES-1) { 815 /* needs to work even if marked "nonexistant" */
729 errno = EINVAL; 816 struct filestr_desc *file = &open_streams[fildes];
730 return -1; 817 if ((unsigned int)fildes >= MAX_OPEN_FILES || !file->stream.flags)
731 } 818 {
732 if ( !file->busy ) { 819 DEBUGF("filedes %d not open\n", fildes);
733 errno = EBADF; 820 FILE_ERROR(EBADF, -2);
734 return -1;
735 } 821 }
736 822
737 switch ( whence ) { 823 rc = close_internal(file);
738 case SEEK_SET: 824 if (rc < 0)
739 pos = offset; 825 FILE_ERROR(ERRNO, rc * 10 - 3);
740 break;
741 826
742 case SEEK_CUR: 827file_error:
743 pos = file->fileoffset + offset; 828 file_internal_unlock_WRITER();
744 break; 829 return rc;
830}
831
832/* truncate a file to a specified length */
833int ftruncate(int fildes, off_t length)
834{
835 DEBUGF("ftruncate(fd=%d,len=%ld)\n", fildes, (long)length);
745 836
746 case SEEK_END: 837 struct filestr_desc * const file = GET_FILESTR(READER, fildes);
747 pos = file->size + offset; 838 if (!file)
748 break; 839 FILE_ERROR_RETURN(ERRNO, -1);
749 840
750 default: 841 int rc;
751 errno = EINVAL; 842
752 return -2; 843 if (!(file->stream.flags & FD_WRITE))
844 {
845 DEBUGF("Descriptor is read-only mode\n");
846 FILE_ERROR(EBADF, -2);
753 } 847 }
754 if ((pos < 0) || (pos > file->size)) { 848
755 errno = EINVAL; 849 if (length < 0)
756 return -3; 850 {
851 DEBUGF("Length %ld is invalid\n", (long)length);
852 FILE_ERROR(EINVAL, -3);
757 } 853 }
758 854
759 /* new sector? */ 855 rc = ftruncate_internal(file, length, true);
760 newsector = pos / SECTOR_SIZE; 856 if (rc < 0)
761 oldsector = file->fileoffset / SECTOR_SIZE; 857 FILE_ERROR(ERRNO, rc * 10 - 4);
762 sectoroffset = pos % SECTOR_SIZE;
763 858
764 if ( (newsector != oldsector) || 859file_error:
765 ((file->cacheoffset==-1) && sectoroffset) ) { 860 RELEASE_FILESTR(READER, file);
861 return rc;
862}
766 863
767 if ( newsector != oldsector ) { 864/* synchronize changes to a file */
768 if (file->dirty) { 865int fsync(int fildes)
769 rc = flush_cache(fd); 866{
770 if (rc < 0) 867 DEBUGF("fsync(fd=%d)\n", fildes);
771 return rc * 10 - 5;
772 }
773 868
774 rc = fat_seek(&(file->fatfile), newsector); 869 struct filestr_desc * const file = GET_FILESTR(WRITER, fildes);
775 if ( rc < 0 ) { 870 if (!file)
776 errno = EIO; 871 FILE_ERROR_RETURN(ERRNO, -1);
777 return rc * 10 - 4; 872
778 } 873 int rc;
779 } 874
780 if ( sectoroffset ) { 875 if (!(file->stream.flags & FD_WRITE))
781 rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false); 876 {
782 if ( rc < 0 ) { 877 DEBUGF("Descriptor is read-only mode\n", fd);
783 errno = EIO; 878 FILE_ERROR(EINVAL, -2);
784 return rc * 10 - 6;
785 }
786 file->cacheoffset = sectoroffset;
787 }
788 else
789 file->cacheoffset = -1;
790 } 879 }
791 else
792 if ( file->cacheoffset != -1 )
793 file->cacheoffset = sectoroffset;
794 880
795 file->fileoffset = pos; 881 rc = fsync_internal(file);
882 if (rc < 0)
883 FILE_ERROR(ERRNO, rc * 10 - 3);
796 884
797 return pos; 885file_error:
886 RELEASE_FILESTR(WRITER, file);
887 return rc;
798} 888}
799 889
800off_t filesize(int fd) 890/* move the read/write file offset */
891off_t lseek(int fildes, off_t offset, int whence)
801{ 892{
802 struct filedesc* file = &openfiles[fd]; 893 DEBUGF("lseek(fd=%d,ofs=%ld,wh=%d)\n", fildes, (long)offset, whence);
803 894
804 if (fd < 0 || fd > MAX_OPEN_FILES-1) { 895 struct filestr_desc * const file = GET_FILESTR(READER, fildes);
805 errno = EINVAL; 896 if (!file)
806 return -1; 897 FILE_ERROR_RETURN(ERRNO, -1);
898
899 off_t rc = lseek_internal(file, offset, whence);
900 if (rc < 0)
901 FILE_ERROR(ERRNO, rc * 10 - 2);
902
903file_error:
904 RELEASE_FILESTR(READER, file);
905 return rc;
906}
907
908/* read from a file */
909ssize_t read(int fildes, void *buf, size_t nbyte)
910{
911 struct filestr_desc * const file = GET_FILESTR(READER, fildes);
912 if (!file)
913 FILE_ERROR_RETURN(ERRNO, -1);
914
915 ssize_t rc;
916
917 if (file->stream.flags & FD_WRONLY)
918 {
919 DEBUGF("read(fd=%d,buf=%p,nb=%lu) - "
920 "descriptor is write-only mode\n", fildes, buf, nbyte);
921 FILE_ERROR(EBADF, -2);
807 } 922 }
808 if ( !file->busy ) { 923
809 errno = EBADF; 924 rc = readwrite(file, buf, nbyte, false);
810 return -1; 925 if (rc < 0)
926 FILE_ERROR(ERRNO, rc * 10 - 3);
927
928file_error:
929 RELEASE_FILESTR(READER, file);
930 return rc;
931}
932
933/* write on a file */
934ssize_t write(int fildes, const void *buf, size_t nbyte)
935{
936 struct filestr_desc * const file = GET_FILESTR(READER, fildes);
937 if (!file)
938 FILE_ERROR_RETURN(ERRNO, -1);
939
940 ssize_t rc;
941
942 if (!(file->stream.flags & FD_WRITE))
943 {
944 DEBUGF("write(fd=%d,buf=%p,nb=%lu) - "
945 "descriptor is read-only mode\n", fildes, buf, nbyte);
946 FILE_ERROR(EBADF, -2);
811 } 947 }
812 948
813 return file->size; 949 rc = readwrite(file, (void *)buf, nbyte, true);
950 if (rc < 0)
951 FILE_ERROR(ERRNO, rc * 10 - 3);
952
953file_error:
954 RELEASE_FILESTR(READER, file);
955 return rc;
814} 956}
815 957
958/* remove a file */
959int remove(const char *path)
960{
961 DEBUGF("remove(path=\"%s\")\n", path);
962
963 file_internal_lock_WRITER();
964 int rc = remove_stream_internal(path, NULL, FF_FILE);
965 file_internal_unlock_WRITER();
966 return rc;
967}
816 968
817/* release all file handles on a given volume "by force", to avoid leaks */ 969/* rename a file */
818int release_files(int volume) 970int rename(const char *old, const char *new)
819{ 971{
820 struct filedesc* pfile = openfiles; 972 DEBUGF("rename(old=\"%s\",new=\"%s\")\n", old, new);
821 int fd; 973
822 int closed = 0; 974 int rc, open1rc = -1, open2rc = -1;
823 for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++) 975 struct filestr_base oldstr, newstr;
976 struct path_component_info oldinfo, newinfo;
977
978 file_internal_lock_WRITER();
979
980 /* open 'old'; it must exist */
981 open1rc = open_stream_internal(old, FF_ANYTYPE, &oldstr, &oldinfo);
982 if (open1rc <= 0)
824 { 983 {
984 DEBUGF("Failed opening old: %d\n", rc);
985 if (open1rc == 0)
986 FILE_ERROR(ENOENT, -1);
987 else
988 FILE_ERROR(ERRNO, open1rc * 10 - 1);
989 }
990
991 /* if 'old' is a directory then 'new' is also required to be one if 'new'
992 is to be overwritten */
993 const bool are_dirs = oldinfo.attr & ATTR_DIRECTORY;
994
995 /* open new (may or may not exist) */
996 unsigned int callflags = FF_FILE;
997 if (are_dirs)
998 {
999 /* if 'old' is found while parsing the new directory components then
1000 'new' contains path prefix that names 'old'; if new and old are in
1001 the same directory, this tests positive but that is checked later */
1002 callflags = FF_DIR | FF_CHECKPREFIX;
1003 newinfo.prefixp = oldstr.infop;
1004 }
1005
1006 open2rc = open_stream_internal(new, callflags, &newstr, &newinfo);
1007 if (open2rc < 0)
1008 {
1009 DEBUGF("Failed opening new file: %d\n", rc);
1010 FILE_ERROR(ERRNO, open2rc * 10 - 2);
1011 }
1012
825#ifdef HAVE_MULTIVOLUME 1013#ifdef HAVE_MULTIVOLUME
826 if (pfile->fatfile.volume == volume) 1014 if (oldinfo.parentinfo.volume != newinfo.parentinfo.volume)
827#else 1015 {
828 (void)volume; 1016 DEBUGF("Cross-device link\n");
829#endif 1017 FILE_ERROR(EXDEV, -3);
1018 }
1019#endif /* HAVE_MULTIVOLUME */
1020
1021 /* if the parent is changing then this is a move, not a simple rename */
1022 const bool is_move = !fat_file_is_same(&oldinfo.parentinfo.fatfile,
1023 &newinfo.parentinfo.fatfile);
1024 /* prefix found and moving? */
1025 if (is_move && (newinfo.attr & ATTR_PREFIX))
1026 {
1027 DEBUGF("New contains prefix that names old\n");
1028 FILE_ERROR(EINVAL, -4);
1029 }
1030
1031 const char * const oldname = strmemdupa(oldinfo.name, oldinfo.length);
1032 const char * const newname = strmemdupa(newinfo.name, newinfo.length);
1033 bool is_overwrite = false;
1034
1035 if (open2rc > 0)
1036 {
1037 /* new name exists in parent; check if 'old' is overwriting 'new';
1038 if it's the very same file, then it's just a rename */
1039 is_overwrite = oldstr.bindp != newstr.bindp;
1040
1041 if (is_overwrite)
1042 {
1043 if (are_dirs)
1044 {
1045 /* the directory to be overwritten must be empty */
1046 rc = test_dir_empty_internal(&newstr);
1047 if (rc < 0)
1048 FILE_ERROR(ERRNO, rc * 10 - 5);
1049 }
1050 }
1051 else if (!strcmp(newname, oldname)) /* case-only is ok */
1052 {
1053 DEBUGF("No name change (success)\n");
1054 rc = 0;
1055 FILE_ERROR(ERRNO, RC);
1056 }
1057 }
1058 else if (!are_dirs && (newinfo.attr & ATTR_DIRECTORY))
1059 {
1060 /* even if new doesn't exist, canonical path type must match
1061 (ie. a directory path such as "/foo/bar/" when old names a file) */
1062 DEBUGF("New path is a directory\n");
1063 FILE_ERROR(EISDIR, -6);
1064 }
1065
1066 /* first, create the new entry so that there's never a time that the
1067 victim's data has no reference in the directory tree, that is, until
1068 everything else first succeeds */
1069 struct file_base_info old_fileinfo = *oldstr.infop;
1070 rc = fat_rename(&newinfo.parentinfo.fatfile, &oldstr.infop->fatfile,
1071 newname);
1072 if (rc < 0)
1073 {
1074 DEBUGF("I/O error renaming file: %d\n", rc);
1075 FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO, rc * 10 - 7);
1076 }
1077
1078 if (is_overwrite)
1079 {
1080 /* 'new' would have been assigned its own directory entry and
1081 succeeded so at this point it is treated like a remove() call
1082 on the victim which preserves data until the last reference is
1083 closed */
1084 rc = remove_stream_internal(NULL, &newstr, callflags);
1085 if (rc < 0)
1086 FILE_ERROR(ERRNO, rc * 10 - 8);
1087 }
1088
1089 fileop_onrename_internal(&oldstr, is_move ? &old_fileinfo : NULL,
1090 &newinfo.parentinfo, newname);
1091
1092file_error:
1093 /* for now, there is nothing to fail upon closing the old stream */
1094 if (open1rc >= 0)
1095 close_stream_internal(&oldstr);
1096
1097 /* the 'new' stream could fail to close cleanly because it became
1098 impossible to remove its data if this was an overwrite operation */
1099 if (open2rc >= 0)
1100 {
1101 int rc2 = close_stream_internal(&newstr);
1102 if (rc2 < 0 && rc >= 0)
830 { 1103 {
831 pfile->busy = false; /* mark as available, no further action */ 1104 DEBUGF("Success but failed closing new: %d\n", rc2);
832 closed++; 1105 rc = rc2 * 10 - 9;
833 } 1106 }
834 } 1107 }
835 return closed; /* return how many we did */ 1108
1109 file_internal_unlock_WRITER();
1110 return rc;
1111}
1112
1113
1114/** Extensions **/
1115
1116/* get the binary size of a file (in bytes) */
1117off_t filesize(int fildes)
1118{
1119 struct filestr_desc * const file = GET_FILESTR(READER, fildes);
1120 if (!file)
1121 FILE_ERROR_RETURN(ERRNO, -1);
1122
1123 off_t rc;
1124 file_size_t size = *file->sizep;
1125
1126 if (size > FILE_SIZE_MAX)
1127 FILE_ERROR(EOVERFLOW, -2);
1128
1129 rc = (off_t)size;
1130file_error:
1131 RELEASE_FILESTR(READER, file);
1132 return rc;
1133}
1134
1135/* test if two file descriptors refer to the same file */
1136int fsamefile(int fildes1, int fildes2)
1137{
1138 struct filestr_desc * const file1 = GET_FILESTR(WRITER, fildes1);
1139 if (!file1)
1140 FILE_ERROR_RETURN(ERRNO, -1);
1141
1142 int rc = -2;
1143
1144 struct filestr_desc * const file2 = get_filestr(fildes2);
1145 if (file2)
1146 rc = file1->stream.bindp == file2->stream.bindp ? 1 : 0;
1147
1148 RELEASE_FILESTR(WRITER, file1);
1149 return rc;
1150}
1151
1152/* tell the relationship of path1 to path2 */
1153int relate(const char *path1, const char *path2)
1154{
1155 /* this is basically what rename() does but reduced to the relationship
1156 determination */
1157 DEBUGF("relate(path1=\"%s\",path2=\"%s\")\n", path1, path2);
1158
1159 int rc, open1rc = -1, open2rc = -1;
1160 struct filestr_base str1, str2;
1161 struct path_component_info info1, info2;
1162
1163 file_internal_lock_WRITER();
1164
1165 open1rc = open_stream_internal(path1, FF_ANYTYPE, &str1, &info1);
1166 if (open1rc <= 0)
1167 {
1168 DEBUGF("Failed opening path1: %d\n", rc);
1169 if (open1rc < 0)
1170 FILE_ERROR(ERRNO, open1rc * 10 - 1);
1171 else
1172 FILE_ERROR(ENOENT, -1);
1173 }
1174
1175 info2.prefixp = str1.infop;
1176 open2rc = open_stream_internal(path2, FF_ANYTYPE | FF_CHECKPREFIX,
1177 &str2, &info2);
1178 if (open2rc < 0)
1179 {
1180 DEBUGF("Failed opening path2: %d\n", rc);
1181 FILE_ERROR(ERRNO, open2rc * 10 - 2);
1182 }
1183
1184 rc = RELATE_DIFFERENT;
1185
1186 if (open2rc > 0)
1187 {
1188 if (str1.bindp == str2.bindp)
1189 rc = RELATE_SAME;
1190 else if (info2.attr & ATTR_PREFIX)
1191 rc = RELATE_PREFIX;
1192 }
1193 else /* open2rc == 0 */
1194 {
1195 /* path1 existing and path2's final part not can only be a prefix or
1196 different */
1197 if (info2.attr & ATTR_PREFIX)
1198 rc = RELATE_PREFIX;
1199 }
1200
1201file_error:
1202 if (open1rc >= 0)
1203 close_stream_internal(&str1);
1204
1205 if (open2rc >= 0)
1206 close_stream_internal(&str2);
1207
1208 file_internal_unlock_WRITER();
1209 return rc;
1210}
1211
1212/* test file or directory existence */
1213bool file_exists(const char *path)
1214{
1215 file_internal_lock_WRITER();
1216 bool rc = test_stream_exists_internal(path, FF_ANYTYPE) > 0;
1217 file_internal_unlock_WRITER();
1218 return rc;
836} 1219}