diff --git a/Makefile.dep b/Makefile.dep index a259d50bc0..17b6abba85 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -83,6 +83,12 @@ ifneq (,$(filter lwext%_vfs,$(USEMODULE))) USEMODULE += vfs endif +ifneq (,$(filter xipfs,$(USEMODULE))) + USEPKG += xipfs + USEMODULE += vfs + USEMODULE += xipfs_fs +endif + ifneq (,$(filter nimble_%,$(USEMODULE))) USEPKG += nimble endif diff --git a/examples/README.md b/examples/README.md index c45eff56c1..0ee0813814 100644 --- a/examples/README.md +++ b/examples/README.md @@ -141,6 +141,7 @@ Here is a quick overview of the examples available in the RIOT: | [twr_aloha](./advanced/twr_aloha/README.md) | This example allows testing different two-way ranging algorithms between two boards supporting a dw1000 device. This makes use of the uwb-core pkg. | | [senml_saul](./advanced/senml_saul/README.md) | This example demonstrates the usage of the SAUL (Sensor Actuator Uber Layer) module with the SenML (Sensor Measurement Lists) format. | | [opendsme](./advanced/opendsme/README.md) | This example demonstrates the usage of the OpenDSME module in RIOT. | +| [xipfs](./advanced/xipfs/README.md) | This example demonstrates the usage of XIPFS for creating and executing an executable file. | ## Examples from Guides diff --git a/examples/advanced/xipfs/Makefile b/examples/advanced/xipfs/Makefile new file mode 100644 index 0000000000..c380a36215 --- /dev/null +++ b/examples/advanced/xipfs/Makefile @@ -0,0 +1,32 @@ +# name of your application +APPLICATION = xipfs + +# If no BOARD is found in the environment, use this default: +BOARD ?= dwm1001 + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# XIPFS is currently not compatible with the llvm toolchain +TOOLCHAINS_BLACKLIST += llvm + +BLOBS += hello-world.fae + +# Modules to include: +USEMODULE += shell +USEMODULE += shell_cmds_default +USEMODULE += ps +USEMODULE += saul_default + +# Use xipfs file system +USEMODULE += xipfs + +include $(RIOTBASE)/Makefile.include diff --git a/examples/advanced/xipfs/Makefile.ci b/examples/advanced/xipfs/Makefile.ci new file mode 100644 index 0000000000..586cb3252a --- /dev/null +++ b/examples/advanced/xipfs/Makefile.ci @@ -0,0 +1,24 @@ +BOARD_INSUFFICIENT_MEMORY := \ + blackpill-stm32f103c8 \ + bluepill-stm32f030c8 \ + bluepill-stm32f103c8 \ + i-nucleo-lrwan1 \ + nucleo-c031c6 \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f302r8 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l011k4 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + samd10-xmini \ + slstk3400a \ + stk3200 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32g0316-disco \ + stm32l0538-disco \ + weact-g030f6 \ + # diff --git a/examples/advanced/xipfs/README.md b/examples/advanced/xipfs/README.md new file mode 100644 index 0000000000..f70469cbe3 --- /dev/null +++ b/examples/advanced/xipfs/README.md @@ -0,0 +1,61 @@ +# The eXecute In-Place File System + +## Description + +`xipfs` is a file system designed to streamline post-issuance software +deployment. `xipfs` allows direct execution of programs from flash +memory, eliminating the need for prior copying to RAM. This approach +conserves memory space and accelerates boot times, as the +microcontroller can run code directly from storage memory without +preloading into RAM. + +The `xipfs` structure is based on a linked list, where each file +occupies at least one flash memory page. To prevent fragmentation, when +a file is deleted, subsequent files are shifted to fill the vacant +space. + +`xipfs` is compatible with all microcontrollers featuring addressable +flash memory and most operating systems, provided they implement the +necessary functions to interact with the flash controller. + +## Limitations + +`xipfs` has the following limitations: + +- No journaling: `xipfs` doesn't provide journaling. Without journaling, + the file system cannot keep track of changes in a way that allows for + recovery in the event of a crash or power failure. This can lead to + data corruption and loss. + +- No checksums: `xipfs` doesn't provide checksums. The lack of checksums + means that the file system cannot verify the integrity of files. This + increases the risk of undetected data corruption, as there is no + mechanism to ensure that files have not been altered or damaged. + +- Per mountpoint file system lock: `xipfs` needs a file system lock per + mountpoint. Such a mechanism can lead to performance bottlenecks, + as it prevents multiple threads from accessing the file system montpoint + simultaneously. + +- Fixed file size: `xipfs` provide fixed file size. By default, a file + created using `vfs_open(2)` has a fixed space reserved in flash that + is the size of a flash page. This size cannot be extended later. To + create a file larger than the fixed size of one flash page, the + `mk(1)` command or the `xipfs_new_file(3)` function must be used. + +- Limited character set: `xipfs` supports only a subset of 7-bit ASCII + characters, specifically `[0-9A-Za-z\/\.\-_]`. + +- Limited path length: `xipfs` maximum path length is 64 characters. + +## Tested cards + +`xipfs` is expected to be compatible with all boards that feature +addressable NVM. However, only the `DWM1001` board has been tested and +is confirmed to function correctly. + +## Funding + +The `xipfs` project is part of the TinyPART project funded by the +MESRI-BMBF German-French cybersecurity program under grant agreements +n°ANR-20-CYAL-0005 and 16KIS1395K. diff --git a/examples/advanced/xipfs/hello-world.fae b/examples/advanced/xipfs/hello-world.fae new file mode 100644 index 0000000000..5a92a6aa13 Binary files /dev/null and b/examples/advanced/xipfs/hello-world.fae differ diff --git a/examples/advanced/xipfs/main.c b/examples/advanced/xipfs/main.c new file mode 100644 index 0000000000..a5049eadc9 --- /dev/null +++ b/examples/advanced/xipfs/main.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2024 Université de Lille + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief An application demonstrating xipfs + * + * @author Damien Amara + * @author Gregory Guche + * + * @} + */ + +#include +#include + +#include "fs/xipfs_fs.h" +#include "periph/flashpage.h" +#include "shell.h" +#include "vfs.h" + +/** + * @def PANIC + * + * @brief This macro handles fatal errors + */ +#define PANIC() for (;;) {} + +/** + * @def NVME0P0_PAGE_NUM + * + * @brief The number of flash page for the nvme0p0 file system + */ +#define NVME0P0_PAGE_NUM 10 + +/** + * @def NVME0P1_PAGE_NUM + * + * @brief The number of flash page for the nvme0p1 file system + */ +#define NVME0P1_PAGE_NUM 15 + +/* + * Allocate a new contiguous space for the nvme0p0 file system + */ +XIPFS_NEW_PARTITION(nvme0p0, "/dev/nvme0p0", NVME0P0_PAGE_NUM); + +/* + * Allocate a new contiguous space for the nvme0p1 file system + */ +XIPFS_NEW_PARTITION(nvme0p1, "/dev/nvme0p1", NVME0P1_PAGE_NUM); + +#ifdef BOARD_DWM1001 + +/** + * @brief hello-world.fae data blob. + * + * To create a *.fae file, you will need to clone the master branch of + * xipfs_format, that can be found at https://github.com/2xs/XiPFS_Format . + * + * Then modify the Makefile to suit your needs/sources and call make. + * You should end up with a *.fae file ready to be uploaded. + */ +#include "blob/hello-world.fae.h" + +#define FILENAME_OF_HELLO_WORLD_FAE "/dev/nvme0p0/hello-world.fae" +#define SIZEOF_HELLO_WORLD_FAE (sizeof(hello_world_fae) / sizeof(hello_world_fae[0])) + +/** + * @brief Execution in-place demonstrator. + * + * This shell command handler will create a file hello-world.fae on /dev/nvme0p0, + * if none exists yet from the data blob above. + * Then, it will execute this file. + * + * Once the file has been created, execute command can be used to rerun + * the executable file as many times as wanted. + * + * ```bash + * 2025-01-14 09:48:36,303 # main(): This is RIOT! (Version: 2024.10) + * 2025-01-14 09:48:36,307 # vfs_mount: "/dev/nvme0p0": OK + * 2025-01-14 09:48:36,313 # vfs_mount: "/dev/nvme0p1": OK + * > help + * 2025-01-14 09:48:42,300 # help + * 2025-01-14 09:48:42,302 # Command Description + * 2025-01-14 09:48:42,305 # --------------------------------------- + * 2025-01-14 09:48:42,309 # exec Execute Hello World + * 2025-01-14 09:48:42,314 # create_executable Create an XIPFS executable file + * 2025-01-14 09:48:42,317 # execute Execute an XIPFS file + * 2025-01-14 09:48:42,320 # ls list files + * 2025-01-14 09:48:42,325 # pm interact with layered PM subsystem + * 2025-01-14 09:48:42,331 # ps Prints information about running threads. + * 2025-01-14 09:48:42,334 # reboot Reboot the node + * 2025-01-14 09:48:42,338 # version Prints current RIOT_VERSION + * 2025-01-14 09:48:42,343 # vfs virtual file system operations + * > exec + * 2025-01-14 09:48:49,572 # exec + * 2025-01-14 09:48:49,573 # Hello World! + * > ls /dev/nvme0p0 + * 2025-01-14 09:48:59,997 # ls /dev/nvme0p0 + * 2025-01-14 09:48:59,999 # hello-world.fae 896 B + * 2025-01-14 09:49:00,000 # total 1 files + * > vfs df + * 2025-01-14 09:49:04,957 # vfs df + * 2025-01-14 09:49:04,962 # Mountpoint Total Used Available Use% + * 2025-01-14 09:49:04,968 # /dev/nvme0p0 40 KiB 4 KiB 36 KiB 10% + * 2025-01-14 09:49:04,974 # /dev/nvme0p1 60 KiB 0 B 60 KiB 0% + * execute /dev/nvme0p0/hello-world.fae ipsum dolores it + * 2025-01-14 09:49:14,223 # execute /dev/nvme0p0/hello-world.fae Lorem ipsum dolor sit amet + * 2025-01-14 09:49:14,225 # Hello World! + * 2025-01-14 09:49:14,225 # Lorem + * 2025-01-14 09:49:14,226 # ipsum + * 2025-01-14 09:49:14,226 # dolor + * 2025-01-14 09:49:14,227 # sit + * 2025-01-14 09:49:14,227 # amet + * ``` + */ +int execution_handler(int argc, char **argv) { + (void)argc; + (void)argv; + + int file_handle = vfs_open(FILENAME_OF_HELLO_WORLD_FAE, O_RDONLY, 0); + if (file_handle < 0) { + + /** There's no executable file yet, let's drop one */ + int ret = xipfs_extended_driver_new_file( + FILENAME_OF_HELLO_WORLD_FAE, SIZEOF_HELLO_WORLD_FAE, 1 + ); + if (ret < 0) { + printf("xipfs_extended_driver_new_file : failed to create '%s' : error=%d\n", + FILENAME_OF_HELLO_WORLD_FAE, ret); + return EXIT_FAILURE; + } + + /** + * Fill it with blob data + * Take care : vfs does not support O_APPEND with vfs_write, only O_WRONLY or O_RDWR + */ + file_handle = vfs_open(FILENAME_OF_HELLO_WORLD_FAE, O_WRONLY, 0); + if (file_handle < 0) { + printf("vfs_open : failed to open '%s' : error =%d\n", + FILENAME_OF_HELLO_WORLD_FAE, file_handle); + return EXIT_FAILURE; + } + + ssize_t write_ret = vfs_write(file_handle, hello_world_fae, SIZEOF_HELLO_WORLD_FAE); + if (write_ret < 0) { + printf("vfs_write : failed to fill '%s' : error=%d\n", + FILENAME_OF_HELLO_WORLD_FAE, write_ret); + vfs_close(file_handle); + return EXIT_FAILURE; + } + } + + vfs_close(file_handle); + + char *exec_argv[] = { + FILENAME_OF_HELLO_WORLD_FAE, + NULL + }; + + int ret = xipfs_extended_driver_execv(FILENAME_OF_HELLO_WORLD_FAE, exec_argv); + if (ret < 0) { + printf("Failed to execute '%s' : error=%d\n", FILENAME_OF_HELLO_WORLD_FAE, ret); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static shell_command_t shell_commands[] = { + {"exec", "Execute Hello World", execution_handler}, + {NULL, NULL, NULL}, +}; + +#else /* BOARD_DWM1001 */ + +static shell_command_t shell_commands[] = { {NULL, NULL, NULL} }; + +#endif /* BOARD_DWM1001 */ + +/** + * @internal + * + * @brief Mount a partition, or if it is corrupted, format and + * remount it + * + * @param xipfs_mp A pointer to a memory region containing an + * xipfs mount point structure + */ +static void mount_or_format(vfs_xipfs_mount_t *xipfs_mp) +{ + if (vfs_mount(&xipfs_mp->vfs_mp) < 0) { + printf("vfs_mount: \"%s\": file system has not been " + "initialized or is corrupted\n", xipfs_mp->vfs_mp.mount_point); + printf("vfs_format: \"%s\": try initializing it\n", + xipfs_mp->vfs_mp.mount_point); + vfs_format(&xipfs_mp->vfs_mp); + printf("vfs_format: \"%s\": OK\n", xipfs_mp->vfs_mp.mount_point); + if (vfs_mount(&xipfs_mp->vfs_mp) < 0) { + printf("vfs_mount: \"%s\": file system is corrupted!\n", + xipfs_mp->vfs_mp.mount_point); + PANIC(); + } + } + printf("vfs_mount: \"%s\": OK\n", xipfs_mp->vfs_mp.mount_point); +} + +int main(void) +{ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + + mount_or_format(&nvme0p0); + mount_or_format(&nvme0p1); + + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 24b4ea1112..b42bdf9424 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -443,6 +443,7 @@ PSEUDOMODULES += shell_cmd_sntp PSEUDOMODULES += shell_cmd_suit PSEUDOMODULES += shell_cmd_sys PSEUDOMODULES += shell_cmd_udptty +PSEUDOMODULES += shell_cmd_xipfs PSEUDOMODULES += shell_cmd_vfs PSEUDOMODULES += shell_cmds_default PSEUDOMODULES += shell_hooks diff --git a/pkg/xipfs/Makefile b/pkg/xipfs/Makefile new file mode 100644 index 0000000000..2908e1733d --- /dev/null +++ b/pkg/xipfs/Makefile @@ -0,0 +1,17 @@ +PKG_NAME=xipfs +PKG_URL=https://github.com/2xs/xipfs.git +PKG_VERSION=962c5edc55abb6363ff2988dc3b2c3c293362e96 +PKG_LICENSE=CeCILL-2.1 + +include $(RIOTBASE)/pkg/pkg.mk + +export RIOT_INCLUDES=$(INCLUDES) +export RIOT_CFLAGS=$(CFLAGS) + +all: $(BINDIR)/xipfs.a + +$(BINDIR)/xipfs.a: + $(QQ)"$(MAKE)" -C $(PKG_SOURCE_DIR) + cp $(PKG_SOURCE_DIR)/$(BOARD)/xipfs.a $@ + +.PHONY: $(BINDIR)/xipfs.a diff --git a/pkg/xipfs/Makefile.dep b/pkg/xipfs/Makefile.dep new file mode 100644 index 0000000000..556ac3748d --- /dev/null +++ b/pkg/xipfs/Makefile.dep @@ -0,0 +1,13 @@ +DEFAULT_MODULE += xipfs_fs + +# require xipfs_fs dependencies if this module isn't disabled +ifeq (,$(filter xipfs_fs,$(DISABLE_MODULE))) + # xipfs only makes sense if the flash memory is addressable + FEATURES_REQUIRED += periph_flashpage_in_address_space + FEATURES_REQUIRED += arch_32bit arch_arm + FEATURES_OPTIONAL += periph_flashpage_aux + USEMODULE += periph_flashpage + USEMODULE += mtd_flashpage + USEMODULE += vfs + USEMODULE += shell_cmd_xipfs +endif diff --git a/pkg/xipfs/Makefile.include b/pkg/xipfs/Makefile.include new file mode 100644 index 0000000000..17329310dc --- /dev/null +++ b/pkg/xipfs/Makefile.include @@ -0,0 +1,8 @@ +INCLUDES += -I$(PKGDIRBASE)/xipfs + +ifneq (,$(filter xipfs_fs,$(USEMODULE))) + DIRS += $(RIOTBASE)/pkg/xipfs/fs +endif + +# include archive +ARCHIVES += $(BINDIR)/xipfs.a diff --git a/pkg/xipfs/doc.txt b/pkg/xipfs/doc.txt new file mode 100644 index 0000000000..102b44801f --- /dev/null +++ b/pkg/xipfs/doc.txt @@ -0,0 +1,32 @@ +/** + * @defgroup pkg_xipfs xipfs file system + * @ingroup pkg + * @ingroup sys_fs + * @brief The eXecute In-Place File System + * + *`xipfs` is a file system designed to streamline post-issuance software + * deployment. + * + * `xipfs` allows direct execution of programs from flash memory, eliminating + * the need for prior copying to RAM. + * + * This approach conserves memory space and accelerates boot times, as the + * microcontroller can run code directly from storage memory without preloading into RAM. + * + * The `xipfs` structure is based on a linked list, where each file occupies at least one flash memory page. + * + * To prevent fragmentation, when a file is deleted, subsequent files are shifted to fill the vacant space. + * + *`xipfs` is compatible with all microcontrollers featuring addressable + * flash memory and most operating systems, provided they implement the + * necessary functions to interact with the flash controller. + * + * **To have a viable executable file within XiPFS**, please follow these steps : + * - create a file with executable flag. + * - Fill it with actual compiled code in an appropriate file format, here XiPFS-Format. + * *This is where the actual startup sequence and relocation happen*. + * - Call execute on it with appropriate args *at least the executable filename should be passed along*. + * + * @see https://github.com/2xs/xipfs + * @see https://github.com/2xs/XiPFS_Format + */ diff --git a/pkg/xipfs/fs/Makefile b/pkg/xipfs/fs/Makefile new file mode 100644 index 0000000000..86f433b0f6 --- /dev/null +++ b/pkg/xipfs/fs/Makefile @@ -0,0 +1,5 @@ +MODULE := xipfs_fs + +CFLAGS+=-DRIOT_OS + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/xipfs/fs/xipfs_flashpage.c b/pkg/xipfs/fs/xipfs_flashpage.c new file mode 100644 index 0000000000..8ca693d4ed --- /dev/null +++ b/pkg/xipfs/fs/xipfs_flashpage.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 Université de Lille + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_fs_xipfs + * @{ + * + * @file + * @brief xipfs integration with flashpage + * + * @author Damien Amara + * + * @} + */ + +#include "periph/flashpage.h" + +void *xipfs_nvm_addr(unsigned page) +{ + return flashpage_addr(page); +} + +void xipfs_nvm_erase(unsigned page) +{ + flashpage_erase(page); +} + +unsigned xipfs_nvm_page(const void *addr) +{ + return flashpage_page(addr); +} + +void xipfs_nvm_write(void *target_addr, const void *data, size_t len) +{ + flashpage_write(target_addr, data, len); +} diff --git a/pkg/xipfs/fs/xipfs_fs.c b/pkg/xipfs/fs/xipfs_fs.c new file mode 100644 index 0000000000..2b99770f08 --- /dev/null +++ b/pkg/xipfs/fs/xipfs_fs.c @@ -0,0 +1,790 @@ +/* + * Copyright (C) 2024 Université de Lille + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_fs_xipfs + * @{ + * + * @file + * @brief xipfs integration with vfs + * + * @author Damien Amara + * @author Gregory Guche + * + * @} + */ + +/* + * The following define is required in order to use strnlen(3) + * since glibc 2.10. Refer to the SYNOPSIS section of the + * strnlen(3) manual and the feature_test_macros(7) manual for + * more information + */ +#define _POSIX_C_SOURCE 200809L + +/* + * libc includes + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * RIOT includes + */ +#define ENABLE_DEBUG 0 +#include "debug.h" +#include "fs/xipfs_fs.h" +#include "periph/flashpage.h" + +#include "saul_reg.h" + +/* + * xipfs includes + */ +#include "include/buffer.h" +#include "include/errno.h" +#include "include/file.h" +#include "include/flash.h" +#include "include/fs.h" +#include "include/path.h" +#ifndef RIOT_OS +#include "include/xipfs.h" +#endif + +/* + * The eXecute In Place File System only makes sense if the + * non-volatile memory of the target MCU is addressable + */ +#ifndef MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE +#error "sys/fs/xipfs: the target MCU has no addressable NVM" +#endif /* !MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE */ + +/* + * Helper functions + */ + +/** + * @internal + * + * @pre dir must be a pointer that references an accessible + * memory region + * + * @pre path must be a pointer that references a path which is + * accessible, null-terminated, starts with a slash, normalized, + * and shorter than XIPFS_PATH_MAX + * + * @brief Copy the directory name component of path, including + * the final slash, into the memory region pointed to by dir + * + * @param dir A pointer to a memory region that respects the + * preconditions for storing the directory name component + * + * @param path A pointer to a path that respects the + * preconditions + */ +static void dirname(char *dir, const char *path) +{ + const char *end; + size_t len, i; + + assert(dir != NULL); + assert(path != NULL); + assert(path[0] == '/'); + + if (path[1] == '\0') { + dir[0] = '/'; + dir[1] = '\0'; + return; + } + + len = strnlen(path, XIPFS_PATH_MAX); + assert(len < XIPFS_PATH_MAX); + end = path + len - 1; + + if (*end == '/') { + /* skip the trailing slash if the + * path ends with one */ + end--; + } + + while (end > path && *end != '/') { + /* skip all characters that are not + * slashes */ + end--; + } + + if (end != path) { + for (i = 0; path + i <= end; i++) { + /* copy the characters of the directory + * name component until end */ + dir[i] = path[i]; + } + dir[i] = '\0'; + } else { + /* no slashes found, except for the root */ + dir[0] = '/'; + dir[1] = '\0'; + } +} + +/** + * @internal + * + * @warning This function provides a workaround for xipfs- + * specific functions that need to retrieve the xipfs mount + * point structure directly, bypassing the VFS layer, as these + * functions are not available in the VFS abstraction + * + * @brief Retrieves the xipfs mount point structure from a + * specified path within the mount point + * + * @param path A path within the mount point for retrieving the + * corresponding xipfs mount point structure + * + * @param mp A pointer to an accessible memory region for + * storing the xipfs mount point structure + * + * @return Returns zero if the function succeeds or a negative + * value otherwise + */ +static int get_xipfs_mp(const char *path, xipfs_mount_t *xipfs_mp) +{ + char dir[XIPFS_PATH_MAX]; + size_t count, len; + int fd, ret; + + if (path[0] != '/') { + return -EINVAL; + } + len = strnlen(path, XIPFS_PATH_MAX); + if (len == XIPFS_PATH_MAX) { + return -ENAMETOOLONG; + } + dirname(dir, path); + + if (len + strlen(".xipfs_infos") + 1 > XIPFS_PATH_MAX) { + return -ENAMETOOLONG; + } + (void)strcat(dir, ".xipfs_infos"); + + if ((ret = vfs_open(dir, O_RDONLY, 0)) < 0) { + /* not a xipfs mount point */ + return ret; + } + fd = ret; + + count = sizeof(*xipfs_mp); + while (count > 0) { + ret = vfs_read(fd, xipfs_mp, count); + if (ret < 0) { + /* error */ + return ret; + } + + count -= ret; + xipfs_mp += ret; + if (ret == 0) { + /* EOF */ + break; + } + } + assert(count == 0); + + (void)vfs_close(fd); + + return 0; +} + +/** + * @internal + * + * @pre full_path must be a pointer that references a path which + * is accessible, null-terminated, starts with a slash, + * normalized, and shorter than XIPFS_PATH_MAX + * + * @brief Returns a pointer to the first character of the + * relative path derived from the absolute path retrieved from + * the vfs_mp mount point structure + * + * @param vfs_mp A pointer to a memory region containing an + * accessible VFS mount point structure + * + * @param full_path A pointer to a path that respects the + * preconditions + */ +static const char *get_rel_path(xipfs_mount_t *mp, + const char *full_path) +{ + const char *p1, *p2; + + assert(mp != NULL); + assert(full_path != NULL); + + p1 = mp->mount_path; + p2 = full_path; + + while (*p1 != '\0') { + if (*p1++ != *p2++) { + return NULL; + } + } + + return p2; +} + +static inline xipfs_mount_t *_get_xipfs_mount_t(vfs_mount_t *vfs_mp) +{ + vfs_xipfs_mount_t *vfs_xipfs_mp; + xipfs_mount_t *xipfs_mp; + + vfs_xipfs_mp = (vfs_xipfs_mount_t *)(uintptr_t)vfs_mp; + xipfs_mp = (xipfs_mount_t *)(uintptr_t)&vfs_xipfs_mp->magic; + + return xipfs_mp; +} + +/* + * Operations on open files + */ + +static int _close(vfs_file_t *filp) +{ + xipfs_file_desc_t *descp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(filp->mp); + descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr; + + mutex_lock(mp->mutex); + ret = xipfs_close(mp, descp); + mutex_unlock(mp->mutex); + + return ret; +} + +static int _fstat(vfs_file_t *filp, struct stat *buf) +{ + xipfs_file_desc_t *descp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(filp->mp); + descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr; + + mutex_lock(mp->mutex); + ret = xipfs_fstat(mp, descp, buf); + mutex_unlock(mp->mutex); + + return ret; +} + +static off_t _lseek(vfs_file_t *filp, off_t off, int whence) +{ + xipfs_file_desc_t *descp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(filp->mp); + descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr; + + mutex_lock(mp->mutex); + ret = xipfs_lseek(mp, descp, off, whence); + mutex_unlock(mp->mutex); + + return ret; +} + +static int _open(vfs_file_t *filp, const char *name, int flags, + mode_t mode) +{ + xipfs_file_desc_t *descp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(filp->mp); + descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr; + + mutex_lock(mp->mutex); + ret = xipfs_open(mp, descp, name, flags, mode); + mutex_unlock(mp->mutex); + + return ret; +} + +static ssize_t _read(vfs_file_t *filp, void *dest, size_t nbytes) +{ + xipfs_file_desc_t *descp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(filp->mp); + descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr; + + mutex_lock(mp->mutex); + ret = xipfs_read(mp, descp, dest, nbytes); + mutex_unlock(mp->mutex); + + return ret; +} + +static ssize_t _write(vfs_file_t *filp, const void *src, size_t nbytes) +{ + xipfs_file_desc_t *descp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(filp->mp); + descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr; + + mutex_lock(mp->mutex); + ret = xipfs_write(mp, descp, src, nbytes); + mutex_unlock(mp->mutex); + + return ret; +} + +static int _fsync(vfs_file_t *filp) +{ + xipfs_file_desc_t *descp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(filp->mp); + descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr; + + mutex_lock(mp->mutex); + ret = xipfs_fsync(mp, descp, filp->pos); + mutex_unlock(mp->mutex); + + return ret; +} + +/* + * Operations on open directories + */ + +static int _opendir(vfs_DIR *dirp, const char *dirname) +{ + xipfs_dir_desc_t *descp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(dirp->mp); + descp = (xipfs_dir_desc_t *)(uintptr_t)&dirp->private_data.ptr; + + mutex_lock(mp->mutex); + ret = xipfs_opendir(mp, descp, dirname); + mutex_unlock(mp->mutex); + + return ret; +} + +static int _readdir(vfs_DIR *dirp, vfs_dirent_t *entry) +{ + xipfs_dir_desc_t *descp; + xipfs_dirent_t *direntp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(dirp->mp); + descp = (xipfs_dir_desc_t *)(uintptr_t)&dirp->private_data.ptr; + direntp = (xipfs_dirent_t *)(uintptr_t)entry->d_name; + + mutex_lock(mp->mutex); + ret = xipfs_readdir(mp, descp, direntp); + mutex_unlock(mp->mutex); + + return ret; +} + +static int _closedir(vfs_DIR *dirp) +{ + xipfs_dir_desc_t *descp; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(dirp->mp); + descp = (xipfs_dir_desc_t *)(uintptr_t)&dirp->private_data.ptr; + + mutex_lock(mp->mutex); + ret = xipfs_closedir(mp, descp); + mutex_unlock(mp->mutex); + + return ret; +} + +/* + * Operations on mounted file systems + */ + +static int _format(vfs_mount_t *vfs_mp) +{ + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(vfs_mp); + + mutex_lock(mp->execution_mutex); + mutex_lock(mp->mutex); + ret = xipfs_format(mp); + mutex_lock(mp->mutex); + mutex_lock(mp->execution_mutex); + + return ret; +} + +static int _mount(vfs_mount_t *vfs_mp) +{ + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(vfs_mp); + + mutex_lock(mp->mutex); + ret = xipfs_mount(mp); + mutex_unlock(mp->mutex); + + return ret; +} + +static int _umount(vfs_mount_t *vfs_mp) +{ + (void)vfs_mp; + + /* nothing to do */ + + return 0; +} + +static int _unlink(vfs_mount_t *vfs_mp, const char *name) +{ + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(vfs_mp); + + mutex_lock(mp->execution_mutex); + mutex_lock(mp->mutex); + ret = xipfs_unlink(mp, name); + mutex_unlock(mp->mutex); + mutex_lock(mp->execution_mutex); + + return ret; +} + +static int _mkdir(vfs_mount_t *vfs_mp, const char *name, mode_t mode) +{ + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(vfs_mp); + + mutex_lock(mp->mutex); + ret = xipfs_mkdir(mp, name, mode); + mutex_unlock(mp->mutex); + + return ret; +} + +static int _rmdir(vfs_mount_t *vfs_mp, const char *name) +{ + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(vfs_mp); + + mutex_lock(mp->execution_mutex); + mutex_lock(mp->mutex); + ret = xipfs_rmdir(mp, name); + mutex_unlock(mp->mutex); + mutex_unlock(mp->execution_mutex); + + return ret; +} + +static int _rename(vfs_mount_t *vfs_mp, const char *from_path, + const char *to_path) +{ + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(vfs_mp); + + mutex_lock(mp->mutex); + ret = xipfs_rename(mp, from_path, to_path); + mutex_unlock(mp->mutex); + + return ret; +} + +static int _stat(vfs_mount_t *vfs_mp, const char *restrict path, + struct stat *restrict buf) +{ + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(vfs_mp); + + mutex_lock(mp->mutex); + ret = xipfs_stat(mp, path, buf); + mutex_unlock(mp->mutex); + + return ret; +} + +static int _statvfs(vfs_mount_t *vfs_mp, const char *restrict path, + struct statvfs *restrict buf) +{ + struct xipfs_statvfs xipfs_buf; + xipfs_mount_t *mp; + int ret; + + mp = _get_xipfs_mount_t(vfs_mp); + + mutex_lock(mp->mutex); + ret = xipfs_statvfs(mp, path, &xipfs_buf); + mutex_unlock(mp->mutex); + + (void)memset(buf, 0, sizeof(*buf)); + buf->f_bsize = xipfs_buf.f_bsize; + buf->f_frsize = xipfs_buf.f_frsize; + buf->f_blocks = xipfs_buf.f_blocks; + buf->f_bfree = xipfs_buf.f_bfree; + buf->f_bavail = xipfs_buf.f_bavail; + buf->f_flag = xipfs_buf.f_flag; + buf->f_namemax = xipfs_buf.f_namemax; + + return ret; +} + +/* + * xipfs-specific functions + */ + +int xipfs_extended_driver_new_file(const char *full_path, uint32_t size, uint32_t exec) +{ + xipfs_mount_t mp; + const char *path; + int ret; + + if (full_path == NULL) { + return -EFAULT; + } + if ((ret = get_xipfs_mp(full_path, &mp)) < 0) { + return ret; + } + if ((path = get_rel_path(&mp, full_path)) == NULL) { + return -EIO; + } + + mutex_lock(mp.mutex); + ret = xipfs_new_file(&mp, path, size, exec); + mutex_unlock(mp.mutex); + + return ret; +} + +static int get_temperature(void) { + phydat_t physical_data; + saul_reg_t *device = saul_reg_find_type(SAUL_SENSE_TEMP); + int res = saul_reg_read(device, &physical_data); + if (res < 0) { + return res; + } + + return physical_data.val[0]; +} + +static int get_led(int pos) { + phydat_t physical_data; + saul_reg_t *device = saul_reg_find_nth(pos); + int res = saul_reg_read(device, &physical_data); + if (res < 0) { + return res; + } + + return physical_data.val[0]; +} + +static int set_led(int pos, int val) { + phydat_t physical_data; + saul_reg_t *device = saul_reg_find_nth(pos); + physical_data.val[0] = val; + int res = saul_reg_write(device, &physical_data); + return res; +} + +static int get_file_size(const char *full_path, size_t *size) { + xipfs_mount_t mp; + const char *path; + int ret; + struct stat buf; + + if ((full_path == NULL) || (size == NULL)) { + return -EFAULT; + } + if ((ret = get_xipfs_mp(full_path, &mp)) < 0) { + return ret; + } + if ((path = get_rel_path(&mp, full_path)) == NULL) { + return -EIO; + } + + *size = 0; + ret = xipfs_stat(&mp, path, &buf); + if (ret < 0) { + return ret; + } + *size = buf.st_size; + return 0; +} + +static ssize_t copy_file(const char *full_path, void *buf, size_t nbyte) { + xipfs_mount_t mp; + const char *path; + xipfs_file_desc_t desc; + int ret; + size_t file_size; + + ret = get_file_size(full_path, &file_size); + if (ret < 0) { + return ret; + } + if (nbyte > file_size) { + nbyte = file_size; + } + if (full_path == NULL) { + return -EFAULT; + } + if ((ret = get_xipfs_mp(full_path, &mp)) < 0) { + return ret; + } + if ((path = get_rel_path(&mp, full_path)) == NULL) { + return -EIO; + } + + ret = xipfs_open(&mp, &desc, path, O_RDONLY, 0); + if (ret < 0) { + return ret; + } + ret = xipfs_read(&mp, &desc, buf, nbyte); + if (ret < 0) { + return ret; + } + ret = xipfs_close(&mp, &desc); + + return nbyte; +} + +static const void *xipfs_user_syscalls_table[XIPFS_USER_SYSCALL_MAX] = { + [ XIPFS_USER_SYSCALL_PRINTF] = vprintf, + [ XIPFS_USER_SYSCALL_GET_TEMP] = get_temperature, + [ XIPFS_USER_SYSCALL_ISPRINT] = isprint, + [ XIPFS_USER_SYSCALL_STRTOL] = strtol, + [ XIPFS_USER_SYSCALL_GET_LED] = get_led, + [ XIPFS_USER_SYSCALL_SET_LED] = set_led, + [ XIPFS_USER_SYSCALL_COPY_FILE] = copy_file, + [XIPFS_USER_SYSCALL_GET_FILE_SIZE] = get_file_size, + [ XIPFS_USER_SYSCALL_MEMSET] = memset +}; + +int xipfs_extended_driver_execv(const char *full_path, char *const argv[]) +{ + xipfs_mount_t mp; + const char *path; + int ret; + + if (full_path == NULL) { + return -EFAULT; + } + if ((ret = get_xipfs_mp(full_path, &mp)) < 0) { + return ret; + } + if ((path = get_rel_path(&mp, full_path)) == NULL) { + return -EIO; + } + + mutex_lock(mp.execution_mutex); + ret = xipfs_execv(&mp, path, argv, xipfs_user_syscalls_table); + mutex_unlock(mp.execution_mutex); + + return ret; +} + +/* + * File system driver structures + */ + +static const vfs_file_ops_t xipfs_file_ops = { + .close = _close, + .fstat = _fstat, + .lseek = _lseek, + .open = _open, + .read = _read, + .write = _write, + .fsync = _fsync, +}; + +static const vfs_dir_ops_t xipfs_dir_ops = { + .opendir = _opendir, + .readdir = _readdir, + .closedir = _closedir, +}; + +static const vfs_file_system_ops_t xipfs_fs_ops = { + .format = _format, + .mount = _mount, + .umount = _umount, + .unlink = _unlink, + .mkdir = _mkdir, + .rmdir = _rmdir, + .rename = _rename, + .stat = _stat, + .statvfs = _statvfs, +}; + +const vfs_file_system_t xipfs_file_system = { + .fs_op = &xipfs_fs_ops, + .f_op = &xipfs_file_ops, + .d_op = &xipfs_dir_ops, +}; + +int xipfs_construct_from_flashpage(mtd_flashpage_t *flashpage, const char *path, + mutex_t *execution_mutex, mutex_t *mutex, + vfs_xipfs_mount_t *vfs_xipfs_mount) { + + if ( (flashpage == NULL) || (path == NULL) || (path[0] == '\0') + || (execution_mutex == NULL) || (mutex == NULL) ) { + return -EINVAL; + } + + vfs_xipfs_mount->vfs_mp.fs = &xipfs_file_system; + vfs_xipfs_mount->vfs_mp.mount_point = path; + + vfs_xipfs_mount->magic = XIPFS_MAGIC; + vfs_xipfs_mount->mount_path = path; + vfs_xipfs_mount->page_num = flashpage->base.sector_count; + vfs_xipfs_mount->page_addr = (void *)( + (unsigned char *)XIPFS_NVM_BASE + + (flashpage->offset * XIPFS_NVM_PAGE_SIZE) + ); + vfs_xipfs_mount->execution_mutex = execution_mutex; + mutex_init(vfs_xipfs_mount->execution_mutex); + vfs_xipfs_mount->mutex = mutex; + mutex_init(vfs_xipfs_mount->mutex); + + return 0; +} diff --git a/sys/include/fs/xipfs_fs.h b/sys/include/fs/xipfs_fs.h new file mode 100644 index 0000000000..ce3f0827ad --- /dev/null +++ b/sys/include/fs/xipfs_fs.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2024 Université de Lille + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_xipfs xipfs integration + * @ingroup pkg_xipfs + * @brief RIOT integration of xipfs + * + * @{ + * + * @file + * @brief xipfs integration with vfs + * + * @author Damien Amara + * @author Gregory Guche + */ + +#ifndef FS_XIPFS_FS_H +#define FS_XIPFS_FS_H + +#include "vfs.h" +#include "mtd_flashpage.h" +#include "mutex.h" +#include "include/xipfs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @def XIPFS_NEW_PARTITION + * + * @brief Allocate a new contiguous space aligned to a page in + * the non-volatile addressable memory of the MCU to serve as a + * partition for an xipfs file system + * + * @param id Identifier name for the mount point used by + * functions that manipulate xipfs file systems + * + * @param path The mount point of the file system in the VFS tree + * + * @param num The total number of pages allocated for the + * partition + */ +#define XIPFS_NEW_PARTITION(id, path, num) \ + FLASH_WRITABLE_INIT(xipfs_part_##id, num); \ + static mutex_t execution_mutex_##id = MUTEX_INIT; \ + static mutex_t mutex_##id = MUTEX_INIT; \ + static vfs_xipfs_mount_t id = { \ + .vfs_mp = { \ + .fs = &xipfs_file_system, \ + .mount_point = path, \ + }, \ + .magic = XIPFS_MAGIC, \ + .mount_path = path, \ + .page_num = num, \ + .page_addr = (void *)xipfs_part_##id, \ + .execution_mutex = &execution_mutex_##id, \ + .mutex = &mutex_##id \ + } + +/** + * @brief xipfs descriptor for vfs integration + * + * execution_mutex is taken by xipfs_extended_driver_execv and by deletion operations. + * mutex is taken by all operations except from xipfs_extended_driver_execv + * + * This two mutexes scheme : + * - allows to call xipfs operation from within a code run by xipfs_extended_driver_execv, + * - prevents from deleting files or directories when operations are performed on them. + */ +typedef struct vfs_xipfs_mount_s { + vfs_mount_t vfs_mp; /**< VFS mount point */ + unsigned magic; /**< xipfs magic number */ + const char *mount_path; /**< mount point path */ + size_t page_num; /**< number of flash page */ + void *page_addr; /**< first flash page address */ + mutex_t *execution_mutex;/**< For execution and deletion operations */ + mutex_t *mutex; /**< For regular and deletion operations */ +} vfs_xipfs_mount_t; + +/** The xipfs vfs driver */ +extern const vfs_file_system_t xipfs_file_system; + +/** + * @brief vfs_xipfs_mount_t constructor from a mtd_flashpage_t. + * + * This function constructs an xipfs mount point from a mtd_flashpage_t such as mtd_flash_aux_slot. + * It allows to use XIPFS with the RIOT AUX slot mechanism. + * + * @param flashpage A valid mtd_flashpage_t pointer such as &mtd_flash_aux_slot. + * @param path A valid mounting point path. + * @param execution_mutex A valid mutex pointer used for execution and deletions operations. + * @param mutex A valid mutex pointer used by all operations except from execution. + * @param vfs_xipfs_mount A valid vfs xipfs mount point pointer. + * + * @retval -EINVAL when one of the parameters is NULL or when path is an empty string. + * @retval 0 on success. + * + * @note Both mutexes will be initialized by this function. + */ +int xipfs_construct_from_flashpage(mtd_flashpage_t *flashpage, const char *path, + mutex_t *execution_mutex, mutex_t *mutex, + vfs_xipfs_mount_t *vfs_xipfs_mount); + +/* Extended driver handling executables */ + +/** + * @brief Executable or regular new file. + * + * Allows to create a regular or executable new file within XiPFS. + * + * @param full_path A full path such as `/dev/nvme0p0/my_new_file` + * + * @param size The file size in bytes. + * + * @param exec 0 for regular files, 1 for executable files. + * + * @retval Less than 0 on errors. + * @retval 0 on success. + */ +int xipfs_extended_driver_new_file(const char *full_path, uint32_t size, uint32_t exec); + +/** + * @brief Executes an executable file with arguments. + * + * @param full_path A full path such as `/dev/nvme0p0/my_executable_file` + * + * @param argv Executable arguments. Cannot be NULL, argv[0] contains the executable filename. + * + * @retval Less than 0 on errors. + * @retval 0 on success. + */ +int xipfs_extended_driver_execv(const char *full_path, char *const argv[]); + +#ifdef __cplusplus +} +#endif + +#endif /* FS_XIPFS_FS_H */ +/** @} */ diff --git a/sys/include/vfs.h b/sys/include/vfs.h index 51f3a31c03..4204081a7c 100644 --- a/sys/include/vfs.h +++ b/sys/include/vfs.h @@ -78,10 +78,10 @@ extern "C" { #endif /** - * @brief MAX6 Function to get the largest of 6 values + * @brief MAX7 Function to get the largest of 7 values */ -#ifndef MAX6 -#define MAX6(a, b, c, d, e, f) MAX(MAX(MAX(MAX((a), (b)), MAX((c), (d))), (e)), (f)) +#ifndef MAX7 +#define MAX7(a, b, c, d, e, f, g) MAX(MAX(MAX(MAX(MAX((a), (b)), MAX((c), (d))), (e)), (f)), (g)) #endif /** @@ -217,6 +217,19 @@ extern "C" { #endif /** @} */ +/** + * @brief VFS parameters for xipfs + * @{ + */ +#if defined(MODULE_XIPFS) || DOXYGEN +# define XIPFS_VFS_DIR_BUFFER_SIZE (68) /**< sizeof(xipfs_dir_desc_t) */ +# define XIPFS_VFS_FILE_BUFFER_SIZE (12) /**< sizeof(xipfs_file_desc_t) */ +#else +# define XIPFS_VFS_DIR_BUFFER_SIZE (1) +# define XIPFS_VFS_FILE_BUFFER_SIZE (1) +#endif +/** @} */ + #ifndef VFS_MAX_OPEN_FILES /** * @brief Maximum number of simultaneous open files @@ -252,12 +265,13 @@ extern "C" { * @attention Put the check in the public header file (.h), do not put the check in the * implementation (.c) file. */ -#define VFS_DIR_BUFFER_SIZE MAX6(FATFS_VFS_DIR_BUFFER_SIZE, \ - LITTLEFS_VFS_DIR_BUFFER_SIZE, \ - LITTLEFS2_VFS_DIR_BUFFER_SIZE, \ - SPIFFS_VFS_DIR_BUFFER_SIZE, \ - LWEXT4_VFS_DIR_BUFFER_SIZE, \ - NANOCOAP_FS_VFS_DIR_BUFFER_SIZE \ +#define VFS_DIR_BUFFER_SIZE MAX7(FATFS_VFS_DIR_BUFFER_SIZE, \ + LITTLEFS_VFS_DIR_BUFFER_SIZE, \ + LITTLEFS2_VFS_DIR_BUFFER_SIZE, \ + SPIFFS_VFS_DIR_BUFFER_SIZE, \ + LWEXT4_VFS_DIR_BUFFER_SIZE, \ + NANOCOAP_FS_VFS_DIR_BUFFER_SIZE, \ + XIPFS_VFS_DIR_BUFFER_SIZE \ ) #endif @@ -281,12 +295,13 @@ extern "C" { * @attention Put the check in the public header file (.h), do not put the check in the * implementation (.c) file. */ -#define VFS_FILE_BUFFER_SIZE MAX6(FATFS_VFS_FILE_BUFFER_SIZE, \ - LITTLEFS_VFS_FILE_BUFFER_SIZE, \ - LITTLEFS2_VFS_FILE_BUFFER_SIZE, \ - SPIFFS_VFS_FILE_BUFFER_SIZE, \ - LWEXT4_VFS_FILE_BUFFER_SIZE, \ - NANOCOAP_FS_VFS_FILE_BUFFER_SIZE \ +#define VFS_FILE_BUFFER_SIZE MAX7(FATFS_VFS_FILE_BUFFER_SIZE, \ + LITTLEFS_VFS_FILE_BUFFER_SIZE, \ + LITTLEFS2_VFS_FILE_BUFFER_SIZE, \ + SPIFFS_VFS_FILE_BUFFER_SIZE, \ + LWEXT4_VFS_FILE_BUFFER_SIZE, \ + NANOCOAP_FS_VFS_FILE_BUFFER_SIZE, \ + XIPFS_VFS_FILE_BUFFER_SIZE \ ) #endif diff --git a/sys/include/vfs_default.h b/sys/include/vfs_default.h index d60e375331..95963fc82c 100644 --- a/sys/include/vfs_default.h +++ b/sys/include/vfs_default.h @@ -43,6 +43,9 @@ #if IS_USED(MODULE_LWEXT4) #include "fs/lwext4_fs.h" #endif +#if IS_USED(MODULE_XIPFS) +#include "fs/xipfs_fs.h" +#endif #if IS_USED(MODULE_FS_NATIVE) #include "fs/native_fs.h" #endif diff --git a/sys/shell/Makefile.dep b/sys/shell/Makefile.dep index 0563d0f1f7..ddaeec0ec2 100644 --- a/sys/shell/Makefile.dep +++ b/sys/shell/Makefile.dep @@ -288,6 +288,10 @@ ifneq (,$(filter shell_cmd_vfs,$(USEMODULE))) USEMODULE += tiny_strerror endif +ifneq (,$(filter shell_cmd_xipfs,$(USEMODULE))) + USEMODULE += xipfs +endif + ifneq (,$(filter shell_democommands,$(USEMODULE))) USEMODULE += rust_riotmodules USEMODULE += shell diff --git a/sys/shell/cmds/xipfs.c b/sys/shell/cmds/xipfs.c new file mode 100644 index 0000000000..e4acce9e46 --- /dev/null +++ b/sys/shell/cmds/xipfs.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2024 Université de Lille + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_shell_commands + * @{ + * + * @file + * @brief Shell commands for XIPFS + * + * @author Gregory Guche + * + * @} + */ + +#if defined(MODULE_XIPFS_FS) || defined(MODULE_XIPFS) +#include +#include +#include + +#include "fs/xipfs_fs.h" +#include "include/xipfs.h" +#include "shell.h" + +static char *execute_file_handler_args[XIPFS_EXEC_ARGC_MAX]; + +static int _execute_file_handler(int argc, char **argv) { + if ( (argc == 1) || (argc > XIPFS_EXEC_ARGC_MAX) ) { + printf("Usage %s xipfs_executable_filename [arg0] [arg1] ... [arg%d]\n", + argv[0], (XIPFS_EXEC_ARGC_MAX - 1)); + printf("\t- xipfs_executable_filename : filename of the desired XIPFS file to execute\n"); + return 1; + } + + memset(execute_file_handler_args, 0, sizeof(execute_file_handler_args)); + for (int i = 1; i = (unsigned long)UINT32_MAX) { + printf("Error : xipfs_executable_file_bytesize must be less than %lu.\n", UINT32_MAX); + print_create_executable_file_usage(argc, argv); + return 3; + } + + int ret = xipfs_extended_driver_new_file(argv[1], (uint32_t)file_size, 1); + if (ret != 0) { + printf("Failed to create '%s' as an XIPFS executable file.", argv[1]); + return 4; + } + + return 0; +} + +SHELL_COMMAND(create_executable, "Create an XIPFS executable file", _create_executable_file); + +#endif /* MODULE_XIPFS_FS */ diff --git a/tests/pkg/xipfs/Makefile b/tests/pkg/xipfs/Makefile new file mode 100644 index 0000000000..6d45c5ed33 --- /dev/null +++ b/tests/pkg/xipfs/Makefile @@ -0,0 +1,13 @@ +include ../Makefile.pkg_common + +BOARD ?= dwm1001 + +SLOT_AUX_LEN := 0x8000 + +USEPKG += xipfs +USEMODULE += xipfs +USEMODULE += saul_default + +TOOLCHAINS_BLACKLIST += llvm + +include $(RIOTBASE)/Makefile.include diff --git a/tests/pkg/xipfs/Makefile.ci b/tests/pkg/xipfs/Makefile.ci new file mode 100644 index 0000000000..26419307b5 --- /dev/null +++ b/tests/pkg/xipfs/Makefile.ci @@ -0,0 +1,26 @@ +BOARD_INSUFFICIENT_MEMORY := \ + blackpill-stm32f103c8 \ + bluepill-stm32f030c8 \ + bluepill-stm32f103c8 \ + i-nucleo-lrwan1 \ + nucleo-c031c6 \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f302r8 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l011k4 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + samd10-xmini \ + saml10-xpro \ + saml11-xpro \ + slstk3400a \ + stk3200 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32g0316-disco \ + stm32l0538-disco \ + weact-g030f6 \ + # diff --git a/tests/pkg/xipfs/README.md b/tests/pkg/xipfs/README.md new file mode 100644 index 0000000000..240746a545 --- /dev/null +++ b/tests/pkg/xipfs/README.md @@ -0,0 +1,20 @@ +# The eXecute In-Place File System tests + +## Description + +This application is a best effort to test the return codes of system +calls provided by `xipfs`. It accomplishes this by executing these calls +in various environments, ensuring that each system call returns the +expected error codes. However, further in-depth tests need to be done. + +## Tested cards + +`xipfs-tests` is expected to be compatible with all boards that feature +addressable NVM. However, only the `DWM1001` board has been tested and +is confirmed to function correctly. + +## Funding + +The `xipfs-tests` project is part of the TinyPART project funded by the +MESRI-BMBF German-French cybersecurity program under grant agreements +n°ANR-20-CYAL-0005 and 16KIS1395K. diff --git a/tests/pkg/xipfs/main.c b/tests/pkg/xipfs/main.c new file mode 100644 index 0000000000..69e60c65d3 --- /dev/null +++ b/tests/pkg/xipfs/main.c @@ -0,0 +1,2507 @@ +/* + * Copyright (C) 2024 Université de Lille + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief An application for testing xipfs + * + * @author Damien Amara + * @author Gregory Guche + * @} + */ + +#include + +#include "fs/xipfs_fs.h" +#include "periph/flashpage.h" +#include "shell.h" +#include "vfs.h" +#include "mtd_flashpage.h" +#include "include/xipfs.h" + +/** + * @def PANIC + * + * @brief This macro handles fatal errors + */ +#define PANIC() for (;;) {} + +/** + * @def NVME0P0_PAGE_NUM + * + * @brief The number of flash page for the nvme0p0 file system + */ +#define NVME0P0_PAGE_NUM 20 + +#define XIPFS_ASSERT(condition) \ +do { \ + if (!(condition)) { \ + printf("Line %d ", __LINE__); \ + } \ + assert((condition)); \ +} while (0) + +/* + * Allocate a new contiguous space for the nvme0p0 file system + */ +XIPFS_NEW_PARTITION(nvme0p0, "/dev/nvme0p0", NVME0P0_PAGE_NUM); + +/* + * Get a pointer to an xipfs_mount_t from a vfs_xipfs_mount_t + */ +xipfs_mount_t *xipfs_nvme0p0 = NULL; + +/* + * Test function prototypes + */ + +static void test_xipfs_close_ebadf_descp(void); +static void test_xipfs_close_ok(void); +static void test_xipfs_fstat_ebadbf_descp(void); +static void test_xipfs_fstat_efault_buf(void); +static void test_xipfs_fstat_ebadbf_xipfs_infos(void); +static void test_xipfs_fstat_ok(void); +static void test_xipfs_lseek_einval_descp(void); +static void test_xipfs_lseek_ebadf_descp(void); +static void test_xipfs_lseek_einval_whence(void); +static void test_xipfs_lseek_einval_new_pos(void); +static void test_xipfs_lseek_ok(void); +static void test_xipfs_open_efault_name(void); +static void test_xipfs_open_einval_flags(void); +static void test_xipfs_open_enametoolong_name(void); +static void test_xipfs_open_eexist_xipfs_infos_flags(void); +static void test_xipfs_open_eacces_xipfs_infos_flags(void); +static void test_xipfs_open_eexist_name(void); +static void test_xipfs_open_eisdir_name(void); +static void test_xipfs_open_enotdir_name(void); +static void test_xipfs_open_enoent_flags(void); +static void test_xipfs_open_eisdir_name_trailing_slash(void); +static void test_xipfs_open_edquot(void); +static void test_xipfs_open_ok(void); +static void test_xipfs_read_ebadf_descp(void); +static void test_xipfs_read_efault_dest(void); +static void test_xipfs_read_eacces_flags(void); +static void test_xipfs_read_ok(void); +static void test_xipfs_write_ebadf_descp(void); +static void test_xipfs_write_efault_src(void); +static void test_xipfs_write_eacces_flags(void); +static void test_xipfs_write_ok(void); +static void test_xipfs_fsync_ebadf_descp(void); +static void test_xipfs_fsync_eacces_flags(void); +static void test_xipfs_fsync_ok(void); +static void test_xipfs_opendir_efault_dirname(void); +static void test_xipfs_opendir_enoent_dirname_null_char(void); +static void test_xipfs_opendir_enametoolong_dirname(void); +static void test_xipfs_opendir_enotdir_dirname(void); +static void test_xipfs_opendir_enoent_dirname(void); +static void test_xipfs_opendir_ok(void); +static void test_xipfs_readdir_efault_direntp(void); +static void test_xipfs_readdir_ebadf_descp(void); +static void test_xipfs_readdir_ok(void); +static void test_xipfs_closedir_ebadf_descp(void); +static void test_xipfs_closedir_ok(void); +static void test_xipfs_unlink_efault_name(void); +static void test_xipfs_unlink_enoent_name_null_char(void); +static void test_xipfs_unlink_eisdir_name_root(void); +static void test_xipfs_unlink_enametoolong_name(void); +static void test_xipfs_unlink_eisdir_name(void); +static void test_xipfs_unlink_enotdir_name(void); +static void test_xipfs_unlink_enoent_name(void); +static void test_xipfs_unlink_ok(void); +static void test_xipfs_mkdir_efault_name(void); +static void test_xipfs_mkdir_enoent_name_null_char(void); +static void test_xipfs_mkdir_eexist_name_root(void); +static void test_xipfs_mkdir_enametoolong_name(void); +static void test_xipfs_mkdir_eexist_name(void); +static void test_xipfs_mkdir_enotdir_name(void); +static void test_xipfs_mkdir_enoent_name(void); +static void test_xipfs_mkdir_enametoolong_name_trailing_slash(void); +static void test_xipfs_mkdir_ok(void); +static void test_xipfs_rmdir_efault_name(void); +static void test_xipfs_rmdir_enoent_name_null_char(void); +static void test_xipfs_rmdir_ebusy_name_root(void); +static void test_xipfs_rmdir_enametoolong_name(void); +static void test_xipfs_rmdir_einval_name(void); +static void test_xipfs_rmdir_enotdir_name(void); +static void test_xipfs_rmdir_enotempty_name(void); +static void test_xipfs_rmdir_enoent_name(void); +static void test_xipfs_rmdir_ok(void); +static void test_xipfs_rename_efault_from_path(void); +static void test_xipfs_rename_efault_to_path(void); +static void test_xipfs_rename_enoent_from_path_null_char(void); +static void test_xipfs_rename_enoent_to_path_null_char(void); +static void test_xipfs_rename_enametoolong_from_path(void); +static void test_xipfs_rename_enametoolong_to_path(void); +static void test_xipfs_rename_eisdir_from_path_file_to_path_dirs(void); +static void test_xipfs_rename_enotdir_from_path_file_to_path_not_dirs(void); +static void test_xipfs_rename_enoent_from_path_file_to_path_not_found(void); +static void test_xipfs_rename_enotdir_from_path_file_to_path_creatable_trailing_slash(void); +static void test_xipfs_rename_enotdir_from_path_empty_dir_to_path_file(void); +static void test_xipfs_rename_enotempty_from_path_empty_dir_to_path_nonempty_dir(void); +static void test_xipfs_rename_enotdir_from_path_empty_dir_to_path_not_dirs(void); +static void test_xipfs_rename_enoent_from_path_empty_dir_to_path_not_found(void); +static void test_xipfs_rename_enametoolong_from_path_empty_dir_to_path_trailing_slash(void); +static void test_xipfs_rename_einval_from_path_empty_dir_to_path_creatable_subdir_itself(void); +static void test_xipfs_rename_enotdir_from_path_nonempty_dir_to_path_file(void); +static void test_xipfs_rename_einval_from_path_nonempty_dir_to_path_empty_dir_subdir_itself(void); +static void test_xipfs_rename_enotempty_from_path_nonempty_dir_to_path_nonempty_dir(void); +static void test_xipfs_rename_enotdir_from_path_nonempty_dir_to_path_not_dirs(void); +static void test_xipfs_rename_enoent_from_path_nonempty_dir_to_path_not_found(void); +static void test_xipfs_rename_enametoolong_from_path_nonempty_dir_to_path_trailing_slash(void); +static void test_xipfs_rename_enotdir_from_path_not_dirs(void); +static void test_xipfs_rename_enoent_from_path_not_found(void); +static void test_xipfs_rename_ok(void); +static void test_xipfs_stat_efault_path(void); +static void test_xipfs_stat_efault_buf(void); +static void test_xipfs_stat_enoent_path_null_char(void); +static void test_xipfs_stat_enametoolong_path(void); +static void test_xipfs_stat_enotdir_path(void); +static void test_xipfs_stat_enoent_path(void); +static void test_xipfs_stat_ok(void); +static void test_xipfs_statvfs_efault_buf(void); +static void test_xipfs_statvfs_ok(void); +static void test_xipfs_new_file_efault_path(void); +static void test_xipfs_new_file_enoent_path_null_char(void); +static void test_xipfs_new_file_eisdir_path_root(void); +static void test_xipfs_new_file_enametoolong_path(void); +static void test_xipfs_new_file_einval_exec(void); +static void test_xipfs_new_file_eexist_path(void); +static void test_xipfs_new_file_eisdir_path(void); +static void test_xipfs_new_file_enotdir_path(void); +static void test_xipfs_new_file_enoent_path(void); +static void test_xipfs_new_file_eisdir_trailing_slash(void); +static void test_xipfs_new_file_edquot(void); +static void test_xipfs_new_file_ok(void); +static void test_xipfs_execv_efault_path(void); +static void test_xipfs_execv_enoent_path_null_char(void); +static void test_xipfs_execv_eisdir_path_root(void); +static void test_xipfs_execv_enametoolong_path(void); +static void test_xipfs_execv_efault_no_syscalls_table(void); +static void test_xipfs_execv_efault_syscalls_table_with_null(void); +static void test_xipfs_execv_eisdir_path(void); +static void test_xipfs_execv_enotdir_path(void); +static void test_xipfs_execv_enoent_path(void); + +void test_xipfs_suite(vfs_xipfs_mount_t *vfs_xipfs_mount) { + int ret; + + printf( "Tests started with vfs_xipfs_mount %p, " + "awaiting for normal termination or assertion...\n", + vfs_xipfs_mount); + + xipfs_nvme0p0 = (xipfs_mount_t *)&(vfs_xipfs_mount->magic); + + /* Should not fail unless there is a corrupted data + * structure or a flash memory failure. */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); + + /* Should not fail unless there is a corrupted data + * structure or a flash memory failure. */ + ret = xipfs_mount(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); + + /* xipfs_close */ + test_xipfs_close_ebadf_descp(); + test_xipfs_close_ok(); + + /* xipfs_fstat */ + test_xipfs_fstat_ebadbf_descp(); + test_xipfs_fstat_efault_buf(); + test_xipfs_fstat_ebadbf_xipfs_infos(); + test_xipfs_fstat_ok(); + + /* xipfs_lseek */ + test_xipfs_lseek_einval_descp(); + test_xipfs_lseek_ebadf_descp(); + test_xipfs_lseek_einval_whence(); + test_xipfs_lseek_einval_new_pos(); + test_xipfs_lseek_ok(); + + /* xipfs_open */ + test_xipfs_open_efault_name(); + test_xipfs_open_einval_flags(); + test_xipfs_open_enametoolong_name(); + test_xipfs_open_eexist_xipfs_infos_flags(); + test_xipfs_open_eacces_xipfs_infos_flags(); + test_xipfs_open_eexist_name(); + test_xipfs_open_eisdir_name(); + test_xipfs_open_enotdir_name(); + test_xipfs_open_enoent_flags(); + test_xipfs_open_eisdir_name_trailing_slash(); + test_xipfs_open_edquot(); + test_xipfs_open_ok(); + + /* xipfs_read */ + test_xipfs_read_ebadf_descp(); + test_xipfs_read_efault_dest(); + test_xipfs_read_eacces_flags(); + test_xipfs_read_ok(); + + /* xipfs_write */ + test_xipfs_write_ebadf_descp(); + test_xipfs_write_efault_src(); + test_xipfs_write_eacces_flags(); + test_xipfs_write_ok(); + + /* xipfs_fsync */ + test_xipfs_fsync_ebadf_descp(); + test_xipfs_fsync_eacces_flags();; + test_xipfs_fsync_ok(); + + /* xipfs_opendir */ + test_xipfs_opendir_efault_dirname(); + test_xipfs_opendir_enoent_dirname_null_char(); + test_xipfs_opendir_enametoolong_dirname(); + test_xipfs_opendir_enotdir_dirname(); + test_xipfs_opendir_enoent_dirname(); + test_xipfs_opendir_ok(); + + /* xipfs_readdir */ + test_xipfs_readdir_efault_direntp(); + test_xipfs_readdir_ebadf_descp(); + test_xipfs_readdir_ok(); + + /* xipfs_closedir */ + test_xipfs_closedir_ebadf_descp(); + test_xipfs_closedir_ok(); + + /* xipfs_unlink */ + test_xipfs_unlink_efault_name(); + test_xipfs_unlink_enoent_name_null_char(); + test_xipfs_unlink_eisdir_name_root(); + test_xipfs_unlink_enametoolong_name(); + test_xipfs_unlink_eisdir_name(); + test_xipfs_unlink_enotdir_name(); + test_xipfs_unlink_enoent_name(); + test_xipfs_unlink_ok(); + + /* xipfs_mkdir */ + test_xipfs_mkdir_efault_name(); + test_xipfs_mkdir_enoent_name_null_char(); + test_xipfs_mkdir_eexist_name_root(); + test_xipfs_mkdir_enametoolong_name(); + test_xipfs_mkdir_eexist_name(); + test_xipfs_mkdir_enotdir_name(); + test_xipfs_mkdir_enoent_name(); + test_xipfs_mkdir_enametoolong_name_trailing_slash(); + test_xipfs_mkdir_ok(); + + /* xipfs_rmdir */ + test_xipfs_rmdir_efault_name(); + test_xipfs_rmdir_enoent_name_null_char(); + test_xipfs_rmdir_ebusy_name_root(); + test_xipfs_rmdir_enametoolong_name(); + test_xipfs_rmdir_einval_name(); + test_xipfs_rmdir_enotdir_name(); + test_xipfs_rmdir_enotempty_name(); + test_xipfs_rmdir_enoent_name(); + test_xipfs_rmdir_ok(); + + /* xipfs_rename */ + test_xipfs_rename_efault_from_path(); + test_xipfs_rename_efault_to_path(); + test_xipfs_rename_enoent_from_path_null_char(); + test_xipfs_rename_enoent_to_path_null_char(); + test_xipfs_rename_enametoolong_from_path(); + test_xipfs_rename_enametoolong_to_path(); + test_xipfs_rename_eisdir_from_path_file_to_path_dirs(); + test_xipfs_rename_enotdir_from_path_file_to_path_not_dirs(); + test_xipfs_rename_enoent_from_path_file_to_path_not_found(); + test_xipfs_rename_enotdir_from_path_file_to_path_creatable_trailing_slash(); + test_xipfs_rename_enotdir_from_path_empty_dir_to_path_file(); + test_xipfs_rename_enotempty_from_path_empty_dir_to_path_nonempty_dir(); + test_xipfs_rename_enotdir_from_path_empty_dir_to_path_not_dirs(); + test_xipfs_rename_enoent_from_path_empty_dir_to_path_not_found(); + test_xipfs_rename_enametoolong_from_path_empty_dir_to_path_trailing_slash(); + test_xipfs_rename_einval_from_path_empty_dir_to_path_creatable_subdir_itself(); + test_xipfs_rename_enotdir_from_path_nonempty_dir_to_path_file(); + test_xipfs_rename_einval_from_path_nonempty_dir_to_path_empty_dir_subdir_itself(); + test_xipfs_rename_enotempty_from_path_nonempty_dir_to_path_nonempty_dir(); + test_xipfs_rename_enotdir_from_path_nonempty_dir_to_path_not_dirs(); + test_xipfs_rename_enoent_from_path_nonempty_dir_to_path_not_found(); + test_xipfs_rename_enametoolong_from_path_nonempty_dir_to_path_trailing_slash(); + test_xipfs_rename_enotdir_from_path_not_dirs(); + test_xipfs_rename_enoent_from_path_not_found(); + test_xipfs_rename_ok(); + + /* xipfs_stat */ + test_xipfs_stat_efault_path(); + test_xipfs_stat_efault_buf(); + test_xipfs_stat_enoent_path_null_char(); + test_xipfs_stat_enametoolong_path(); + test_xipfs_stat_enotdir_path(); + test_xipfs_stat_enoent_path(); + test_xipfs_stat_ok(); + + /* xipfs_statvfs */ + test_xipfs_statvfs_efault_buf(); + test_xipfs_statvfs_ok(); + + /* xipfs_new_file */ + test_xipfs_new_file_efault_path(); + test_xipfs_new_file_enoent_path_null_char(); + test_xipfs_new_file_eisdir_path_root(); + test_xipfs_new_file_enametoolong_path(); + test_xipfs_new_file_einval_exec(); + test_xipfs_new_file_eexist_path(); + test_xipfs_new_file_eisdir_path(); + test_xipfs_new_file_enotdir_path(); + test_xipfs_new_file_enoent_path(); + test_xipfs_new_file_eisdir_trailing_slash(); + test_xipfs_new_file_edquot(); + test_xipfs_new_file_ok(); + + /* xipfs_execv */ + test_xipfs_execv_efault_path(); + test_xipfs_execv_enoent_path_null_char(); + test_xipfs_execv_eisdir_path_root(); + test_xipfs_execv_enametoolong_path(); + test_xipfs_execv_efault_no_syscalls_table(); + test_xipfs_execv_efault_syscalls_table_with_null(); + test_xipfs_execv_eisdir_path(); + test_xipfs_execv_enotdir_path(); + test_xipfs_execv_enoent_path(); + + /* Should not fail unless there is a corrupted data + * structure or a flash memory failure. */ + ret = xipfs_umount(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); + + printf("Tests finished.\n"); +} + +/* + * Entry point + */ + +int main(void) +{ + test_xipfs_suite(&nvme0p0); + + { + printf("Constructing vfs_xipfs_mount from mtd_flash_aux_slot..."); + + mutex_t mutex; + mutex_t execution_mutex; + vfs_xipfs_mount_t vfs_xipfs_mount; + + int ret = xipfs_construct_from_flashpage( + &mtd_flash_aux_slot, "/dev/nvme0p0", + &execution_mutex, &mutex, + &vfs_xipfs_mount); + XIPFS_ASSERT(ret == 0); + printf("Done.\n"); + + printf("mtd_flash_aux_slot.offset : %lu, mtd_flash_aux_slot.base.sector_count : %lu\n", + mtd_flash_aux_slot.offset, mtd_flash_aux_slot.base.sector_count); + + printf("vfs_xipfs_mount.page_num : %u, vfs_xipfs_mount.page_addr : %p\n", + vfs_xipfs_mount.page_num, vfs_xipfs_mount.page_addr); + + test_xipfs_suite(&vfs_xipfs_mount); + } + + for (;;) {} +} + +/* + * Test function implementations + */ + +static void test_xipfs_close_ebadf_descp(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == -EINVAL); +} + +static void test_xipfs_close_ok(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_fstat_ebadbf_descp(void) +{ + xipfs_file_desc_t desc; + struct stat buf; + int ret; + + /* test */ + ret = xipfs_fstat(xipfs_nvme0p0, &desc, &buf); + XIPFS_ASSERT(ret == -EINVAL); +} + +static void test_xipfs_fstat_efault_buf(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_RDONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_fstat(xipfs_nvme0p0, &desc, NULL); + XIPFS_ASSERT(ret == -EFAULT); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_fstat_ebadbf_xipfs_infos(void) +{ + xipfs_file_desc_t desc; + struct stat buf; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/.xipfs_infos", + O_RDONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_fstat(xipfs_nvme0p0, &desc, &buf); + XIPFS_ASSERT(ret == -EBADF); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_fstat_ok(void) +{ + xipfs_file_desc_t desc; + struct stat buf; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_RDONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_fstat(xipfs_nvme0p0, &desc, &buf); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_lseek_einval_descp(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_lseek(xipfs_nvme0p0, &desc, 0, SEEK_SET); + XIPFS_ASSERT(ret == -EINVAL); +} + +static void test_xipfs_lseek_ebadf_descp(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_lseek(xipfs_nvme0p0, &desc, 0, SEEK_SET); + XIPFS_ASSERT(ret == -EBADF); +} + +static void test_xipfs_lseek_einval_whence(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_lseek(xipfs_nvme0p0, &desc, 0, 0xffffffff); + XIPFS_ASSERT(ret == -EINVAL); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_lseek_einval_new_pos(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_lseek(xipfs_nvme0p0, &desc, 0xffffffff, + SEEK_SET); + XIPFS_ASSERT(ret == -EINVAL); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_lseek_ok(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_lseek(xipfs_nvme0p0, &desc, 0, SEEK_SET); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_open_efault_name(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, NULL, O_CREAT, 0); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_open_einval_flags(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + 0xffffffff, 0); + XIPFS_ASSERT(ret == -EINVAL); +} + +static void test_xipfs_open_enametoolong_name(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/totoooooooooooooooo" + "ooooooooooooooooooooooooooooooooooooooooooooo", + O_CREAT, 0); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_open_eexist_xipfs_infos_flags(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/.xipfs_infos", + O_CREAT | O_EXCL, 0); + XIPFS_ASSERT(ret == -EEXIST); +} + +static void test_xipfs_open_eacces_xipfs_infos_flags(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/.xipfs_infos", + O_WRONLY, 0); + XIPFS_ASSERT(ret == -EACCES); + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/.xipfs_infos", + O_APPEND, 0); + XIPFS_ASSERT(ret == -EACCES); + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/.xipfs_infos", + O_RDWR, 0); + XIPFS_ASSERT(ret == -EACCES); +} + +static void test_xipfs_open_eexist_name(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_EXCL, 0); + XIPFS_ASSERT(ret == -EEXIST); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_open_eisdir_name(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_RDONLY, 0); + XIPFS_ASSERT(ret == -EISDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_open_enotdir_name(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_open_enoent_flags(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_RDONLY, 0); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_open_eisdir_name_trailing_slash(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto/", + O_CREAT | O_RDONLY, 0); + XIPFS_ASSERT(ret == -EISDIR); +} + +static void test_xipfs_open_edquot(void) +{ + char path[XIPFS_PATH_MAX]; + xipfs_file_desc_t desc; + size_t i; + int ret; + + /* initialization */ + for (i = 0; i < xipfs_nvme0p0->page_num; i++) { + (void)sprintf(path, "/%d", i); + + ret = xipfs_open(xipfs_nvme0p0, &desc, path, + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + } + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == -EDQUOT); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_open_ok(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_read_ebadf_descp(void) +{ + xipfs_file_desc_t desc; + char buf; + int ret; + + /* test */ + ret = xipfs_read(xipfs_nvme0p0, &desc, &buf, sizeof(buf)); + XIPFS_ASSERT(ret == -EBADF); +} + +static void test_xipfs_read_efault_dest(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_RDONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_read(xipfs_nvme0p0, &desc, NULL, 0); + XIPFS_ASSERT(ret == -EFAULT); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_read_eacces_flags(void) +{ + xipfs_file_desc_t desc; + char buf; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_WRONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_read(xipfs_nvme0p0, &desc, &buf, sizeof(buf)); + XIPFS_ASSERT(ret == -EACCES); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_read_ok(void) +{ + xipfs_file_desc_t desc; + char buf; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_RDONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_read(xipfs_nvme0p0, &desc, &buf, sizeof(buf)); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_write_ebadf_descp(void) +{ + xipfs_file_desc_t desc; + char buf = 0xff; + int ret; + + /* test */ + ret = xipfs_write(xipfs_nvme0p0, &desc, &buf, sizeof(buf)); + XIPFS_ASSERT(ret == -EBADF); +} + +static void test_xipfs_write_efault_src(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_WRONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_write(xipfs_nvme0p0, &desc, NULL, 0); + XIPFS_ASSERT(ret == -EFAULT); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_write_eacces_flags(void) +{ + xipfs_file_desc_t desc; + char buf = 0xff; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_write(xipfs_nvme0p0, &desc, &buf, sizeof(buf)); + XIPFS_ASSERT(ret == -EACCES); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_write_ok(void) +{ + xipfs_file_desc_t desc; + char buf = 0xff; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_WRONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_write(xipfs_nvme0p0, &desc, &buf, sizeof(buf)); + XIPFS_ASSERT(ret == 1); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_fsync_ebadf_descp(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* test */ + ret = xipfs_fsync(xipfs_nvme0p0, &desc, 0); + XIPFS_ASSERT(ret == -EBADF); +} + +static void test_xipfs_fsync_eacces_flags(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_RDONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_fsync(xipfs_nvme0p0, &desc, 0); + XIPFS_ASSERT(ret == -EACCES); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_fsync_ok(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT | O_WRONLY, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_fsync(xipfs_nvme0p0, &desc, 0); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_opendir_efault_dirname(void) +{ + xipfs_dir_desc_t desc; + int ret; + + /* test */ + ret = xipfs_opendir(xipfs_nvme0p0, &desc, NULL); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_opendir_enoent_dirname_null_char(void) +{ + xipfs_dir_desc_t desc; + int ret; + + /* test */ + ret = xipfs_opendir(xipfs_nvme0p0, &desc, ""); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_opendir_enametoolong_dirname(void) +{ + xipfs_dir_desc_t desc; + int ret; + + /* test */ + ret = xipfs_opendir(xipfs_nvme0p0, &desc, "/totooooo" + "oooooooooooooooooooooooooooooooooooooooooooooooooo" + "ooooo/"); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_opendir_enotdir_dirname(void) +{ + xipfs_file_desc_t desc_file; + xipfs_dir_desc_t desc_dir; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc_file, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc_file); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_opendir(xipfs_nvme0p0, &desc_dir, "/toto"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_opendir_enoent_dirname(void) +{ + xipfs_dir_desc_t desc; + int ret; + + /* test */ + ret = xipfs_opendir(xipfs_nvme0p0, &desc, "/toto/"); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_opendir_ok(void) +{ + xipfs_dir_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_opendir(xipfs_nvme0p0, &desc, "/toto/"); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_closedir(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_readdir_efault_direntp(void) +{ + xipfs_dir_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_opendir(xipfs_nvme0p0, &desc, "/toto/"); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_readdir(xipfs_nvme0p0, &desc, NULL); + XIPFS_ASSERT(ret == -EFAULT); + + /* clean up */ + ret = xipfs_closedir(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_readdir_ebadf_descp(void) +{ + xipfs_dir_desc_t desc; + xipfs_dirent_t dirent; + int ret; + + /* test */ + ret = xipfs_readdir(xipfs_nvme0p0, &desc, &dirent); + XIPFS_ASSERT(ret == -EBADF); +} + +static void test_xipfs_readdir_ok(void) +{ + xipfs_dir_desc_t desc; + xipfs_dirent_t dirent; + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_opendir(xipfs_nvme0p0, &desc, "/toto/"); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_readdir(xipfs_nvme0p0, &desc, &dirent); + XIPFS_ASSERT(ret >= 0); + + /* clean up */ + ret = xipfs_closedir(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_closedir_ebadf_descp(void) +{ + xipfs_dir_desc_t desc; + xipfs_dirent_t dirent; + int ret; + + /* test */ + ret = xipfs_readdir(xipfs_nvme0p0, &desc, &dirent); + XIPFS_ASSERT(ret == -EBADF); +} + +static void test_xipfs_closedir_ok(void) +{ + xipfs_dir_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_opendir(xipfs_nvme0p0, &desc, "/toto/"); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_closedir(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_unlink_efault_name(void) +{ + int ret; + + /* test */ + ret = xipfs_unlink(xipfs_nvme0p0, NULL); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_unlink_enoent_name_null_char(void) +{ + int ret; + + /* test */ + ret = xipfs_unlink(xipfs_nvme0p0, ""); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_unlink_eisdir_name_root(void) +{ + int ret; + + /* test */ + ret = xipfs_unlink(xipfs_nvme0p0, "/"); + XIPFS_ASSERT(ret == -EISDIR); +} + +static void test_xipfs_unlink_enametoolong_name(void) +{ + int ret; + + /* test */ + ret = xipfs_unlink(xipfs_nvme0p0, "/totooooooooooooo" + "oooooooooooooooooooooooooooooooooooooooooooooooo"); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_unlink_eisdir_name(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_unlink(xipfs_nvme0p0, "/toto"); + XIPFS_ASSERT(ret == -EISDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_unlink_enotdir_name(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_unlink(xipfs_nvme0p0, "/toto/toto"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_unlink_enoent_name(void) +{ + int ret; + + /* test */ + ret = xipfs_unlink(xipfs_nvme0p0, "/toto"); + XIPFS_ASSERT(ret == -ENOENT); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_unlink_ok(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_unlink(xipfs_nvme0p0, "/toto"); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_mkdir_efault_name(void) +{ + int ret; + + /* test */ + ret = xipfs_mkdir(xipfs_nvme0p0, NULL, 0); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_mkdir_enoent_name_null_char(void) +{ + int ret; + + /* test */ + ret = xipfs_mkdir(xipfs_nvme0p0, "", 0); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_mkdir_eexist_name_root(void) +{ + int ret; + + /* test */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/", 0); + XIPFS_ASSERT(ret == -EEXIST); +} + +static void test_xipfs_mkdir_enametoolong_name(void) +{ + int ret; + + /* test */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/totoooooooooooooo" + "oooooooooooooooooooooooooooooooooooooooooooooo/", 0); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_mkdir_eexist_name(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == -EEXIST); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_mkdir_enotdir_name(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", + O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/toto/", 0); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_mkdir_enoent_name(void) +{ + int ret; + + /* test */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/toto/", 0); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_mkdir_enametoolong_name_trailing_slash(void) +{ + int ret; + + /* test */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/totoooooooooooooo" + "ooooooooooooooooooooooooooooooooooooooooooooooo", 0); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_mkdir_ok(void) +{ + int ret; + + /* test */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rmdir_efault_name(void) +{ + int ret; + + /* test */ + ret = xipfs_rmdir(xipfs_nvme0p0, NULL); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_rmdir_enoent_name_null_char(void) +{ + int ret; + + /* test */ + ret = xipfs_rmdir(xipfs_nvme0p0, ""); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_rmdir_ebusy_name_root(void) +{ + int ret; + + /* test */ + ret = xipfs_rmdir(xipfs_nvme0p0, "/"); + XIPFS_ASSERT(ret == -EBUSY); +} + +static void test_xipfs_rmdir_enametoolong_name(void) +{ + int ret; + + /* test */ + ret = xipfs_rmdir(xipfs_nvme0p0, "/totoooooooooooooooooooooo" + "oooooooooooooooooooooooooooooooooooooo/"); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_rmdir_einval_name(void) +{ + int ret; + + /* test */ + ret = xipfs_rmdir(xipfs_nvme0p0, "."); + XIPFS_ASSERT(ret == -EINVAL); +} + +static void test_xipfs_rmdir_enotdir_name(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rmdir(xipfs_nvme0p0, "/toto"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rmdir_enotempty_name(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/toto/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rmdir(xipfs_nvme0p0, "/toto/"); + XIPFS_ASSERT(ret == -ENOTEMPTY); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rmdir_enoent_name(void) +{ + int ret; + + /* test */ + ret = xipfs_rmdir(xipfs_nvme0p0, "/toto/toto/"); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_rmdir_ok(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rmdir(xipfs_nvme0p0, "/toto/"); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_efault_from_path(void) +{ + int ret; + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, NULL, "/to/"); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_rename_efault_to_path(void) +{ + int ret; + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", NULL); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_rename_enoent_from_path_null_char(void) +{ + int ret; + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "", "/to/"); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_rename_enoent_to_path_null_char(void) +{ + int ret; + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", ""); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_rename_enametoolong_from_path(void) +{ + int ret; + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/frommmmmmmmmmmmmmmmmmmmm" + "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm/", "/to/"); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_rename_enametoolong_to_path(void) +{ + int ret; + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/tooooooooooooo" + "oooooooooooooooooooooooooooooooooooooooooooooooo/"); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_rename_eisdir_from_path_file_to_path_dirs(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/from", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/to/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from", "/to/"); + XIPFS_ASSERT(ret == -EISDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enotdir_from_path_file_to_path_not_dirs(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/from", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/to", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from", "/to/from"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enoent_from_path_file_to_path_not_found(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/from", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from", "/to/from"); + XIPFS_ASSERT(ret == -ENOENT); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enotdir_from_path_file_to_path_creatable_trailing_slash(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/from", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from", "/to/"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enotdir_from_path_empty_dir_to_path_file(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/to", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enotempty_from_path_empty_dir_to_path_nonempty_dir(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/to/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/to/to/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/"); + XIPFS_ASSERT(ret == -ENOTEMPTY); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enotdir_from_path_empty_dir_to_path_not_dirs(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/to", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/from/"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enoent_from_path_empty_dir_to_path_not_found(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/from/"); + XIPFS_ASSERT(ret == -ENOENT); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enametoolong_from_path_empty_dir_to_path_trailing_slash(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/toooooooooooooooooo" + "oooooooooooooooooooooooooooooooooooooooooooo"); + XIPFS_ASSERT(ret == -ENAMETOOLONG); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_einval_from_path_empty_dir_to_path_creatable_subdir_itself(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/from/from/"); + XIPFS_ASSERT(ret == -EINVAL); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enotdir_from_path_nonempty_dir_to_path_file(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/to", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_einval_from_path_nonempty_dir_to_path_empty_dir_subdir_itself(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/from/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/from/from/from/"); + XIPFS_ASSERT(ret == -EINVAL); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enotempty_from_path_nonempty_dir_to_path_nonempty_dir(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/to/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/to/to/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/"); + XIPFS_ASSERT(ret == -ENOTEMPTY); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enotdir_from_path_nonempty_dir_to_path_not_dirs(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/to", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/from/"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enoent_from_path_nonempty_dir_to_path_not_found(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/from/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/from/"); + XIPFS_ASSERT(ret == -ENOENT); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enametoolong_from_path_nonempty_dir_to_path_trailing_slash(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/from/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/toooooooooooooooooo" + "oooooooooooooooooooooooooooooooooooooooooooo"); + XIPFS_ASSERT(ret == -ENAMETOOLONG); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enotdir_from_path_not_dirs(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/from", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/from", "/to"); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_enoent_from_path_not_found(void) +{ + int ret; + + /* test */ + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/"); + XIPFS_ASSERT(ret == -ENOENT); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_rename_ok(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* file to file */ + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/from", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/to", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_rename(xipfs_nvme0p0, "/from", "/to"); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); + + /* file to creatable */ + + ret = xipfs_open(xipfs_nvme0p0, &desc, "/from", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_rename(xipfs_nvme0p0, "/from", "/to"); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); + + /* empty directory to empty directory */ + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/to/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/"); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); + + /* empty directory to creatable */ + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/"); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); + + /* non empty directory to empty directory */ + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/to/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/"); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); + + /* non empty directory to creatable */ + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_mkdir(xipfs_nvme0p0, "/from/from/", 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_rename(xipfs_nvme0p0, "/from/", "/to/"); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_stat_efault_path(void) +{ + struct stat buf; + int ret; + + /* test */ + ret = xipfs_stat(xipfs_nvme0p0, NULL, &buf); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_stat_efault_buf(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_stat(xipfs_nvme0p0, "/toto", NULL); + XIPFS_ASSERT(ret == -EFAULT); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_stat_enoent_path_null_char(void) +{ + struct stat buf; + int ret; + + /* test */ + ret = xipfs_stat(xipfs_nvme0p0, "", &buf); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_stat_enametoolong_path(void) +{ + struct stat buf; + int ret; + + /* test */ + ret = xipfs_stat(xipfs_nvme0p0, "/totooooooooooooooooooooooo" + "ooooooooooooooooooooooooooooooooooooo", &buf); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_stat_enotdir_path(void) +{ + xipfs_file_desc_t desc; + struct stat buf; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_stat(xipfs_nvme0p0, "/toto/toto", &buf); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_stat_enoent_path(void) +{ + struct stat buf; + int ret; + + /* test */ + ret = xipfs_stat(xipfs_nvme0p0, "/toto", &buf); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_stat_ok(void) +{ + xipfs_file_desc_t desc; + struct stat buf; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_stat(xipfs_nvme0p0, "/toto", &buf); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_statvfs_efault_buf(void) +{ + int ret; + + /* test */ + ret = xipfs_statvfs(xipfs_nvme0p0, NULL, NULL); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_statvfs_ok(void) +{ + struct xipfs_statvfs buf; + int ret; + + /* test */ + ret = xipfs_statvfs(xipfs_nvme0p0, NULL, &buf); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_new_file_efault_path(void) +{ + int ret; + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, NULL, + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_new_file_enoent_path_null_char(void) +{ + int ret; + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "", + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_new_file_eisdir_path_root(void) +{ + int ret; + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/", + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == -EISDIR); +} + +static void test_xipfs_new_file_enametoolong_path(void) +{ + int ret; + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/totooooooooooooooooooo" + "ooooooooooooooooooooooooooooooooooooooooo", + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_new_file_einval_exec(void) +{ + int ret; + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/toto", + XIPFS_NVM_PAGE_SIZE, 0xffffffff); + XIPFS_ASSERT(ret == -EINVAL); +} + +static void test_xipfs_new_file_eexist_path(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/toto", + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == -EEXIST); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_new_file_eisdir_path(void) +{ + int ret; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/toto", + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == -EISDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_new_file_enotdir_path(void) +{ + xipfs_file_desc_t desc; + int ret; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/toto/toto", + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_new_file_enoent_path(void) +{ + int ret; + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/toto/toto", + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_new_file_eisdir_trailing_slash(void) +{ + int ret; + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/toto/", + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == -EISDIR); +} + +static void test_xipfs_new_file_edquot(void) +{ + char path[XIPFS_PATH_MAX]; + size_t i; + int ret; + + /* initialization */ + for (i = 0; i < xipfs_nvme0p0->page_num; i++) { + (void)sprintf(path, "/%d", i); + ret = xipfs_new_file(xipfs_nvme0p0, path, + 0, 0); + XIPFS_ASSERT(ret == 0); + } + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/toto", + 0, 0); + XIPFS_ASSERT(ret == -EDQUOT); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_new_file_ok(void) +{ + int ret; + + /* test */ + ret = xipfs_new_file(xipfs_nvme0p0, "/toto", + XIPFS_NVM_PAGE_SIZE, 0); + XIPFS_ASSERT(ret == 0); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_execv_efault_path(void) +{ + int ret; + + /* test */ + ret = xipfs_execv(xipfs_nvme0p0, NULL, NULL, NULL); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_execv_enoent_path_null_char(void) +{ + int ret; + + /* test */ + ret = xipfs_execv(xipfs_nvme0p0, "", NULL, NULL); + XIPFS_ASSERT(ret == -ENOENT); +} + +static void test_xipfs_execv_eisdir_path_root(void) +{ + int ret; + + /* test */ + ret = xipfs_execv(xipfs_nvme0p0, "/", NULL, NULL); + XIPFS_ASSERT(ret == -EISDIR); +} + +static void test_xipfs_execv_enametoolong_path(void) +{ + int ret; + + /* test */ + ret = xipfs_execv(xipfs_nvme0p0, "/totoooooooooooooooooooooo" + "oooooooooooooooooooooooooooooooooooooo", NULL, NULL); + XIPFS_ASSERT(ret == -ENAMETOOLONG); +} + +static void test_xipfs_execv_efault_no_syscalls_table(void) { + int ret; + + char *argv[2] = { + "/toto/", NULL + }; + + /* test */ + ret = xipfs_execv(xipfs_nvme0p0, "/toto/", argv, NULL); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void test_xipfs_execv_efault_syscalls_table_with_null(void) { + int ret; + + char *argv[2] = { + "/toto/", NULL + }; + const void *user_syscalls[XIPFS_USER_SYSCALL_MAX] = { + NULL + }; + + /* test */ + ret = xipfs_execv(xipfs_nvme0p0, "/toto/", argv, user_syscalls); + XIPFS_ASSERT(ret == -EFAULT); +} + +static void fake_syscall(void) {} +static const void *fake_user_syscalls[XIPFS_USER_SYSCALL_MAX] = { + [ XIPFS_USER_SYSCALL_PRINTF] = fake_syscall, + [ XIPFS_USER_SYSCALL_GET_TEMP] = fake_syscall, + [ XIPFS_USER_SYSCALL_ISPRINT] = fake_syscall, + [ XIPFS_USER_SYSCALL_STRTOL] = fake_syscall, + [ XIPFS_USER_SYSCALL_GET_LED] = fake_syscall, + [ XIPFS_USER_SYSCALL_SET_LED] = fake_syscall, + [ XIPFS_USER_SYSCALL_COPY_FILE] = fake_syscall, + [XIPFS_USER_SYSCALL_GET_FILE_SIZE] = fake_syscall, + [ XIPFS_USER_SYSCALL_MEMSET] = fake_syscall +}; + +static void test_xipfs_execv_eisdir_path(void) +{ + int ret; + + char *argv[2] = { + "/toto/", NULL + }; + + /* initialization */ + ret = xipfs_mkdir(xipfs_nvme0p0, "/toto/", 0); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_execv(xipfs_nvme0p0, "/toto/", argv, fake_user_syscalls); + XIPFS_ASSERT(ret == -EISDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_execv_enotdir_path(void) +{ + xipfs_file_desc_t desc; + int ret; + char *argv[2] = { + "/toto/toto", NULL + }; + + /* initialization */ + ret = xipfs_open(xipfs_nvme0p0, &desc, "/toto", O_CREAT, 0); + XIPFS_ASSERT(ret == 0); + + ret = xipfs_close(xipfs_nvme0p0, &desc); + XIPFS_ASSERT(ret == 0); + + /* test */ + ret = xipfs_execv(xipfs_nvme0p0, "/toto/toto", argv, fake_user_syscalls); + XIPFS_ASSERT(ret == -ENOTDIR); + + /* clean up */ + ret = xipfs_format(xipfs_nvme0p0); + XIPFS_ASSERT(ret == 0); +} + +static void test_xipfs_execv_enoent_path(void) +{ + int ret; + char *argv[2] = { + "/toto", NULL + }; + + /* test */ + ret = xipfs_execv(xipfs_nvme0p0, "/toto", argv, fake_user_syscalls); + XIPFS_ASSERT(ret == -ENOENT); +}