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:
Paul Sokolovsky 2018-10-08 13:55:03 +03:00 committed by Carles Cufí
commit f484bbaa26
4 changed files with 294 additions and 3 deletions

110
include/misc/fdtable.h Normal file
View 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_ */

View file

@ -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
View 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 */

View file

@ -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