lib: posix: Implement generic file descriptor table
The table allows to wrap read/write (i.e. POSIX-compatible) semantics of any I/O object in POSIX-compatible fd (file descriptor) handling. Intended I/O objects include files, sockets, special devices, etc. The table table itself consists of (underlying obj*, function table*) pairs, where function table provides entries for read(), write, and generalized ioctl(), where generalized ioctl handles all other operations, up to and including closing of the underlying I/O object. Fixes: #7405 Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
This commit is contained in:
parent
3215dd862f
commit
f484bbaa26
4 changed files with 294 additions and 3 deletions
110
include/misc/fdtable.h
Normal file
110
include/misc/fdtable.h
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Linaro Limited
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#ifndef ZEPHYR_INCLUDE_POSIX_POSIX__FDTABLE_H_
|
||||||
|
#define ZEPHYR_INCLUDE_POSIX_POSIX__FDTABLE_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
/* FIXME: For native_posix ssize_t, off_t. */
|
||||||
|
#include <fs.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File descriptor virtual method table.
|
||||||
|
* All operations beyond read/write go thru ioctl method.
|
||||||
|
*/
|
||||||
|
struct fd_op_vtable {
|
||||||
|
ssize_t (*read)(void *obj, void *buf, size_t sz);
|
||||||
|
ssize_t (*write)(void *obj, const void *buf, size_t sz);
|
||||||
|
int (*ioctl)(void *obj, unsigned int request, ...);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reserve file descriptor.
|
||||||
|
*
|
||||||
|
* This function allows to reserve a space for file descriptor entry in
|
||||||
|
* the underlying table, and thus allows caller to fail fast if no free
|
||||||
|
* descriptor is available. If this function succeeds, z_finalize_fd()
|
||||||
|
* or z_free_fd() must be called mandatorily.
|
||||||
|
*
|
||||||
|
* @return Allocated file descriptor, or -1 in case of error (errno is set)
|
||||||
|
*/
|
||||||
|
int z_reserve_fd(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finalize creation of file descriptor.
|
||||||
|
*
|
||||||
|
* This function should be called exactly once after z_reserve_fd(), and
|
||||||
|
* should not be called in any other case.
|
||||||
|
*
|
||||||
|
* @param fd File descriptor previously returned by z_reserve_fd()
|
||||||
|
* @param obj pointer to I/O object structure
|
||||||
|
* @param vtable pointer to I/O operation implementations for the object
|
||||||
|
*/
|
||||||
|
void z_finalize_fd(int fd, void *obj, const struct fd_op_vtable *vtable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocate file descriptor for underlying I/O object.
|
||||||
|
*
|
||||||
|
* This function combines operations of z_reserve_fd() and z_finalize_fd()
|
||||||
|
* in one step, and provided for convenience.
|
||||||
|
*
|
||||||
|
* @param obj pointer to I/O object structure
|
||||||
|
* @param vtable pointer to I/O operation implementations for the object
|
||||||
|
*
|
||||||
|
* @return Allocated file descriptor, or -1 in case of error (errno is set)
|
||||||
|
*/
|
||||||
|
int z_alloc_fd(void *obj, const struct fd_op_vtable *vtable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Release reserved file descriptor.
|
||||||
|
*
|
||||||
|
* This function may be called once after z_reserve_fd(), and should
|
||||||
|
* not be called in any other case.
|
||||||
|
*
|
||||||
|
* @param fd File descriptor previously returned by z_reserve_fd()
|
||||||
|
*/
|
||||||
|
void z_free_fd(int fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get underlying object pointer from file descriptor.
|
||||||
|
*
|
||||||
|
* This function is useful for functions other than read/write/ioctl
|
||||||
|
* to look up underlying I/O object by fd, optionally checking its
|
||||||
|
* type (using vtable reference). If fd refers to invalid entry,
|
||||||
|
* NULL will be returned with errno set to EBADF. If fd is valid,
|
||||||
|
* but vtable param is not NULL and doesn't match object's vtable,
|
||||||
|
* NULL is returned and errno set to err param.
|
||||||
|
*
|
||||||
|
* @param fd File descriptor previously returned by z_reserve_fd()
|
||||||
|
* @param vtable Expected object vtable or NULL
|
||||||
|
* @param err errno value to set if object vtable doesn't match
|
||||||
|
*
|
||||||
|
* @return Object pointer or NULL, with errno set
|
||||||
|
*/
|
||||||
|
void *z_get_fd_obj(int fd, const struct fd_op_vtable *vtable, int err);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request codes for fd_op_vtable.ioctl().
|
||||||
|
*
|
||||||
|
* Note that these codes are internal Zephyr numbers, for internal
|
||||||
|
* Zephyr operation (and subject to change without notice, not part
|
||||||
|
* of "stable ABI"). These are however expected to co-exist with
|
||||||
|
* "well-known" POSIX/Linux ioctl numbers, and not clash with them.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
ZFD_IOCTL_CLOSE = 1,
|
||||||
|
ZFD_IOCTL_FSYNC,
|
||||||
|
ZFD_IOCTL_LSEEK,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_INCLUDE_POSIX_POSIX__FDTABLE_H_ */
|
|
@ -1,4 +1,5 @@
|
||||||
zephyr_sources(thread_entry.c)
|
zephyr_sources(thread_entry.c)
|
||||||
|
zephyr_sources(fdtable.c)
|
||||||
add_subdirectory(crc)
|
add_subdirectory(crc)
|
||||||
add_subdirectory_ifdef(CONFIG_JSON_LIBRARY json)
|
add_subdirectory_ifdef(CONFIG_JSON_LIBRARY json)
|
||||||
if(NOT CONFIG_NATIVE_APPLICATION)
|
if(NOT CONFIG_NATIVE_APPLICATION)
|
||||||
|
|
171
lib/fdtable.c
Normal file
171
lib/fdtable.c
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Linaro Limited
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief File descriptor table
|
||||||
|
*
|
||||||
|
* This file provides generic file descriptor table implementation, suitable
|
||||||
|
* for any I/O object implementing POSIX I/O semantics (i.e. read/write +
|
||||||
|
* aux operations).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <misc/fdtable.h>
|
||||||
|
|
||||||
|
struct fd_entry {
|
||||||
|
void *obj;
|
||||||
|
const struct fd_op_vtable *vtable;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fd_entry fdtable[CONFIG_POSIX_MAX_FDS];
|
||||||
|
|
||||||
|
static K_MUTEX_DEFINE(fdtable_lock);
|
||||||
|
|
||||||
|
static int _find_fd_entry(void)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
for (fd = 0; fd < ARRAY_SIZE(fdtable); fd++) {
|
||||||
|
if (fdtable[fd].obj == NULL) {
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = ENFILE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _check_fd(int fd)
|
||||||
|
{
|
||||||
|
if (fd < 0 || fd >= ARRAY_SIZE(fdtable) || fdtable[fd].obj == NULL) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *z_get_fd_obj(int fd, const struct fd_op_vtable *vtable, int err)
|
||||||
|
{
|
||||||
|
struct fd_entry *fd_entry;
|
||||||
|
|
||||||
|
if (_check_fd(fd) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd_entry = &fdtable[fd];
|
||||||
|
|
||||||
|
if (vtable != NULL && fd_entry->vtable != vtable) {
|
||||||
|
errno = err;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd_entry->obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
int z_reserve_fd(void)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
(void)k_mutex_lock(&fdtable_lock, K_FOREVER);
|
||||||
|
|
||||||
|
fd = _find_fd_entry();
|
||||||
|
if (fd >= 0) {
|
||||||
|
/* Mark entry as used, z_finalize_fd() will fill it in. */
|
||||||
|
fdtable[fd].obj = FD_OBJ_RESERVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_mutex_unlock(&fdtable_lock);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void z_finalize_fd(int fd, void *obj, const struct fd_op_vtable *vtable)
|
||||||
|
{
|
||||||
|
/* Assumes fd was already bounds-checked. */
|
||||||
|
fdtable[fd].obj = obj;
|
||||||
|
fdtable[fd].vtable = vtable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void z_free_fd(int fd)
|
||||||
|
{
|
||||||
|
/* Assumes fd was already bounds-checked. */
|
||||||
|
fdtable[fd].obj = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int z_alloc_fd(void *obj, const struct fd_op_vtable *vtable)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = z_reserve_fd();
|
||||||
|
if (fd >= 0) {
|
||||||
|
z_finalize_fd(fd, obj, vtable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_POSIX_API
|
||||||
|
|
||||||
|
ssize_t read(int fd, void *buf, size_t sz)
|
||||||
|
{
|
||||||
|
if (_check_fd(fd) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fdtable[fd].vtable->read(fdtable[fd].obj, buf, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t write(int fd, const void *buf, size_t sz)
|
||||||
|
{
|
||||||
|
if (_check_fd(fd) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fdtable[fd].vtable->write(fdtable[fd].obj, buf, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
int close(int fd)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (_check_fd(fd) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = fdtable[fd].vtable->ioctl(fdtable[fd].obj, ZFD_IOCTL_CLOSE);
|
||||||
|
z_free_fd(fd);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fsync(int fd)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (_check_fd(fd) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = fdtable[fd].vtable->ioctl(fdtable[fd].obj, ZFD_IOCTL_FSYNC);
|
||||||
|
z_free_fd(fd);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t lseek(int fd, off_t offset, int whence)
|
||||||
|
{
|
||||||
|
if (_check_fd(fd) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fdtable[fd].vtable->ioctl(fdtable[fd].obj, ZFD_IOCTL_LSEEK,
|
||||||
|
offset, whence);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_POSIX_API */
|
|
@ -4,6 +4,14 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
#
|
#
|
||||||
|
|
||||||
|
config POSIX_MAX_FDS
|
||||||
|
int "Maximum number of open file descriptors"
|
||||||
|
default 16 if POSIX_API
|
||||||
|
default 4
|
||||||
|
help
|
||||||
|
Maximum number of open file descriptors, this includes
|
||||||
|
files, sockets, special devices, etc.
|
||||||
|
|
||||||
config POSIX_API
|
config POSIX_API
|
||||||
bool "POSIX APIs"
|
bool "POSIX APIs"
|
||||||
help
|
help
|
||||||
|
@ -75,15 +83,16 @@ if FILE_SYSTEM
|
||||||
config POSIX_FS
|
config POSIX_FS
|
||||||
bool "Enable POSIX file system API support"
|
bool "Enable POSIX file system API support"
|
||||||
help
|
help
|
||||||
This enabled POSIX style file system related APIs.
|
This enables POSIX style file system related APIs.
|
||||||
|
|
||||||
if POSIX_FS
|
if POSIX_FS
|
||||||
config POSIX_MAX_OPEN_FILES
|
config POSIX_MAX_OPEN_FILES
|
||||||
int "Maximum number of open file descriptors"
|
int "Maximum number of open file descriptors"
|
||||||
default 16
|
default 16
|
||||||
help
|
help
|
||||||
Mention maximum number of open file descriptors.
|
Maximum number of open files. Note that this setting
|
||||||
endif
|
is additionally bounded by CONFIG_POSIX_MAX_FDS.
|
||||||
endif
|
endif
|
||||||
|
endif # FILE_SYSTEM
|
||||||
|
|
||||||
endif # POSIX_API
|
endif # POSIX_API
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue