Skip to content

Commit f0639b8

Browse files
committed
zvfs: improve libc FILE to integer fd abstraction
Previously, there was an implicit assumption that Zephyr's internal struct fd_entry * was synonymous with FILE * from the C library. This is generally not the case and aliasing these two distinct types was preventing a fair bit of functionality from Just Working - namely stdio function calls like fgets() and fopen(). The problem count be seen directly when trying to use a function like zvfs_fdopen(). Instead of aliasing the two types, require that all Zephyr C libraries provide 1. FILE *z_libc_file_alloc(int fd, const char *mode) Allocate and populate the required fields of a FILE object 2. int z_libc_file_get_fd(FILE *fp) Convert a FILE* object to an integer file descriptor. For Picolibc and Newlib-based C libraries, these functions set and get the integer file descriptor from a field of the internal FILE object representation. For the minimal C library, these functions convert between array index and struct fd_entry pointers. Signed-off-by: Chris Friedt <[email protected]>
1 parent 455b0ee commit f0639b8

File tree

17 files changed

+305
-8
lines changed

17 files changed

+305
-8
lines changed

include/zephyr/posix/unistd.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ extern "C" {
3030
/* File related operations */
3131
int close(int file);
3232
ssize_t write(int file, const void *buffer, size_t count);
33+
ssize_t pwrite(int file, const void *buffer, size_t count, off_t offset);
3334
ssize_t read(int file, void *buffer, size_t count);
35+
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
3436
off_t lseek(int file, off_t offset, int whence);
3537
int fsync(int fd);
3638
int ftruncate(int fd, off_t length);

include/zephyr/sys/fdtable.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define ZEPHYR_INCLUDE_SYS_FDTABLE_H_
88

99
#include <stdarg.h>
10+
#include <stdio.h>
1011
#include <time.h>
1112

1213
/* FIXME: For native_posix ssize_t, off_t. */
@@ -261,6 +262,12 @@ __syscall int zvfs_select(int nfds, struct zvfs_fd_set *ZRESTRICT readfds,
261262
struct zvfs_fd_set *ZRESTRICT errorfds,
262263
const struct timespec *ZRESTRICT timeout, const void *ZRESTRICT sigmask);
263264

265+
void zvfs_libc_file_alloc_cb(int fd, const char *mode, FILE *fp);
266+
int zvfs_libc_file_alloc(int fd, const char *mode, FILE **fp, k_timeout_t timeout);
267+
void zvfs_libc_file_free(FILE *fp);
268+
int zvfs_libc_file_get_fd(FILE *fp);
269+
FILE *zvfs_libc_file_from_fd(int fd);
270+
264271
/**
265272
* Request codes for fd_op_vtable.ioctl().
266273
*

lib/libc/common/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ config COMMON_LIBC_THRD
109109
depends on DYNAMIC_THREAD
110110
# Note: the POSIX_API dependency is only necessary until common elements
111111
# of C11 threads and POSIX API can be abstracted out to a common library.
112-
depends on POSIX_API
112+
depends on POSIX_THREADS
113113
default y
114114
help
115115
Common implementation of C11 <threads.h> API.

lib/libc/newlib/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
zephyr_library()
44
zephyr_library_sources(libc-hooks.c)
55

6+
zephyr_library_sources_ifdef(CONFIG_ZVFS zvfs_libc.c)
7+
68
# Do not allow LTO when compiling libc-hooks.c file
79
set_source_files_properties(libc-hooks.c PROPERTIES COMPILE_OPTIONS $<TARGET_PROPERTY:compiler,prohibit_lto>)
810

lib/libc/newlib/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,12 @@ config NEWLIB_LIBC_CUSTOM_SBRK
7070
Allow user to define custom version of the _sbrk function. Some application
7171
may need to use the remaining RAM for also other purposes than heap.
7272

73+
if ZVFS
74+
75+
config ZVFS_LIBC_FILE_SIZE
76+
default 184 if 64BIT
77+
default 104
78+
79+
endif # ZVFS
80+
7381
endif # NEWLIB_LIBC

lib/libc/newlib/zvfs_libc.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2024 Tenstorrent AI ULC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stddef.h>
8+
#include <stdio.h>
9+
#include <stdlib.h>
10+
11+
#include <zephyr/sys/fdtable.h>
12+
#include <zephyr/toolchain.h>
13+
14+
BUILD_ASSERT(CONFIG_ZVFS_LIBC_FILE_SIZE == sizeof(__FILE),
15+
"CONFIG_ZVFS_LIBC_FILE_SIZE must match size of FILE object");
16+
BUILD_ASSERT(CONFIG_ZVFS_LIBC_FILE_ALIGN == __alignof(__FILE),
17+
"CONFIG_ZVFS_LIBC_FILE_SIZE must match alignment of FILE object");
18+
19+
static int z_libc_sflags(const char *mode)
20+
{
21+
int ret = 0;
22+
23+
switch (mode[0]) {
24+
case 'r':
25+
ret = ZVFS_O_RDONLY;
26+
break;
27+
28+
case 'w':
29+
ret = ZVFS_O_WRONLY | ZVFS_O_CREAT | ZVFS_O_TRUNC;
30+
break;
31+
32+
case 'a':
33+
ret = ZVFS_O_WRONLY | ZVFS_O_CREAT | ZVFS_O_APPEND;
34+
break;
35+
default:
36+
return 0;
37+
}
38+
39+
while (*++mode) {
40+
switch (*mode) {
41+
case '+':
42+
ret |= ZVFS_O_RDWR;
43+
break;
44+
case 'x':
45+
ret |= ZVFS_O_EXCL;
46+
break;
47+
default:
48+
break;
49+
}
50+
}
51+
52+
return ret;
53+
}
54+
55+
void zvfs_libc_file_alloc_cb(int fd, const char *mode, FILE *fp)
56+
{
57+
/*
58+
* These symbols have conflicting declarations in upstream headers.
59+
* Use uintptr_t and a cast to assign cleanly.
60+
*/
61+
extern uintptr_t _close_r;
62+
extern uintptr_t _lseek_r;
63+
extern uintptr_t _read_r;
64+
extern uintptr_t _write_r;
65+
66+
__ASSERT_NO_MSG(mode != NULL);
67+
__ASSERT_NO_MSG(fp != NULL);
68+
69+
fp->_flags = z_libc_sflags(mode);
70+
fp->_file = fd;
71+
fp->_cookie = (void *)fp;
72+
73+
*(uintptr_t *)&fp->_read = (uintptr_t)&_read_r;
74+
*(uintptr_t *)&fp->_write = (uintptr_t)&_write_r;
75+
*(uintptr_t *)&fp->_seek = (uintptr_t)&_lseek_r;
76+
*(uintptr_t *)&fp->_close = (uintptr_t)&_close_r;
77+
}
78+
79+
int zvfs_libc_file_get_fd(FILE *fp)
80+
{
81+
return fp->_file;
82+
}

