feat(storage/vfs): add /dev/null for unwanted output redirection

This commit is contained in:
Tomáš Rohlínek 2024-04-19 10:03:36 +02:00
parent 0b4fff69c5
commit a615f487dd
No known key found for this signature in database
GPG Key ID: 288BC7E47BF1409B
6 changed files with 349 additions and 1 deletions

View File

@ -50,6 +50,7 @@ CORE: 105: init_newlib_time in components/esp_system/startup_funcs.c on BIT(0)
CORE: 110: init_vfs_uart in components/esp_driver_uart/src/uart_vfs.c on BIT(0)
CORE: 111: init_vfs_usj in components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c on BIT(0)
CORE: 112: init_vfs_usj_sec in components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c on BIT(0)
CORE: 113: init_vfs_nullfs in components/vfs/nullfs.c on BIT(0)
CORE: 114: init_vfs_console in components/esp_vfs_console/vfs_console.c on BIT(0)
CORE: 115: init_newlib_stdio in components/newlib/newlib_init.c on BIT(0)

View File

@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <assert.h>
#include "sdkconfig.h"
#include "esp_rom_uart.h"
@ -15,6 +16,9 @@
#include "driver/uart.h"
#include "soc/uart_channel.h"
#include <fcntl.h>
#include <unistd.h>
#if CONFIG_ESP_CONSOLE_NONE
/* Set up UART on UART_0 (console) to be able to
notify pytest test case that app booted successfully
@ -49,6 +53,15 @@ void app_main(void)
{
printf("Hello World\n");
int fd = open("/dev/null", O_RDWR);
assert(fd >= 0); // Standard check
// Check if correct file descriptor is returned
// In this case it should be neither of 0, 1, 2 (== stdin, stdout, stderr)
assert(fd > 2);
close(fd);
#if CONFIG_ESP_CONSOLE_NONE
console_none_print();
#endif // CONFIG_ESP_CONSOLE_NONE

View File

@ -15,6 +15,7 @@
#include "esp_vfs_console.h"
#include "sdkconfig.h"
#include "esp_private/startup_internal.h"
#include "esp_vfs_null.h"
#define STRINGIFY(s) STRINGIFY2(s)
#define STRINGIFY2(s) #s
@ -52,6 +53,8 @@ int console_open(const char * path, int flags, int mode)
vfs_console.fd_primary = open("/dev/usbserjtag", flags, mode);
#elif CONFIG_ESP_CONSOLE_USB_CDC
vfs_console.fd_primary = open("/dev/cdcacm", flags, mode);
#else
vfs_console.fd_primary = open("/dev/null", flags, mode);
#endif
// Secondary port open
@ -207,13 +210,15 @@ esp_err_t esp_vfs_console_register(void)
if (err != ESP_OK) {
return err;
}
#else
esp_vfs_null_register();
primary_vfs = esp_vfs_null_get_vfs();
#endif
// Secondary vfs part.
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
secondary_vfs = esp_vfs_usb_serial_jtag_get_vfs();
#endif
err = esp_vfs_dev_console_register();
return err;
}

View File

@ -7,6 +7,7 @@ endif()
list(APPEND sources "vfs.c"
"vfs_eventfd.c"
"vfs_semihost.c"
"nullfs.c"
)
list(APPEND pr esp_timer

View File

@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_vfs.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Get VFS structure for /dev/null
*
* @return VFS structure for /dev/null
*/
const esp_vfs_t *esp_vfs_null_get_vfs(void);
/**
* @brief Register filesystem for /dev/null
*
* @return ESP_OK on success; any other value indicates an error
*/
esp_err_t esp_vfs_null_register(void);
#ifdef __cplusplus
}
#endif

297
components/vfs/nullfs.c Normal file
View File

