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