lib/libc/picolibc/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ zephyr_library_sources(
1111
stdio.c
1212
)
1313

14+
zephyr_library_sources_ifdef(CONFIG_ZVFS zvfs_libc.c)
15+
1416
zephyr_library_compile_options($<TARGET_PROPERTY:compiler,prohibit_lto>)
1517

1618
# define __LINUX_ERRNO_EXTENSIONS__ so we get errno defines like -ESHUTDOWN

lib/libc/picolibc/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,12 @@ config PICOLIBC_GLOBAL_ERRNO
200200

201201
endif # PICOLIBC_USE_MODULE
202202

203+
if ZVFS
204+
205+
config ZVFS_LIBC_FILE_SIZE
206+
default 144 if 64BIT
207+
default 80
208+
209+
endif # ZVFS
210+
203211
endif # PICOLIBC

lib/libc/picolibc/zvfs_libc.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2024 Tenstorrent AI ULC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "stdio-bufio.h"
8+
9+
#include <stddef.h>
10+
#include <stdio.h>
11+
#include <stdlib.h>
12+
13+
#include <zephyr/sys/fdtable.h>
14+
#include <zephyr/toolchain.h>
15+
16+
BUILD_ASSERT(CONFIG_ZVFS_LIBC_FILE_SIZE == sizeof(struct __file_bufio),
17+
"CONFIG_ZVFS_LIBC_FILE_SIZE must match size of FILE object");
18+
BUILD_ASSERT(CONFIG_ZVFS_LIBC_FILE_ALIGN == __alignof(struct __file_bufio),
19+
"CONFIG_ZVFS_LIBC_FILE_SIZE must match alignment of FILE object");
20+
21+
#define FDEV_SETUP_ZVFS(fd, buf, size, rwflags, bflags) \
22+
FDEV_SETUP_BUFIO(fd, buf, size, zvfs_read_wrap, zvfs_write_wrap, zvfs_lseek, zvfs_close, \
23+
rwflags, bflags)
24+
25+
int __posix_sflags(const char *mode, int *optr);
26+
27+
/* FIXME: do not use ssize_t or off_t */
28+
ssize_t zvfs_read(int fd, void *buf, size_t sz, const size_t *from_offset);
29+
ssize_t zvfs_write(int fd, const void *buf, size_t sz, const size_t *from_offset);
30+
off_t zvfs_lseek(int fd, off_t offset, int whence);
31+
int zvfs_close(int fd);
32+
33+
static ssize_t zvfs_read_wrap(int fd, void *buf, size_t sz)
34+
{
35+
return zvfs_read(fd, buf, sz, NULL);
36+
}
37+
38+
static ssize_t zvfs_write_wrap(int fd, const void *buf, size_t sz)
39+
{
40+
return zvfs_write(fd, buf, sz, NULL);
41+
}
42+
43+
void zvfs_libc_file_alloc_cb(int fd, const char *mode, FILE *fp)
44+
{
45+
struct __file_bufio *const bf = (struct __file_bufio *)fp;
46+
int __maybe_unused unused;
47+
48+
*bf = (struct __file_bufio)FDEV_SETUP_ZVFS(fd, (char *)(bf + 1), BUFSIZ,
49+
__posix_sflags(mode, &unused), __BFALL);
50+
51+
__bufio_lock_init(&(bf->xfile.cfile.file));
52+
}
53+
54+
int zvfs_libc_file_get_fd(FILE *fp)
55+
{
56+
const struct __file_bufio *const bf = (const struct __file_bufio *)fp;
57+
58+
return POINTER_TO_INT(bf->ptr);
59+
}

