TARFS 0.1.5
Read-only TAR filesystem for ESP32
Loading...
Searching...
No Matches
file.c
Go to the documentation of this file.
1/*
2 * TARFS - Immutable (read-only) filesystem for embedded systems.
3 *
4 * Copyright (c) 2026 Viacheslav Logunov
5 * SPDX-License-Identifier: MMIT
6 *
7 * Author:
8 * Viacheslav Logunov <vvb333007@gmail.com>
9 *
10 * Project:
11 * https://github.com/vvb333007/tarfs
12 */
13
14
15/*
16 * Thread Safety Notes
17 * ===================
18 *
19 * close() during an active read():
20 *
21 * In TARFS, every read() operation is atomic and guaranteed to complete,
22 * even if another thread successfully calls close() before read() returns.
23 * In this case, the file descriptor is closed, but the ongoing read()
24 * completes as if it were still open.
25 *
26 * However, if unmount() has already been called, close() may release the
27 * filesystem and unmap the underlying image. In this case, an ongoing
28 * read() may access unmapped memory, resulting in undefined behavior
29 * (typically a crash).
30 *
31 * To avoid this race (if you really need to call unmount() and close() while
32 * another thread is reading from the same file descriptor), provide the
33 * necessary synchronization in your application or, preferably, redesign
34 * the application logic.
35 */
36
37
38#include <stdint.h>
39#include <stdarg.h>
40#include <stdlib.h>
41#include <stdio.h>
42#include <stdbool.h>
43#include <stdatomic.h>
44#include <string.h>
45
46#include <unistd.h>
47#include <dirent.h>
48#include <sys/errno.h>
49#include <sys/fcntl.h>
50#include <sys/utime.h>
51#include <assert.h>
52#include <fcntl.h>
53#include <time.h>
54
55#include "config.h"
56
57#if CONFIG_TARFS_HAVE_SENDFILE
58# include <sys/socket.h>
59#endif
60
61#include "os.h"
62#include "tar.h"
63#include "fs.h"
64#include "posix.h"
65
66
67/*
68 * Common function prologue.
69 *
70 * Note that tarf_open() uses a hardcoded version of the PROLOGUE() macro.
71 * Whenever this macro is modified, the corresponding code in tarf_open()
72 * must be updated accordingly.
73 *
74 * The hardcoded version differs from this macro in two ways:
75 *
76 * - it performs the is_sanefd() check differently;
77 * - it obtains the filesystem context by incrementing its reference
78 * count, whereas this macro simply retrieves the filesystem pointer,
79 * assuming that open() has already acquired the reference.
80 */
81
82#define PROLOGUE( TYPE ) \
83 struct tarfs_fs *fs = NULL; \
84\
85 int fs_idx = (intptr_t )ctx; \
86\
87 if (false == (fs_idx >= 0 && \
88 fs_idx < TARFS_MAX_FS && \
89 ((fs = tarfs_getfs(fs_idx)) != NULL))) { \
90\
91 log("FS#%d is not mounted\r\n",fs_idx); \
92 errno = EIO; \
93 return (TYPE)(-1); \
94 } \
95 if (!is_sanefd(fs, fd)) { \
96 log("FS#%d, fd#%d is not open\r\n", fs_idx, fd); \
97 errno = EBADF; \
98 return (TYPE)(-1); \
99 }
100
101
102/* Macro to increment stats */
103#if CONFIG_TARFS_COUNTERS
104# define ADD_STATS(X, Y) do { X += Y; } while(0)
105#else
106# define ADD_STATS(X, Y) do {} while(0)
107#endif
108
109
110
111/* Bitmap is used only for fd allocation. It is NOT used as a publication mechanism.
112 * Descriptor contents are initialized before open() returns and become visible through
113 * the VFS call chain. Therefore memory_order_relaxed is sufficient. Period.
114 */
115static const uint32_t s_valid_mask = (TARFS_MAX_FDS == 32) ? 0xffffffffUL : ((1UL << TARFS_MAX_FDS) - 1UL);
116
117
129static int allocfd(struct tarfs_fs *fs) {
130
131 int index;
132 uint32_t free_mask;
133 uint32_t used_indices, new_value;
134
135 do {
136 used_indices = atomic_load_explicit(&fs->fs_usedfd, memory_order_relaxed);
137 free_mask = ~used_indices & s_valid_mask;
138
139 if (free_mask == 0)
140 return -1;
141
142 index = __builtin_ctz(free_mask);
143 new_value = used_indices | (1u << index);
144
145 } while (!atomic_compare_exchange_weak_explicit(
146 &fs->fs_usedfd,
147 &used_indices,
148 new_value,
149 memory_order_relaxed,
150 memory_order_relaxed));
151
152 return index;
153}
154
158static void freefd(struct tarfs_fs *fs, int index) {
159
160 uint32_t used_indices, new_value;
161
162 if (index < 0 || index >= TARFS_MAX_FDS)
163 return ;
164
165 do {
166 used_indices = atomic_load_explicit(&fs->fs_usedfd, memory_order_relaxed);
167 new_value = used_indices & ~(1u << index);
168
169 } while (!atomic_compare_exchange_weak_explicit(
170 &fs->fs_usedfd,
171 &used_indices,
172 new_value,
173 memory_order_relaxed,
174 memory_order_relaxed));
175}
176
177
182static bool is_sanefd(struct tarfs_fs *fs, int fd) {
183
184 return (fd >= 0) &&
185 (fd < TARFS_MAX_FDS) &&
186 ((atomic_load_explicit(&fs->fs_usedfd, memory_order_relaxed) & (1u << fd)) != 0);
187}
188
189
190
191/*
192 * File operation handlers.
193 *
194 * These functions are called either by the VFS (on systems that provide one)
195 * or directly by the application.
196 *
197 * When called directly, it is the caller's responsibility to pass the
198 * filesystem context as the first argument. Although its type is void *,
199 * the filesystem context is actually an integer index (not a pointer) in
200 * the range [0..TARFS_MAX_FS). This index is returned by tarfs_mount().
201 */
202
203
210int tarf_access(void* ctx, const char *path, int amode) {
211
212 if (amode & X_OK)
213 errno = EPERM;
214 else if (amode & W_OK)
215 errno = EROFS;
216 else {
217 /*
218 * `errno` is set by tarf_stat() if needed
219 */
220 struct stat st;
221 if (tarf_stat(ctx, path, &st) == 0) {
222 if (S_ISREG(st.st_mode)) {
223
224 /* F_OK is 0 on glibc so we have to check it */
225 if ((amode & (F_OK|R_OK)) || amode == F_OK)
226 return 0;
227
228 } else if (S_ISDIR(st.st_mode)) {
229
230 /* Directories can not be read() */
231 if (amode & R_OK) {
232 errno = EISDIR;
233 return -1;
234 }
235 /* Yes, directory do exist */
236 if ((amode & F_OK) || (amode == F_OK))
237 return 0;
238 }
239 errno = ENOSYS;
240 }
241 }
242
243 return -1;
244}
245
252int tarf_open(void* ctx, const char * path0, int flags, int mode) {
253
254 tart_t type;
255 size_t size;
256 time_t mtime;
257 int fd = 0;
258 const char *path = path0;
259 char *alt_path = NULL;
260
261 mode = mode; /* UNUSED */
262
263 struct tarfs_fs *fs = NULL;
264 int const fs_idx = (int )(intptr_t )ctx;
265
266 /* Quick reject */
267 if (path == NULL) {
268 ADD_STATS(fs->fs_nfail, 1);
269 errno = EFAULT;
270 return -1;
271 }
272 if (((flags & O_ACCMODE) == O_WRONLY) || (flags & O_TRUNC)) {
273
274 ADD_STATS(fs->fs_nfail, 1);
275 errno = EROFS;
276
277 return -1;
278 }
279
280 /* Check fs_idx and grab the filesystem
281 * add a reference to the filesystem. this will ensure that context remain alive until tarf_close()
282 */
283 if ( NULL == (fs = tarfs_getfs_addref(fs_idx))) {
284
285 ADD_STATS(fs->fs_nfail, 1);
286
287 log("bad filesystem context: %d, %p\r\n",fs_idx, (void *)fs);
288 errno = EIO;
289
290 return -1;
291 }
292
293 /* Find corresponding inode.
294 * If user asks for a directory, we try to open what was asked and, if it fails, we
295 * are trying to open() an alternative path (with a trailing / added or removed)
296 * This is where we DO allocations in open(). Normally this should not happen because user
297 * can supply paths with / at the end so they can be found from the first ttry
298 */
299 int idx = inode_lookup(fs->fs_ino, fs->fs_nino,path);
300
301 if (idx < 0) {
302
303 errno = -idx;
304
305 if (flags & O_DIRECTORY) {
306
307 int i = strlen(path);
308
309 /*
310 * Create an alternative path by adding or removing the trailing '/'.
311 *
312 * If the initial inode_lookup() fails and O_DIRECTORY is specified,
313 * try the alternative path as well. POSIX requires directory names
314 * with and without a trailing '/' (e.g. "dirname" and "dirname/")
315 * to be treated equivalently.
316 *
317 * Our filesystem metadata may contain both forms: for example,
318 * hard links may have an explicit trailing '/', while PAX symlink
319 * targets may not.
320 */
321
322 if (i > 0) {
323
324 if (path[i - 1] != '/') {
325
326 alt_path = tar_strdup1(path, NULL); /* NUL+NUL terminated string */
327
328 if (alt_path != NULL)
329 alt_path[i] = '/';
330 else
331 ADD_STATS(fs->fs_nfail, 1);
332
333 } else {
334
335 alt_path = tar_strdup1(path, NULL);
336
337 if (alt_path != NULL)
338 alt_path[i - 1] = '\0';
339 else
340 ADD_STATS(fs->fs_nfail, 1);
341 }
342 }
343
344 if (alt_path != NULL) {
345 log("trying '%s' instead..\r\n",alt_path);
346 idx = inode_lookup(fs->fs_ino, fs->fs_nino,alt_path);
347 tarfs_os_free(alt_path);
348 alt_path = NULL;
349 }
350 } /* if O_DIRECTORY */
351 } /* Seconf attempt */
352
353 if (idx < 0) {
354 log("path '%s' not found\r\n",path);
355 if (errno == 0)
356 errno = -idx;
357 goto unref_and_exit;
358 }
359
360 /* Check inode: if it has NULL data pointer then it means it was not correctly mounted
361 * i.e. archive was damaged or may be it was a symlink which was not correctly resolved.
362 * NULL in_dvaddr == BAD INODE
363 */
364 struct tarfs_inode const *inode = fs->fs_ino[idx];
365 if (inode->in_dvaddr == 0) {
366 ADD_STATS(fs->fs_nfail, 1);
367 log("floating inode '%s', no data\r\n", path);
368 errno = EIO;
369 goto unref_and_exit;
370 }
371
372
373 /* Determine object type and size. */
374 type = inode_getinfo(fs->fs_ino, idx, &size, &mtime);
375
376 /* O_DIRECTORY requires the target to be a directory.
377 * Absence of O_DIRECTORY requires the target NOT to be a directory.
378 */
379 if ((flags & O_DIRECTORY) != 0) {
380 if (type != TART_DIR) {
381 log("O_DIRECTORY specified for a non-directory object\r\n");
382 errno = ENOTDIR;
383 goto unref_and_exit;
384 }
385 /* Success: A directory was requested, a directory has been found.
386 * Set size to 0 for directories. It must be zero by default but just to be sure. Th reason for that
387 * is that read() must fail when reading any fd associated with a directory
388 */
389 size = 0;
390 } else {
391 if (type == TART_DIR) {
392 log("target is a directory, O_DIRECTORY flag is required\r\n");
393 errno = EISDIR;
394 goto unref_and_exit;
395 }
396 /* Success: a file was requested, file has been found.
397 * Perform CRC64 check if FS is configured to do so (CRC64 on open() is off by default.)
398 */
399#if CONFIG_TARFS_INTEGRITY
400 if (fs->fs_opencrc) {
401 if (tar_baddata((struct tarhdr const *)inode->in_dvaddr, size)) {
402 log("data integrity check failed for '%s'\r\n", path);
403 errno = EIO;
404 goto unref_and_exit;
405 }
406 }
407#endif /* #if CONFIG_TARFS_INTEGRITY */
408 } /* File or Directory? */
409
410
411 /* Allocate a file descriptor. It is atomic bitmap but we do not use publish/consume semantics.
412 * Instead we rely on that fact that fd becomes available only upon open() return, so publishing is done by
413 * function return
414 */
415 if ((fd = allocfd(fs)) >= 0) {
416 struct tarfs_fp *fp = &fs->fs_fd[fd];
417 log("fd=%d allocated\r\n", fd);
418
419
420 fp->fp_vaddr = inode->in_dvaddr + sizeof(struct tarhdr);
421 fp->fp_pos = 0;
422 fp->fp_size = size;
423 fp->fp_idx = idx; /* inode index, used by opendir()/readdir() */
424
425 log("success, ino=%d, type=%c, path=%s, fd=%d, size=%u, vaddr=%p\r\n",idx, type, path, fd,(unsigned int)fp->fp_size, (void *)fp->fp_vaddr);
426
427 return fd;
428 }
429
430 log("allocfd failed for path=%s\r\n",path);
431 errno = EMFILE;
432
433 ADD_STATS(fs->fs_nfail, 1);
434
435unref_and_exit:
436
437 tarfs_unref(fs);
438 return -1;
439}
440
441/* close()
442 * FS has at least 1 extra ref, because of open()
443 * double close() will fail at PROLOGUE() stage as it calls is_sanefd()
444 */
445int tarf_close(void* ctx, int fd) {
446
447
448 PROLOGUE( int );
449
450 log("closing fd=%d, fs_idx=%d\r\n", fd, fs_idx);
451
452 freefd(fs, fd);
453 tarfs_unref(fs);
454
455 return 0;
456}
457
458
464ssize_t tarf_pread(void* ctx, int fd, void *dst, size_t size, off_t offset) {
465
466
467 PROLOGUE( ssize_t );
468
469 struct tarfs_fp *fp = &fs->fs_fd[fd];
470
471 if (offset <0) {
472 errno = EINVAL;
473 return -1;
474 }
475
476 size_t off = (size_t)offset;
477
478 if (fp->fp_size <= off)
479 return 0;
480
481
482 /* Compute the source address: file_base + requested offset.
483 * In case of EOF this address will point one byte past the file end.
484 * This is permitted by the C standard as long as the pointer is not
485 * dereferenced.
486 */
487
488 void const *src = (void const *)(fp->fp_vaddr + off);
489
490 /* Clamp the read size so we never read past the end of the file.
491 * `size` can become zero after the clamp (happens for directories for example), it is normal
492 */
493 if (fp->fp_size - off < size)
494 size = fp->fp_size - off;
495
496 /* Copy the requested data.
497 * Use an architecture-optimized memcpy() where available (e.g. ESP32-S3, P4, etc.).
498 */
499 if (size > 0) {
500 tarfs_os_memcpy(dst, src, size);
501 ADD_STATS(fs->fs_bread, size);
502 }
503
504 /* Return number of data copied */
505 return size;
506}
507
508
512ssize_t tarf_read(void* ctx, int fd, void *dst, size_t size) {
513
514
515 PROLOGUE( ssize_t );
516
517 struct tarfs_fp *fp = &fs->fs_fd[fd];
518
519 ssize_t siz = tarf_pread(ctx, fd, dst, size, (off_t)fp->fp_pos);
520
521 if (siz > 0)
522 fp->fp_pos += (size_t)siz;
523
524
525 return siz;
526}
527
528
533off_t tarf_lseek(void* ctx, int fd, off_t offset, int whence) {
534
535
536 PROLOGUE( off_t );
537
538 struct tarfs_fp *fp = (struct tarfs_fp *)(&fs->fs_fd[fd]);
539
540 /* Check if lseek() results in a valid fp_pos:
541 * it is different from POSIX which allows lseek() to set pos beyond the file end.
542 * We allow pointer to be set to the first byte after the file end. In this case
543 * lseek(fd, 0, SEEK_END) will position the pointer, while read() will return 0;
544 */
545
546 if (whence == SEEK_SET) {
547
548 if (offset >= 0 && offset <= fp->fp_size) {
549 fp->fp_pos = offset;
550 return (off_t)fp->fp_pos;
551 }
552
553 } else if (whence == SEEK_END) {
554
555 /* Unlike POSIX, TARFS never allows the file position to move past EOF.
556 * The only valid position at the end of the file is exactly EOF:
557 *
558 * lseek(fd, 0, SEEK_END)
559 *
560 * After positioning at EOF, subsequent read() calls return 0.
561 */
562 if (offset <= 0) {
563 uint64_t back = (uint64_t)(-(offset + 1)) + 1; /* 64bit arith here is for reason */
564
565 if (back <= fp->fp_size) {
566 fp->fp_pos = fp->fp_size - back;
567 return (off_t)fp->fp_pos;
568 }
569 }
570 } else if (whence == SEEK_CUR) {
571
572 off_t new_offset = offset + (off_t)fp->fp_pos;
573 if (new_offset >= 0 && new_offset <= fp->fp_size) {
574 fp->fp_pos = new_offset;
575 return (off_t)fp->fp_pos;
576 }
577 }
578
579 log("bad whence(%d) or/and offset(%ld)\r\n", whence, offset);
580
581 errno = EINVAL;
582 return (off_t)(-1);
583}
584
589int tarf_fstat(void* ctx, int fd, struct stat * st) {
590
591 PROLOGUE( int ); /* defines fs_idx */
592
593 tart_t type;
594 time_t mtime;
595
596 struct tarfs_fp *fp = &fs->fs_fd[fd];
597
598 type = inode_getinfo(fs->fs_ino, fp->fp_idx, NULL, &mtime);
599
600 memset(st, 0, sizeof(struct stat));
601
602 uint32_t perm = S_IRUSR|S_IRGRP|S_IROTH; /* default read-only permissions */
603
604 if (type == TART_DIR)
605 perm |= S_IXUSR|S_IXGRP|S_IXOTH|S_IFDIR;
606 else
607 perm |= S_IFREG;
608
609 st->st_dev = (dev_t)fs_idx; /* Filesystem index [0..TARFS_MAX_FS) */
610 st->st_ino = (ino_t)fp->fp_idx; /* Inode index */
611 st->st_blksize = 512;
612 st->st_blocks = ((fp->fp_size + 511) & ~511) / 512;
613 st->st_size = fp->fp_size;
614 st->st_mode = perm;
615 st->st_mtime = fs->fs_mtime;
616 st->st_atime = 0;
617 st->st_ctime = fs->fs_mtime;
618
619 return 0;
620}
621
622#if CONFIG_TARFS_HAVE_LSTAT
627int tarf_lstat(void* ctx, const char *path, struct stat * st) {
628
629 tart_t type;
630 struct tarfs_fp *fp;
631 struct tarfs_fs *fs = NULL;
632 struct tarfs_inode const *ino;
633 size_t size;
634 int fd, fs_idx = (int)(intptr_t )ctx;
635 unsigned int in_idx;
636 uint32_t perm;
637 time_t tim;
638
639 /* Locate the inode, by open()ing it*/
640 if ((fd = tarf_open(ctx, path, 0, 0)) < 0) {
641 if ((fd = tarf_open(ctx, path, O_DIRECTORY, 0)) < 0) {
642 log("path not found: %s\r\n", path);
643 return -1;
644 }
645 }
646
647 /* open() succeeded == fs_idx is sane; get the FS pointer by its index
648 * no tarfs_getfs_addref() here, open() did it for us
649 */
650 if ((fs = tarfs_getfs(fs_idx)) == NULL) {
651
652 log("bad filesystem context: %d\r\n",fs_idx);
653 errno = EIO;
654 return -1;
655 }
656
657 /* Fetch all required information and close the file/directory/link ASAP */
658 tim = fs->fs_mtime;
659 fp = &fs->fs_fd[fd];
660 in_idx = fp->fp_idx;
661 ino = fs->fs_ino[in_idx];
662 type = inode_rawtype( ino );
663 size = fp->fp_size;
664
665 /* Can be closed now */
666 tarf_close(ctx, fd);
667
668 memset(st, 0, sizeof(struct stat));
669
670 perm = S_IRUSR|S_IRGRP|S_IROTH; /* default read-only permissions */
671
672 /* Adjust permissions, and size */
673 if (type == TART_DIR) {
674 perm |= S_IXUSR|S_IXGRP|S_IXOTH|S_IFDIR;
675 size = 0;
676 } else if (type == TART_HARDLINK || type == TART_SYMLINK) {
677 perm |= S_IFLNK;
678 size = 0;
679 } else {
680 perm |= S_IFREG;
681 }
682
683 st->st_dev = (dev_t)fs_idx; /* Filesystem index [0..TARFS_MAX_FS) */
684 st->st_ino = (ino_t)in_idx; /* Inode index */
685 st->st_blksize = 512;
686 st->st_blocks = ((size + 511) & ~511) / 512;
687 st->st_size = size;
688 st->st_mode = perm;
689 st->st_mtime = fs->fs_mtime;
690 st->st_atime = fs->fs_mtime;
691 st->st_ctime = fs->fs_mtime;
692
693 return 0;
694}
695#endif
696
697
702int tarf_fsync(void* ctx, int fd) {
703
704 PROLOGUE( int );
705
706 return 0;
707}
708
709/*
710 * Perform file descriptor control operations.
711 *
712 * Supports a minimal subset of POSIX @c fcntl() commands.
713 * TARFS is inherently non-blocking, therefore @c F_SETFL is accepted
714 * but ignored. @c F_GETFL always reports @c O_NONBLOCK.
715 */
716int tarf_fcntl(void *ctx, int fd, int cmd, int arg) {
717
718 PROLOGUE( int );
719
720 arg = arg;
721
722 switch (cmd) {
723
724 case F_GETFL:
725 return O_NONBLOCK | O_RDONLY; /* O_RDONLY is usually 0, but who knows */
726
727 case F_SETFL:
728 return 0;
729
730 default:
731 errno = ENOSYS;
732 };
733 return -1;
734}
735
740int tarf_ioctl(void *ctx, int fd, int cmd, va_list args) {
741
742
743 PROLOGUE( int );
744
745 switch (cmd) {
746
747 /* Bytes left == File_Size - File_Current_Position */
748 case FIONREAD:
749 {
750 int *o;
751 o = va_arg(args, int *);
752 if (o == NULL) {
753 errno = EFAULT;
754 return -1;
755 }
756 *o = fs->fs_fd[fd].fp_size - fs->fs_fd[fd].fp_pos;
757 return 0;
758 }
759 /* TARFS is non-blocking by design. Just ignore O_NONBLOCK */
760 case FIONBIO:
761 return 0;
762
763 /* Return local FD number and the FS index
764 */
765 case FIOGETFD:
766 {
767 struct ioctl_req *out = va_arg(args, struct ioctl_req *);
768
769 if (out) {
770
771 out->fd = fd;
772 out->fs_idx = fs_idx;
773
774 return 0;
775 }
776 errno = EFAULT;
777 return -1;
778 }
779 default:
780 break;
781 };
782 errno = ENOSYS;
783 return -1;
784}
785
786
790int tarf_stat(void* ctx, const char * path, struct stat * st) {
791
792 int fd;
793
794
795 if ((fd = tarf_open(ctx, path, O_RDONLY, 0)) < 0) {
796 if ((fd = tarf_open(ctx, path, O_RDONLY | O_DIRECTORY, 0)) < 0) {
797 log("path does not exist (not file not directory)");
798 return -1;
799 }
800 }
801
802 int rc = tarf_fstat(ctx, fd, st);
803
804 tarf_close(ctx, fd);
805
806 return rc;
807}
808
809
817int tarf_dupfd(void *ctx, int fd) {
818
819 PROLOGUE(int);
820
821 /* allocfd() is called under addref() : open() adds areference */
822 int dup = allocfd(fs);
823
824 if (dup >= 0) {
825
826 fs->fs_fd[dup] = fs->fs_fd[fd];
827 tarfs_addref(fs);
828 log("fd=%d was duplicated as fd=%d\r\n", fd, dup);
829
830 } else {
831
832 errno = EMFILE;
833 log("fd=%d NOT duplicated, EMFILE!\r\n", fd);
834
835 }
836 return dup;
837}
838
839
846void *tarf_mmap(void *ctx, void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
847
848
849 PROLOGUE(void *);
850
851 struct tarfs_fp *fp;
852
853 /* Check incompatible flags */
854 if (((flags & MAP_FIXED) && addr != NULL) ||
855 ((flags & MAP_ANONYMOUS) && fd < 0) ||
856 ((prot & (PROT_WRITE|PROT_EXEC)) != 0)) {
857
858 log("Unsupported combination of flags/protection\r\n");
859 errno = EINVAL;
860 return MAP_FAILED;
861 }
862
863 fp = &fs->fs_fd[fd];
864
865 /* Check if args are compatible with the fp */
866 if (fp->fp_vaddr == 0 ||
867 offset < 0 ||
868 offset > fp->fp_size ||
869 length > (fp->fp_size - offset)) {
870
871 log("failed: offset=%u, fp_size=%u, length=%u\r\n", (unsigned int)offset, (unsigned int)fp->fp_size, (unsigned int)length);
872 errno = EINVAL;
873
874 return MAP_FAILED;
875 }
876
877 tarfs_addref(fs);
878 ADD_STATS(fs->fs_bmmap, length);
879
880 log("fd=%d, mapped %u bytes vaddr=%p, offset=%d\r\n",fd, (unsigned int)length, (void *)fp->fp_vaddr, (int)offset);
881
882 /* Partition is mmaped already by mount, here we just calculate the right
883 * memory offset
884 */
885 return (void *)(fp->fp_vaddr + offset);
886}
887
888
898int tarf_munmap(void *ctx, void *addr, size_t length) {
899
900 struct tarfs_fs *fs = NULL;
901
902 int fs_idx = (intptr_t )ctx;
903
904 if (false == (fs_idx >= 0 &&
905 fs_idx < TARFS_MAX_FS &&
906 ((fs = tarfs_getfs(fs_idx)) != NULL))) {
907
908 log("filesystem #%d is not mounted\r\n",fs_idx);
909 errno = EIO;
910 return -1;
911 }
912
913 log("unmapping ptr=%p (len=%u) from fs_idx=%d\r\n",(void *)addr, (unsigned int)length, fs_idx);
914 tarfs_unref(fs);
915 return 0;
916}
917
918
919#if CONFIG_TARFS_HAVE_SENDFILE
926ssize_t tarf_sendfile(void *ctx, int sock, int fd, off_t *offset, size_t count) {
927
928 uint8_t *data;
929 off_t pos;
930 struct tarfs_fp *fp;
931 size_t total = 0;
932
933 if (count == 0)
934 return 0;
935
936 PROLOGUE( ssize_t );
937
938 /* obtain the file pointer and check if it is not NULL */
939 fp = &fs->fs_fd[fd];
940
941 /* Extra paranoia: normally open() filters out bad inodes with NULL data so there are no chances for
942 * zero fp_vaddr. But just in case
943 */
944 if (fp->fp_vaddr == 0) {
945
946 log("bad virtual address, fd=%d\r\n", fd);
947 errno = EIO;
948 return -1;
949 }
950
951 /* obtain the file position: either use current pos or use user-supplied pos
952 */
953 if (offset != NULL ) {
954
955 pos = *offset;
956
957 /* sanity checks: we don't trust user-supplied position */
958 if (pos < 0 || pos > fp->fp_size) {
959 log("bad user-supplied pos=%d\r\n",(int)pos);
960 errno = EINVAL;
961 return -1;
962 }
963
964 } else
965 pos = fp->fp_pos;
966
967 /* clamp count if needed */
968 if (count > (fp->fp_size - pos))
969 count = fp->fp_size - pos;
970
971 /* obtain direct pointer to the file data */
972 data = (void *)(fp->fp_vaddr + pos);
973
974 /* Send. We are trying to send everything at once. If send() transmits less than
975 * was requested - we just continue to send the rest
976 */
977 while (total < count) {
978
979 ssize_t n = send(sock, data + total, count - total, 0);
980
981 if (n < 0) {
982 if (errno == EINTR)
983 continue;
984
985 log("send failed\r\n");
986 break;
987 }
988
989 if (n == 0) {
990 log("eof\r\n");
991 break;
992 }
993
994 log("sent %u\r\n", (unsigned int)n);
995 total += n;
996 }
997
998 /* Update offset: either user-supplied or real file offset */
999 if (total) {
1000 if (offset)
1001 *offset += total;
1002 else {
1003 fp->fp_pos += total;
1004
1005 /* Extra paranoia */
1006 if (fp->fp_pos > fp->fp_size)
1007 fp->fp_pos = fp->fp_size;
1008 }
1009 }
1010
1011 /* Done */
1012 return total;
1013}
1014#endif /* #if CONFIG_TARFS_HAVE_SENDFILE */
1015
1016/* Compile-time sanity checks */
1017_Static_assert(TARFS_MAX_FDS > 0 && TARFS_MAX_FDS <= 32, "TARFS_MAX_FD must be in range [1..32]");
int tarf_stat(void *ctx, const char *path, struct stat *st)
stat() system call
Definition file.c:790
static bool is_sanefd(struct tarfs_fs *fs, int fd)
Extra paranoia: we check fds which are passed to us by VFS layer for being in our range [0 .
Definition file.c:182
off_t tarf_lseek(void *ctx, int fd, off_t offset, int whence)
lseek()
Definition file.c:533
ssize_t tarf_sendfile(void *ctx, int sock, int fd, off_t *offset, size_t count)
Copy data from a TARFS file descriptor to a socket.
Definition file.c:926
int tarf_close(void *ctx, int fd)
Definition file.c:445
int tarf_access(void *ctx, const char *path, int amode)
Check the accessibility of a file or directory in the TarFS filesystem.
Definition file.c:210
int tarf_fstat(void *ctx, int fd, struct stat *st)
Get file status information.
Definition file.c:589
ssize_t tarf_pread(void *ctx, int fd, void *dst, size_t size, off_t offset)
Read data from an open TARFS file at a specified offset.
Definition file.c:464
int tarf_ioctl(void *ctx, int fd, int cmd, va_list args)
Perform TARFS-specific I/O control operations.
Definition file.c:740
ssize_t tarf_read(void *ctx, int fd, void *dst, size_t size)
Read data from an open TARFS file.
Definition file.c:512
void * tarf_mmap(void *ctx, void *addr, size_t length, int prot, int flags, int fd, off_t offset)
Mimics POSIX mmap().
Definition file.c:846
int tarf_dupfd(void *ctx, int fd)
Create an independent duplicate of a file descriptor.
Definition file.c:817
#define PROLOGUE(TYPE)
Definition file.c:82
int tarf_fcntl(void *ctx, int fd, int cmd, int arg)
Perform file descriptor control operations.
Definition file.c:716
int tarf_munmap(void *ctx, void *addr, size_t length)
munmap().
Definition file.c:898
static void freefd(struct tarfs_fs *fs, int index)
Marks previously allocated index as free.
Definition file.c:158
int tarf_open(void *ctx, const char *path0, int flags, int mode)
Open a TARFS file or directory.
Definition file.c:252
int tarf_fsync(void *ctx, int fd)
Synchronize file contents.
Definition file.c:702
static const uint32_t s_valid_mask
Definition file.c:115
#define ADD_STATS(X, Y)
Definition file.c:104
static int allocfd(struct tarfs_fs *fs)
Allocate a file descriptor slot.
Definition file.c:129
int tarfs_unref(struct tarfs_fs *fs)
Definition fs.c:260
struct tarfs_fs * tarfs_getfs(int i)
Lockless, not thread safe, does not increase refcounters.
Definition fs.c:54
int tarfs_addref(struct tarfs_fs *fs)
Filesystem reference counting.
Definition fs.c:253
struct tarfs_fs * tarfs_getfs_addref(int i)
Thread safe, increases refcounter, uses mutex!
Definition fs.c:70
#define TARFS_MAX_FS
Definition fs.h:29
#define FIOGETFD
Definition fs.h:47
#define log(Format_,...)
Definition fs.h:445
#define FIONREAD
Definition fs.h:50
#define FIONBIO
Definition fs.h:53
#define TARFS_MAX_FDS
Definition fs.h:30
tart_t inode_getinfo(struct tarfs_inode const *const *index, int idx, size_t *size, time_t *mtime)
inode_getinfo() : get inode's Type, Size and Mtime These are not precached and must be calculated eve...
Definition inode.c:466
tart_t inode_rawtype(struct tarfs_inode const *ino)
Return raw inode type: TART_HARDLINK, TART_SYMLINK, TART_DIR, TART_FILE or TART_BAD.
Definition inode.c:506
int inode_lookup(struct tarfs_inode const *const *index, size_t num_inodes, const char *path)
Find an inode that corresponds to given path name.
Definition inode.c:399
static void * tarfs_os_memcpy(void *dst, const void *src, size_t len)
read() uses memcpy(), which can be optimized on many architectures
Definition os.h:133
void tarfs_os_free(void *buffer)
Definition os_esp32.c:207
#define PROT_WRITE
Definition posix.h:29
#define MAP_FIXED
Definition posix.h:36
#define PROT_EXEC
Definition posix.h:30
#define MAP_FAILED
Definition posix.h:41
#define MAP_ANONYMOUS
Definition posix.h:37
Return/Request argument for FIOGETFD ioctl.
Definition fs.h:59
int fs_idx
Definition fs.h:60
int fd
Definition fs.h:61
TARFS File API: tarf_open(), tarf_close(), tarf_read(), tarf_pread(), tarf_lseek(),...
Definition file.h:63
uint32_t fp_pos
Definition file.h:66
uintptr_t fp_vaddr
Definition file.h:65
int fp_idx
Definition file.h:68
size_t fp_size
Definition file.h:67
This descriptor holds all file descriptors opened.
Definition fs.h:94
struct tarfs_fp fs_fd[16]
Definition fs.h:106
uint32_t fs_nfail
Definition fs.h:114
tarfs_inode_t const *const * fs_ino
Definition fs.h:102
uint64_t fs_bmmap
Definition fs.h:112
uint16_t fs_opencrc
Definition fs.h:108
uint32_t fs_nino
Definition fs.h:101
time_t fs_mtime
Definition fs.h:107
For TAR files with modified PADDING field (see tarsum.c TARFS Checksum Utility): The type and meaning...
Definition tar.h:55
char * tar_strdup1(const char *s1, const char *s1_end)
Duplicate a TAR string as a regular NUL-terminated C string.
Definition tar.c:171
bool tar_baddata(struct tarhdr const *hdr, size_t size)
Verify CRC64 checksum stored in a TAR archive, if present.
Definition tar.c:529
const tart_t type
Definition tar.h:8
const char mtime[12]
Definition tar.h:6
tart_t
Definition tar.h:46
const char mode[8]
Definition tar.h:2
const char size[12]
Definition tar.h:5