@ -0,0 +1,297 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <sys/types.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "esp_vfs.h"
#include "esp_log.h"
#include "esp_private/startup_internal.h"
#include "esp_vfs_null.h"
#define UNUSED(x) (void)(x)
typedef enum {
VFS_NULL_CLOSED = 0,
VFS_NULL_READ = 1 << 0,
VFS_NULL_WRITE = 1 << 1,
} vfs_null_flags_t;
typedef uint64_t vfs_null_ctx_t;
#define GET_FLAGS(ctx, fd) ((ctx >> (fd * 2)) & 0x3)
#define SET_FLAGS(ctx, fd, flags) (ctx |= (flags << (fd * 2)))
#define WRITABLE(ctx, fd) (GET_FLAGS(ctx, fd) & VFS_NULL_WRITE)
#define READABLE(ctx, fd) (GET_FLAGS(ctx, fd) & VFS_NULL_READ)
#define SET_WRITABLE(ctx, fd) SET_FLAGS(ctx, fd, VFS_NULL_WRITE)
#define SET_READABLE(ctx, fd) SET_FLAGS(ctx, fd, VFS_NULL_READ)
#define FD_IN_RANGE(fd) (fd >= 0 && fd < 32)
static vfs_null_ctx_t g_fds = 0;
static const char* TAG = "nullfs";
static ssize_t vfs_null_write(int fd, const void *data, size_t size);
static off_t vfs_null_lseek(int fd, off_t offset, int whence);
static ssize_t vfs_null_read(int fd, void *data, size_t size);
static ssize_t vfs_null_pread(int fd, void *data, size_t size, off_t offset);
static ssize_t vfs_null_pwrite(int fd, const void *data, size_t size, off_t offset);
static int vfs_null_open(const char* path, int flags, int mode);
static int vfs_null_close(int fd);
static int vfs_null_fstat(int fd, struct stat *st);
#if CONFIG_VFS_SUPPORT_DIR
static int vfs_null_stat(const char* path, struct stat *st);
#endif // CONFIG_VFS_SUPPORT_DIR
static int vfs_null_fcntl(int fd, int cmd, int arg);
static int vfs_null_ioctl(int fd, int cmd, va_list args);
static int vfs_null_fsync(int fd);
static const esp_vfs_t s_vfs_null = {
.flags = ESP_VFS_FLAG_DEFAULT,
.write = &vfs_null_write,
.lseek = &vfs_null_lseek,
.read = &vfs_null_read,
.pread = &vfs_null_pread,
.pwrite = &vfs_null_pwrite,
.open = &vfs_null_open,
.close = &vfs_null_close,
.fstat = &vfs_null_fstat,
#if CONFIG_VFS_SUPPORT_DIR
.stat = &vfs_null_stat,
#endif // CONFIG_VFS_SUPPORT_DIR
.fcntl = &vfs_null_fcntl,
.ioctl = &vfs_null_ioctl,
.fsync = &vfs_null_fsync,
};
const esp_vfs_t *esp_vfs_null_get_vfs(void)
{
return &s_vfs_null;
}
esp_err_t esp_vfs_null_register(void)
{
return esp_vfs_register("/dev/null", &s_vfs_null, NULL);
}
static ssize_t vfs_null_write(int fd, const void *data, size_t size)
{
UNUSED(fd);
UNUSED(data);
if (FD_IN_RANGE(fd) && WRITABLE(g_fds, fd)) {
return size;
}
errno = EBADF;
return -1;
}
static off_t vfs_null_lseek(int fd, off_t offset, int whence)
{
UNUSED(fd);
UNUSED(offset);
UNUSED(whence);
ESP_LOGD(TAG, "lseek %ld", offset);
switch (whence) {
case SEEK_SET:
case SEEK_CUR:
case SEEK_END:
return 0;
default:
errno = EINVAL;
return -1;
}
}
static ssize_t vfs_null_read(int fd, void *data, size_t size)
{
UNUSED(fd);
UNUSED(data);
ESP_LOGD(TAG, "read %u bytes", size);
if (FD_IN_RANGE(fd) && READABLE(g_fds, fd)) {
return 0; // EOF
}
errno = EBADF;
return -1;
}
static int vfs_null_pread(int fd, void *data, size_t size, off_t offset)
{
UNUSED(fd);
UNUSED(data);
UNUSED(size);
UNUSED(offset);
errno = ESPIPE;
return -1;
}
static int vfs_null_pwrite(int fd, const void *data, size_t size, off_t offset)
{
UNUSED(fd);
UNUSED(data);
UNUSED(size);
UNUSED(offset);
errno = ESPIPE;
return -1;
}
static int vfs_null_get_empty_fd(void)
{
for (int i = 0; i < 32; i++) {
if (GET_FLAGS(g_fds, i) == VFS_NULL_CLOSED) {
return i;
}
}
return -1;
}
static int vfs_null_open(const char* path, int flags, int mode)
{
UNUSED(mode);
ESP_LOGD(TAG, "open %s, flags %d", path, flags);
// Possibly check if the flags are valid
if (strcmp(path, "/") != 0) {
errno = ENOENT;
return -1;
}
int fd = vfs_null_get_empty_fd();
if (fd == -1) {
errno = EMFILE;
return -1;
}
if (flags & O_RDWR) {
SET_READABLE(g_fds, fd);
SET_WRITABLE(g_fds, fd);
} else if (flags & O_WRONLY) {
SET_WRITABLE(g_fds, fd);
} else if (flags & O_RDONLY) {
SET_READABLE(g_fds, fd);
} else {
errno = EINVAL;
return -1;
}
return fd;
}
static int vfs_null_close(int fd)
{
if (!FD_IN_RANGE(fd)) {
errno = EBADF;
return -1;
}
SET_FLAGS(g_fds, fd, VFS_NULL_CLOSED);
return 0;
}
static int vfs_null_fstat(int fd, struct stat *st)
{
if (!FD_IN_RANGE(fd)) {
errno = EBADF;
return -1;
}
memset(st, 0, sizeof(struct stat));
st->st_mode = S_IFCHR | 0666; // Special character device with read/write permissions for everyone
st->st_nlink = 1;
st->st_uid = 0;
st->st_gid = 0;
st->st_rdev = makedev(1, 3); // Null device (from Linux)
return 0;
}
#if CONFIG_VFS_SUPPORT_DIR
static int vfs_null_stat(const char* path, struct stat *st)
{
if (strcmp(path, "/") != 0) {
errno = ENOENT;
return -1;
}
memset(st, 0, sizeof(struct stat));
st->st_mode = S_IFCHR | 0666; // Special character device with read/write permissions for everyone
st->st_nlink = 1;
st->st_uid = 0;
st->st_gid = 0;
st->st_rdev = makedev(1, 3); // Null device (from Linux)
return 0;
}
#endif // CONFIG_VFS_SUPPORT_DIR
static int vfs_null_fcntl(int fd, int cmd, int arg)
{
UNUSED(arg);
if (!FD_IN_RANGE(fd)) {
errno = EBADF;
return -1;
}
switch (cmd) {
default:
errno = ENOSYS;
return -1;
}
}
static int vfs_null_ioctl(int fd, int cmd, va_list args)
{
UNUSED(args);
if (!FD_IN_RANGE(fd)) {
errno = EBADF;
return -1;
}
switch (cmd) {
default:
errno = ENOSYS;
return -1;
}
}
static int vfs_null_fsync(int fd)
{
if (!FD_IN_RANGE(fd)) {
errno = EBADF;
return -1;
}
return 0;
}
#if CONFIG_ESP_CONSOLE_NONE
ESP_SYSTEM_INIT_FN(init_vfs_nullfs, CORE, BIT(0), 113)
{
return esp_vfs_null_register();
}
#endif