lib/os/fdtable.c

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,47 @@ static struct fd_entry fdtable[CONFIG_ZVFS_OPEN_MAX] = {
7575

7676
static K_MUTEX_DEFINE(fdtable_lock);
7777

78+
#ifdef CONFIG_MINIMAL_LIBC
79+
void zvfs_libc_file_alloc_cb(int fd, const char *mode, FILE *fp)
80+
{
81+
ARG_UNUSED(fd);
82+
ARG_UNUSED(mode);
83+
ARG_UNUSED(fp);
84+
}
85+
86+
int zvfs_libc_file_alloc(int fd, const char *mode, FILE **fp, k_timeout_t timeout)
87+
{
88+
ARG_UNUSED(mode);
89+
ARG_UNUSED(timeout);
90+
91+
__ASSERT_NO_MSG(mode != NULL);
92+
__ASSERT_NO_MSG(fp != NULL);
93+
94+
*fp = (FILE *)&fdtable[fd];
95+
96+
return 0;
97+
}
98+
99+
void zvfs_libc_file_free(FILE *fp)
100+
{
101+
ARG_UNUSED(fp);
102+
}
103+
104+
int zvfs_libc_file_get_fd(FILE *fp)
105+
{
106+
return (const struct fd_entry *)fp - fdtable;
107+
}
108+
109+
FILE *zvfs_libc_file_from_fd(int fd)
110+
{
111+
if ((fd < 0) || (fd >= ARRAY_SIZE(fdtable))) {
112+
return NULL;
113+
}
114+
115+
return (FILE *)&fdtable[fd];
116+
}
117+
#endif
118+
78119
static int z_fd_ref(int fd)
79120
{
80121
return atomic_inc(&fdtable[fd].refcount) + 1;
@@ -406,30 +447,41 @@ int zvfs_close(int fd)
406447
}
407448
k_mutex_unlock(&fdtable[fd].lock);
408449

450+
zvfs_libc_file_free(zvfs_libc_file_from_fd(fd));
409451
zvfs_free_fd(fd);
410452

411453
return res;
412454
}
413455

414456
FILE *zvfs_fdopen(int fd, const char *mode)
415457
{
416-
ARG_UNUSED(mode);
458+
int ret;
459+
FILE *fp = NULL;
417460

418461
if (_check_fd(fd) < 0) {
419462
return NULL;
420463
}
421464

422-
return (FILE *)&fdtable[fd];
465+
ret = zvfs_libc_file_alloc(fd, mode, &fp, K_NO_WAIT);
466+
if (ret < 0) {
467+
errno = ENOMEM;
468+
} else {
469+
__ASSERT(fp != NULL, "zvfs_libc_file_alloc() returned an invalid file pointer");
470+
}
471+
472+
return fp;
423473
}
424474

425475
int zvfs_fileno(FILE *file)
426476
{
427-
if (!IS_ARRAY_ELEMENT(fdtable, file)) {
477+
int fd = zvfs_libc_file_get_fd(file);
478+
479+
if (fd < 0 || fd >= ARRAY_SIZE(fdtable)) {
428480
errno = EBADF;
429481
return -1;
430482
}
431483

432-
return (struct fd_entry *)file - fdtable;
484+
return fd;
433485
}
434486

435487
int zvfs_fstat(int fd, struct stat *buf)

0 commit comments

Comments
 (0)