Skip to content

posix: options: implement the POSIX_FILE_LOCKING Option Group #84860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions doc/services/portability/posix/option_groups/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,19 @@ Enable this option group with :kconfig:option:`CONFIG_POSIX_FD_MGMT`.
POSIX_FILE_LOCKING
++++++++++++++++++

Enable this option group with :kconfig:option:`CONFIG_POSIX_FILE_LOCKING`.

.. csv-table:: POSIX_FILE_LOCKING
:header: API, Supported
:widths: 50,10

flockfile(),
ftrylockfile(),
funlockfile(),
getc_unlocked(),
getchar_unlocked(),
putc_unlocked(),
putchar_unlocked(),
flockfile(), yes
ftrylockfile(), yes
funlockfile(), yes
getc_unlocked(), yes
getchar_unlocked(), yes
putc_unlocked(), yes
putchar_unlocked(), yes

.. _posix_option_group_file_system:

Expand Down
1 change: 1 addition & 0 deletions lib/libc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ config PICOLIBC
select LIBC_ERRNO if THREAD_LOCAL_STORAGE
select NEED_LIBC_MEM_PARTITION
select TC_PROVIDES_POSIX_C_LANG_SUPPORT_R
select TC_PROVIDES_POSIX_FILE_LOCKING
imply COMMON_LIBC_MALLOC
depends on PICOLIBC_SUPPORTED
help
Expand Down
107 changes: 95 additions & 12 deletions lib/os/fdtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,43 +319,37 @@ static bool supports_pread_pwrite(uint32_t mode)
}
}

