TARFS 0.1.5
Read-only TAR filesystem for ESP32
Loading...
Searching...
No Matches
os_esp32.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 * @file os_esp32.c
14 * @brief Platform porting layer implementation for Espressif MCUs
15 */
16
17
43
44#include <stdint.h>
45#include <stdlib.h>
46#include <string.h>
47#include <stdio.h>
48#include <stdbool.h>
49#include <stdatomic.h>
50
51#include <unistd.h>
52#include <dirent.h>
53#include <sys/errno.h>
54#include <sys/fcntl.h>
55
56#include "freertos/FreeRTOS.h"
57#include "freertos/task.h"
58#include "freertos/semphr.h"
59#include "esp_vfs.h"
60#include "esp_err.h"
61#include "esp_partition.h"
62#include "esp_rom_spiflash.h"
63
64#include "config.h"
65#include "os.h"
66#include "fs.h"
67#include "file.h"
68#include "dir.h"
69#include "inode.h"
70
75enum {
76
77 ESP_PARTITION_SUBTYPE_DATA_TARFS = 0xF0, /* !< TARFS partition, v0 */
78 ESP_PARTITION_SUBTYPE_DATA_TARFS1 = 0xF1, /* !< TARFS partition, v1, encrypted */
79 ESP_PARTITION_SUBTYPE_DATA_TARFS2 = 0xF2, /* !< TARFS partition, v2, ImFS overlay support */
80
81};
82
86
87#if CONFIG_VFS_SUPPORT_DIR /* <-- defined in ESP-IDF */
88
89static const esp_vfs_dir_ops_t s_tarfs_dir = {
90
91 .stat_p = &tarf_stat,
92 .opendir_p = &tard_opendir,
93 .closedir_p = &tard_closedir,
94 .readdir_p = &tard_readdir,
95 .seekdir_p = &tard_seekdir,
96 .telldir_p = &tard_telldir,
97 .access_p = &tarf_access,
98};
99#else
100# warning "Dir support is disabled in VFS"
101#endif
102
103
104/* VFS file operations
105 *
106 */
107static const esp_vfs_fs_ops_t s_tarfs_fs = {
108
109 .lseek_p = &tarf_lseek,
110 .read_p = &tarf_read,
111 .pread_p = &tarf_pread,
112 .open_p = &tarf_open,
113 .close_p = &tarf_close,
114 .fstat_p = &tarf_fstat,
115 .fcntl_p = &tarf_fcntl,
116 .ioctl_p = &tarf_ioctl,
117 .fsync_p = &tarf_fsync,
118#if CONFIG_VFS_SUPPORT_DIR
119 .dir = &s_tarfs_dir,
120#endif
121};
122
123/*
124 * TARFS uses a single recursive mutex, if one is available on the platform.
125 * If recursive mutexes are not available, this is not a problem: TARFS can
126 * still operate without a mutex, but with some limitations.
127 *
128 * In particular, mount() and unmount() will no longer be protected by
129 * the mutex, and this must be taken into account by the platform
130 * implementation.
131 *
132 *
133 * void tarfs_os_init(); --> create a recursive mutex (or no-op)
134 * void tarfs_os_acquire_mutex(); --> acquire the mutex (or no-op)
135 * void tarfs_os_release_mutex(); --> release the mutex (or no-op)
136 *
137 * These functions take no arguments and return no values. The mutex itself
138 * and its implementation must be completely hidden from the filesystem
139 * through this interface.
140 */
141static SemaphoreHandle_t s_lock = NULL;
142
143
148
149 if (s_lock == NULL)
150 s_lock = xSemaphoreCreateRecursiveMutex();
151}
152
157
158 if (s_lock != NULL) do {
159 /* absolutely nothing */
160 } while (pdFALSE == xSemaphoreTake(s_lock, portMAX_DELAY));
161}
162
167
168 if (s_lock != NULL)
169 xSemaphoreGive(s_lock);
170}
171
172/* Maximum mountpoint length (not counting \0 )
173 *
174 */
176
177 return sizeof(((esp_partition_t *)0)->label) - 1;
178}
179
180/*
181 * Default allocator.
182 *
183 * Allocations of 1 KiB or larger are attempted from SPIRAM first
184 * when external memory support is enabled. If SPIRAM is unavailable
185 * or the allocation fails, the default heap is used as a fallback.
186 */
187void *tarfs_os_malloc(size_t size) {
188
189 void *ptr = NULL;
190
191#if CONFIG_TARFS_EXTMEM
192 if (size > 1023) /* TODO: no magic numbers! */
193 ptr = heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
194
195 if (ptr == NULL)
196 /* fallback to the default allocator */
197#endif
198 ptr = heap_caps_malloc(size, MALLOC_CAP_DEFAULT);
199
200 if (ptr == NULL)
201 errno = ENOMEM;
202
203 return ptr;
204}
205
206/* Default free() */
207void tarfs_os_free(void *buffer) {
208
209 if (buffer != NULL)
210 heap_caps_free(buffer);
211
212}
213
214
222void const *tarfs_os_map_tarfile(const char *label, void **os_handle_out, size_t *size_out) {
223
224 esp_partition_iterator_t i;
225 esp_partition_mmap_handle_t handle;
226 const esp_partition_t *part;
227
228 void const *map;
229
230 if (label == NULL || os_handle_out == NULL) {
231 log("ESP32: invalid arguments\r\n");
232 errno = EFAULT;
233 return NULL;
234 }
235
236 /* Fetch pointer to the FLASH partition descriptor by its label */
237 i = esp_partition_find(ESP_PARTITION_TYPE_DATA,
238 ESP_PARTITION_SUBTYPE_ANY,
239 label);
240 if (i == NULL) {
241 log("ESP32: partition not found '%s'\r\n", label);
242 errno = ENOENT;
243 return NULL;
244 }
245
246 part = esp_partition_get(i);
247 esp_partition_iterator_release(i);
248
249 /* non-NULL iterator can not yield NULL partition pointer, but just in case */
250 if (part == NULL) {
251 log("esp_partition_get() returned NULL, this must not happen!\r\n");
252 errno = EIO;
253 return NULL;
254 }
255
256 if (ESP_OK == esp_partition_mmap(part,0,part->size,ESP_PARTITION_MMAP_DATA,&map,&handle)) {
257 *os_handle_out = (void *)handle;
258 if (size_out)
259 *size_out = part->size;
260
261 log("ESP32: partition '%s' -> vaddr=%p, size=%u\r\n", label, map, (unsigned int)part->size);
262
263 return map;
264 }
265
266 log("esp_partition_mmap() failed\r\n");
267 errno = ENOMEM;
268 return NULL;
269}
270
279void tarfs_os_unmap_tarfile(void *os_handle, const void *map, size_t size) {
280 map = map;
281 size = size;
282 esp_partition_munmap((esp_partition_mmap_handle_t)os_handle);
283}
284
285/* If the platform provides a VFS (Virtual File System), two additional
286 * functions should be implemented:
287 *
288 * bool tarfs_os_register_fs(const char *prefix, void *context)
289 * bool tarfs_os_unregister_fs(const char *prefix)
290 *
291 * TARFS calls tarfs_os_register_fs() when mounting a filesystem.
292 * This function must inform the platform that the path specified by
293 * prefix is now handled by TARFS.
294 *
295 * The context argument is an opaque value that the VFS will pass as the
296 * first argument to tarf_open(), tarf_close(), tarf_read(), tarf_write(), etc.
297 *
298 * The implementation must make no assumptions about the type or meaning
299 * of context. It may be a pointer, an integer value, or even NULL.
300 */
301
307
308 return ESP_OK == esp_vfs_unregister_fs(prefix);
309}
310
311
312
319bool tarfs_os_register_fs(const char *prefix, void *context) {
320
321 return ESP_OK == esp_vfs_register_fs(prefix,
322 &s_tarfs_fs,
323 ESP_VFS_FLAG_CONTEXT_PTR |
324 ESP_VFS_FLAG_STATIC |
325 ESP_VFS_FLAG_READONLY_FS,
326 context);
327}
long tard_telldir(void *ctx, DIR *pdir)
Return the current directory position.
Definition dir.c:357
int tard_closedir(void *ctx, DIR *pdir)
Close a directory stream.
Definition dir.c:260
struct dirent * tard_readdir(void *ctx, DIR *pdir)
Read the next directory entry.
Definition dir.c:293
DIR * tard_opendir(void *ctx, const char *name)
Open a directory for reading.
Definition dir.c:233
void tard_seekdir(void *ctx, DIR *pdir, long offset)
Reposition a directory stream.
Definition dir.c:371
int tarf_stat(void *ctx, const char *path, struct stat *st)
stat() system call
Definition file.c:790
off_t tarf_lseek(void *ctx, int fd, off_t offset, int whence)
lseek()
Definition file.c:533
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
int tarf_fcntl(void *ctx, int fd, int cmd, int arg)
Perform file descriptor control operations.
Definition file.c:716
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
#define log(Format_,...)
Definition fs.h:445
size_t tarfs_os_mp_maxlen()
Return the maximum mount point name length supported by the platform.
Definition os_esp32.c:175
bool tarfs_os_register_fs(const char *prefix, void *context)
Tell the VFS that TARFS is now handle all the paths starting from 'prefix'.
Definition os_esp32.c:319
void * tarfs_os_malloc(size_t size)
Memory allocation backend.
Definition os_esp32.c:187
void tarfs_os_unmap_tarfile(void *os_handle, const void *map, size_t size)
Opposite of tarfs_os_map_tarfile().
Definition os_esp32.c:279
@ ESP_PARTITION_SUBTYPE_DATA_TARFS
Definition os_esp32.c:77
@ ESP_PARTITION_SUBTYPE_DATA_TARFS2
Definition os_esp32.c:79
@ ESP_PARTITION_SUBTYPE_DATA_TARFS1
Definition os_esp32.c:78
void tarfs_os_free(void *buffer)
Definition os_esp32.c:207
bool tarfs_os_unregister_fs(const char *prefix)
Tell the VFS that path 'prefix' is not handled by tarfs anymore.
Definition os_esp32.c:306
void tarfs_os_release_mutex()
Unlock access to s_tarfs[] table and to the s_numfs counter only.
Definition os_esp32.c:166
void tarfs_os_init()
Create a recursive sync object.
Definition os_esp32.c:147
void tarfs_os_acquire_mutex()
Lock access to s_tarfs[] table and to the s_numfs counter only.
Definition os_esp32.c:156
void const * tarfs_os_map_tarfile(const char *label, void **os_handle_out, size_t *size_out)
Map a ESP32 flash partition to a virtual address space.
Definition os_esp32.c:222
const char prefix[155]
Definition tar.h:16
const char size[12]
Definition tar.h:5