static ssize_t zvfs_rw(int fd, void *buf, size_t sz, bool is_write, const size_t *from_offset)
static ssize_t zvfs_rw_unlocked(int fd, void *buf, size_t sz, bool is_write,
const size_t *from_offset)
{
bool prw;
ssize_t res;
const size_t *off;

if (_check_fd(fd) < 0) {
return -1;
}

(void)k_mutex_lock(&fdtable[fd].lock, K_FOREVER);

prw = supports_pread_pwrite(fdtable[fd].mode);
if (from_offset != NULL && !prw) {
/*
* Seekable file types should support pread() / pwrite() and per-fd offset passing.
* Otherwise, it's a bug.
*/
errno = ENOTSUP;
res = -1;
goto unlock;
return -1;
}

/* If there is no specified from_offset, then use the current offset of the fd */
off = (from_offset == NULL) ? &fdtable[fd].offset : from_offset;

if (is_write) {
if (fdtable[fd].vtable->write_offs == NULL) {
res = -1;
errno = EIO;
return -1;
} else {
res = fdtable[fd].vtable->write_offs(fdtable[fd].obj, buf, sz, *off);
}
} else {
if (fdtable[fd].vtable->read_offs == NULL) {
res = -1;
errno = EIO;
return -1;
} else {
res = fdtable[fd].vtable->read_offs(fdtable[fd].obj, buf, sz, *off);
}
Expand All @@ -368,7 +362,21 @@ static ssize_t zvfs_rw(int fd, void *buf, size_t sz, bool is_write, const size_t
fdtable[fd].offset += res;
}

unlock:
return res;
}

static ssize_t zvfs_rw(int fd, void *buf, size_t sz, bool is_write, const size_t *from_offset)
{
ssize_t res;

if (_check_fd(fd) < 0) {
return -1;
}

(void)k_mutex_lock(&fdtable[fd].lock, K_FOREVER);

res = zvfs_rw_unlocked(fd, buf, sz, is_write, from_offset);

k_mutex_unlock(&fdtable[fd].lock);

return res;
Expand Down Expand Up @@ -535,6 +543,81 @@ int zvfs_ioctl(int fd, unsigned long request, va_list args)
return fdtable[fd].vtable->ioctl(fdtable[fd].obj, request, args);
}

int zvfs_lock_file(FILE *file, k_timeout_t timeout)
{
int fd;
int prev_errno;
struct fd_entry *entry;

fd = z_libc_file_get_fd(file);
prev_errno = errno;
if (_check_fd(fd) < 0) {
if (errno != prev_errno) {
errno = prev_errno;
}
return -1;
}

entry = &fdtable[fd];
return k_mutex_lock(&entry->lock, timeout);
}

int zvfs_unlock_file(FILE *file)
{
int fd;
int prev_errno;
struct fd_entry *entry;

fd = z_libc_file_get_fd(file);
prev_errno = errno;
if (_check_fd(fd) < 0) {
if (errno != prev_errno) {
errno = prev_errno;
}
return -1;
}

entry = &fdtable[fd];
return k_mutex_unlock(&entry->lock);
}

int zvfs_getc_unlocked(FILE *stream)
{
int fd;
int res;
char buf;

fd = z_libc_file_get_fd(stream);
if (_check_fd(fd) < 0) {
return EOF;
}

res = zvfs_rw_unlocked(fd, &buf, 1, false, NULL);
if (res <= 0) {
return EOF;
}

return (int)buf;
}

int zvfs_putc_unlocked(int c, FILE *stream)
{
int fd;
int res;
char buf = (char)c;

fd = z_libc_file_get_fd(stream);
if (_check_fd(fd) < 0) {
return EOF;
}

res = zvfs_rw_unlocked(fd, &buf, 1, true, NULL);
if (res <= 0) {
return EOF;
}

return c;
}

#if defined(CONFIG_POSIX_DEVICE_IO)
/*
Expand Down
6 changes: 6 additions & 0 deletions lib/posix/options/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ if (NOT CONFIG_TC_PROVIDES_POSIX_FD_MGMT)
)
endif()

if (NOT CONFIG_TC_PROVIDES_POSIX_FILE_LOCKING)
zephyr_library_sources_ifdef(CONFIG_POSIX_FILE_LOCKING
file_locking.c
)
endif()

if (NOT CONFIG_TC_PROVIDES_POSIX_FILE_SYSTEM)
zephyr_library_sources_ifdef(CONFIG_POSIX_FILE_SYSTEM fs.c)
endif()
Expand Down
1 change: 1 addition & 0 deletions lib/posix/options/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rsource "Kconfig.c_lang_r"
rsource "Kconfig.c_lib_ext"
rsource "Kconfig.device_io"
rsource "Kconfig.fd_mgmt"
rsource "Kconfig.file_locking"
rsource "Kconfig.file_system_r"
rsource "Kconfig.fs"
rsource "Kconfig.mem"
Expand Down
14 changes: 14 additions & 0 deletions lib/posix/options/Kconfig.file_locking
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) 2024 Tenstorrent AI ULC
#
# SPDX-License-Identifier: Apache-2.0

config POSIX_FILE_LOCKING
bool "POSIX file locking"
help
Select 'y' here and the following functions will be available:

flockfile(), ftrylockfile(), funlockfile(), getc_unlocked(), getchar_unlocked(),
putc_unlocked(), putchar_unlocked().

For more information, please see
https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_subprofiles.html
69 changes: 69 additions & 0 deletions lib/posix/options/file_locking.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2024 Tenstorrent AI ULC
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <stdio.h>

#include <zephyr/toolchain.h>
#include <zephyr/sys/fdtable.h>

int zvfs_lock_file(FILE *file, k_timeout_t timeout);
int zvfs_unlock_file(FILE *file);
int zvfs_getc_unlocked(FILE *stream);
int zvfs_putc_unlocked(int c, FILE *stream);

/*
* This is incorrectly declared by (at least) newlib to be a macro with 2 arguments
* but it only takes 1 argument.
*
* Undefine any possible macro before attempting to define a duplicately-named function.
*/
#undef putchar_unlocked

/*
* This is incorrectly declared by (at least) newlib to be a macro with 0 arguments
* but it should take 1 argument.
*
* Undefine any possible macro before attempting to define a duplicately-named function.
*/
#undef getchar_unlocked

void flockfile(FILE *file)
{
while (zvfs_lock_file(file, K_FOREVER) != 0) {
k_yield();
}
}

int ftrylockfile(FILE *file)
{
return zvfs_lock_file(file, K_NO_WAIT);
}

void funlockfile(FILE *file)
{
(void)zvfs_unlock_file(file);
}

int getc_unlocked(FILE *stream)
{
return zvfs_getc_unlocked(stream);
}

int getchar_unlocked(void)
{
return zvfs_getc_unlocked(stdin);
}

int putc_unlocked(int c, FILE *stream)
{
return zvfs_putc_unlocked(c, stream);
}

int putchar_unlocked(int c)
{
return zvfs_putc_unlocked(c, stdout);
}
10 changes: 10 additions & 0 deletions tests/posix/file_locking/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(posix_file_locking)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

target_compile_options(app PRIVATE -U_POSIX_C_SOURCE -D_POSIX_C_SOURCE=200809L)
21 changes: 21 additions & 0 deletions tests/posix/file_locking/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright (c) 2024 Tenstorrent AI ULC
# SPDX-License-Identifier: Apache-2.0

source "Kconfig.zephyr"

menu "File Locking Tests"

config TEST_NEGLIGIBLE_DELAY_MS
int "Negligible delay in milliseconds"
default 10
help
Delays less than or equal to this value are considered neglible for the purposes of this
testsuite.

config TEST_LOCK_PERIOD_MS
int "Time to hold a lock in milliseconds"
default 100
help
The amount of time to hold a lock before releasing it, for the purposes of this testuite.

endmenu
14 changes: 14 additions & 0 deletions tests/posix/file_locking/app.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

/ {
ramdisk0 {
compatible = "zephyr,ram-disk";
disk-name = "RAM";
sector-size = <512>;
sector-count = <160>;
};
};
13 changes: 13 additions & 0 deletions tests/posix/file_locking/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CONFIG_POSIX_API=y
CONFIG_ZTEST=y

CONFIG_POSIX_AEP_CHOICE_BASE=y
CONFIG_POSIX_FILE_LOCKING=y
CONFIG_FAT_FILESYSTEM_ELM=y

# pthreads are used for simplicity here, although that isn't strictly necessary
CONFIG_THREAD_STACK_INFO=y
CONFIG_DYNAMIC_THREAD=y
CONFIG_DYNAMIC_THREAD_POOL_SIZE=4

CONFIG_MAIN_STACK_SIZE=4096
Loading
Loading