diff --git a/include/fs/fs.h b/include/fs/fs.h index 1ad785baa96..ffa30576a09 100644 --- a/include/fs/fs.h +++ b/include/fs/fs.h @@ -113,7 +113,7 @@ struct fs_statvfs { /** * @brief File System interface structure * - * @param open Opens an existing file or create a new one + * @param open Opens or creates a file, depending on flags given * @param read Reads items of data of size bytes long * @param write Writes items of data of size bytes long * @param lseek Moves the file position to a new location in the file @@ -134,7 +134,8 @@ struct fs_statvfs { */ struct fs_file_system_t { /* File operations */ - int (*open)(struct fs_file_t *filp, const char *fs_path); + int (*open)(struct fs_file_t *filp, const char *fs_path, + fs_mode_t flags); ssize_t (*read)(struct fs_file_t *filp, void *dest, size_t nbytes); ssize_t (*write)(struct fs_file_t *filp, const void *src, size_t nbytes); @@ -160,6 +161,17 @@ struct fs_file_system_t { struct fs_statvfs *stat); }; +#define FS_O_READ 0x01 +#define FS_O_WRITE 0x02 +#define FS_O_RDWR (FS_O_READ | FS_O_WRITE) +#define FS_O_MODE_MASK 0x03 + +#define FS_O_CREATE 0x10 +#define FS_O_APPEND 0x20 +#define FS_O_FLAGS_MASK 0x30 + +#define FS_O_MASK (FS_O_MODE_MASK | FS_O_FLAGS_MASK) + #ifndef FS_SEEK_SET #define FS_SEEK_SET 0 /* Seek from beginning of file. */ #endif @@ -170,20 +182,29 @@ struct fs_file_system_t { #define FS_SEEK_END 2 /* Seek from end of file. */ #endif - /** * @brief File open * - * Opens an existing file or create a new one and associates - * a stream with it. + * Opens or creates, if does not exist, file depending on flags provided + * and associates a stream with it. * * @param zfp Pointer to file object * @param file_name The name of file to open + * @param flags The mode flags + * + * @p flags can be empty, or combination of one or more of following flags: + * FS_O_READ open for read + * FS_O_WRITE open for write + * FS_O_RDWR open for read/write (FS_O_READ | FS_O_WRITE) + * FS_O_CREATE create file if it does not exist + * FS_O_APPEND move to end of file before each write * * @retval 0 Success - * @retval -ERRNO errno code if error + * @retval -EINVAL when bad file name is given + * @retval -NOENT when file path is not possible (bad mount point) + * @retval other negative error code, depending on file system back-end. */ -int fs_open(struct fs_file_t *zfp, const char *file_name); +int fs_open(struct fs_file_t *zfp, const char *file_name, fs_mode_t flags); /** * @brief File close diff --git a/include/fs/fs_interface.h b/include/fs/fs_interface.h index 283e1cbcd41..2f20b58179d 100644 --- a/include/fs/fs_interface.h +++ b/include/fs/fs_interface.h @@ -7,6 +7,8 @@ #ifndef ZEPHYR_INCLUDE_FS_FS_INTERFACE_H_ #define ZEPHYR_INCLUDE_FS_FS_INTERFACE_H_ +#include + #ifdef __cplusplus extern "C" { #endif @@ -24,6 +26,9 @@ extern "C" { #define MAX_FILE_NAME 12 #endif /* filesystem selection */ +/* Type for fs_open flags */ +typedef uint8_t fs_mode_t; + struct fs_mount_t; /** @@ -35,6 +40,7 @@ struct fs_mount_t; struct fs_file_t { void *filep; const struct fs_mount_t *mp; + fs_mode_t flags; }; /** diff --git a/samples/subsys/fs/littlefs/src/main.c b/samples/subsys/fs/littlefs/src/main.c index 819474dbb43..1b2856ab1f5 100644 --- a/samples/subsys/fs/littlefs/src/main.c +++ b/samples/subsys/fs/littlefs/src/main.c @@ -87,7 +87,7 @@ void main(void) struct fs_file_t file; - rc = fs_open(&file, fname); + rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR); if (rc < 0) { printk("FAIL: open %s: %d\n", fname, rc); goto out; diff --git a/subsys/fs/fat_fs.c b/subsys/fs/fat_fs.c index 67be8ee04c4..6e3ee49adb5 100644 --- a/subsys/fs/fat_fs.c +++ b/subsys/fs/fat_fs.c @@ -63,7 +63,25 @@ static int translate_error(int error) return -EIO; } -static int fatfs_open(struct fs_file_t *zfp, const char *file_name) +static uint8_t translate_flags(fs_mode_t flags) +{ + uint8_t fat_mode = 0; + + fat_mode |= (flags & FS_O_READ) ? FA_READ : 0; + fat_mode |= (flags & FS_O_WRITE) ? FA_WRITE : 0; + fat_mode |= (flags & FS_O_CREATE) ? FA_OPEN_ALWAYS : 0; + /* NOTE: FA_APPEND is not translated because FAT FS does not + * support append semantics of the Zephyr, where file position + * is forwarded to the end before each write, the fatfs_write + * will be tasked with setting a file position to the end, + * if FA_APPEND flag is present. + */ + + return fat_mode; +} + +static int fatfs_open(struct fs_file_t *zfp, const char *file_name, + fs_mode_t mode) { FRESULT res; uint8_t fs_mode; @@ -76,7 +94,7 @@ static int fatfs_open(struct fs_file_t *zfp, const char *file_name) return -ENOMEM; } - fs_mode = FA_READ | FA_WRITE | FA_OPEN_ALWAYS; + fs_mode = translate_flags(mode); res = f_open(zfp->filep, &file_name[1], fs_mode); @@ -143,10 +161,23 @@ static ssize_t fatfs_read(struct fs_file_t *zfp, void *ptr, size_t size) static ssize_t fatfs_write(struct fs_file_t *zfp, const void *ptr, size_t size) { - FRESULT res; + FRESULT res = FR_OK; unsigned int bw; + off_t pos = f_size((FIL *)zfp->filep); + + /* FA_APPEND flag means that file has been opened for append. + * The FAT FS write does not support the POSIX append semantics, + * to always write at the end of file, so set file position + * at the end before each write if FA_APPEND is set. + */ + if (zfp->flags & FS_O_APPEND) { + res = f_lseek(zfp->filep, pos); + } + + if (res == FR_OK) { + res = f_write(zfp->filep, ptr, size, &bw); + } - res = f_write(zfp->filep, ptr, size, &bw); if (res != FR_OK) { return translate_error(res); } diff --git a/subsys/fs/fs.c b/subsys/fs/fs.c index 6507576e88e..2ca1cb514b3 100644 --- a/subsys/fs/fs.c +++ b/subsys/fs/fs.c @@ -10,6 +10,7 @@ #include #include #include +#include #define LOG_LEVEL CONFIG_FS_LOG_LEVEL @@ -75,11 +76,14 @@ static int fs_get_mnt_point(struct fs_mount_t **mnt_pntp, } /* File operations */ -int fs_open(struct fs_file_t *zfp, const char *file_name) +int fs_open(struct fs_file_t *zfp, const char *file_name, fs_mode_t flags) { struct fs_mount_t *mp; int rc = -EINVAL; + /* COpy flags to zfp for use with other fs_ API calls */ + zfp->flags = flags; + if ((file_name == NULL) || (strlen(file_name) <= 1) || (file_name[0] != '/')) { LOG_ERR("invalid file name!!"); @@ -95,7 +99,7 @@ int fs_open(struct fs_file_t *zfp, const char *file_name) zfp->mp = mp; if (zfp->mp->fs->open != NULL) { - rc = zfp->mp->fs->open(zfp, file_name); + rc = zfp->mp->fs->open(zfp, file_name, flags); if (rc < 0) { LOG_ERR("file open error (%d)", rc); return rc; diff --git a/subsys/fs/littlefs_fs.c b/subsys/fs/littlefs_fs.c index f42aa5954d0..259b176aa92 100644 --- a/subsys/fs/littlefs_fs.c +++ b/subsys/fs/littlefs_fs.c @@ -181,14 +181,29 @@ static void release_file_data(struct fs_file_t *fp) fp->filep = NULL; } -static int littlefs_open(struct fs_file_t *fp, const char *path) +static int lfs_flags_from_zephyr(unsigned int zflags) +{ + int flags = (zflags & FS_O_CREATE) ? LFS_O_CREAT : 0; + + /* LFS_O_READONLY and LFS_O_WRONLY can be selected at the same time, + * this is not a mistake, together they create RDWR access. + */ + flags |= (zflags & FS_O_READ) ? LFS_O_RDONLY : 0; + flags |= (zflags & FS_O_WRITE) ? LFS_O_WRONLY : 0; + + flags |= (zflags & FS_O_APPEND) ? LFS_O_APPEND : 0; + + return flags; +} + +static int littlefs_open(struct fs_file_t *fp, const char *path, + fs_mode_t zflags) { struct fs_littlefs *fs = fp->mp->fs_data; struct lfs *lfs = &fs->lfs; - int flags = LFS_O_CREAT | LFS_O_RDWR; - int ret; + int flags = lfs_flags_from_zephyr(zflags); + int ret = k_mem_slab_alloc(&file_data_pool, &fp->filep, K_NO_WAIT); - ret = k_mem_slab_alloc(&file_data_pool, &fp->filep, K_NO_WAIT); if (ret != 0) { return ret; } diff --git a/subsys/fs/shell.c b/subsys/fs/shell.c index 19ea26a7e0b..dbbc7d01af1 100644 --- a/subsys/fs/shell.c +++ b/subsys/fs/shell.c @@ -188,7 +188,7 @@ static int cmd_trunc(const struct shell *shell, size_t argc, char **argv) length = 0; } - err = fs_open(&file, path); + err = fs_open(&file, path, FS_O_CREATE | FS_O_RDWR); if (err) { shell_error(shell, "Failed to open %s (%d)", path, err); return -ENOEXEC;; @@ -277,7 +277,7 @@ static int cmd_read(const struct shell *shell, size_t argc, char **argv) shell_print(shell, "File size: %zd", dirent.size); - err = fs_open(&file, path); + err = fs_open(&file, path, FS_O_CREATE | FS_O_RDWR); if (err) { shell_error(shell, "Failed to open %s (%d)", path, err); return -ENOEXEC; @@ -376,7 +376,7 @@ static int cmd_write(const struct shell *shell, size_t argc, char **argv) arg_offset = 2; } - err = fs_open(&file, path); + err = fs_open(&file, path, FS_O_CREATE | FS_O_RDWR); if (err) { shell_error(shell, "Failed to open %s (%d)", path, err); return -ENOEXEC; diff --git a/subsys/settings/src/settings_file.c b/subsys/settings/src/settings_file.c index 29b68f95762..89ad2608f8f 100644 --- a/subsys/settings/src/settings_file.c +++ b/subsys/settings/src/settings_file.c @@ -123,7 +123,7 @@ static int settings_file_load_priv(struct settings_store *cs, line_load_cb cb, lines = 0; - rc = fs_open(&file, cf->cf_name); + rc = fs_open(&file, cf->cf_name, FS_O_CREATE | FS_O_RDWR); if (rc != 0) { return -EINVAL; } @@ -207,7 +207,7 @@ static int settings_file_create_or_replace(struct fs_file_t *zfp, } } - return fs_open(zfp, file_name); + return fs_open(zfp, file_name, FS_O_CREATE | FS_O_RDWR); } /* @@ -240,7 +240,7 @@ static int settings_file_save_and_compress(struct settings_file *cf, size_t new_name_len; size_t val1_off; - if (fs_open(&rf, cf->cf_name) != 0) { + if (fs_open(&rf, cf->cf_name, FS_O_CREATE | FS_O_RDWR) != 0) { return -ENOEXEC; } @@ -380,7 +380,7 @@ static int settings_file_save_priv(struct settings_store *cs, const char *name, /* * Open the file to add this one value. */ - rc = fs_open(&file, cf->cf_name); + rc = fs_open(&file, cf->cf_name, FS_O_CREATE | FS_O_RDWR); if (rc == 0) { rc = fs_seek(&file, 0, FS_SEEK_END); if (rc == 0) { diff --git a/tests/subsys/fs/fat_fs_api/src/test_fat_dir.c b/tests/subsys/fs/fat_fs_api/src/test_fat_dir.c index 8f799b09901..eba15f4260e 100644 --- a/tests/subsys/fs/fat_fs_api/src/test_fat_dir.c +++ b/tests/subsys/fs/fat_fs_api/src/test_fat_dir.c @@ -35,7 +35,7 @@ static int test_mkdir(void) return res; } - res = fs_open(&filep, TEST_DIR_FILE); + res = fs_open(&filep, TEST_DIR_FILE, FS_O_CREATE | FS_O_RDWR); if (res) { TC_PRINT("Failed opening file [%d]\n", res); return res; diff --git a/tests/subsys/fs/fat_fs_api/src/test_fat_file.c b/tests/subsys/fs/fat_fs_api/src/test_fat_file.c index e87c171cfbe..5bc5a860f0e 100644 --- a/tests/subsys/fs/fat_fs_api/src/test_fat_file.c +++ b/tests/subsys/fs/fat_fs_api/src/test_fat_file.c @@ -21,7 +21,7 @@ static int test_file_open(void) } /* Verify fs_open() */ - res = fs_open(&filep, TEST_FILE); + res = fs_open(&filep, TEST_FILE, FS_O_CREATE | FS_O_RDWR); if (res) { TC_PRINT("Failed opening file [%d]\n", res); return res; diff --git a/tests/subsys/fs/fat_fs_api/src/test_fat_rename.c b/tests/subsys/fs/fat_fs_api/src/test_fat_rename.c index d8615d0e6ac..34cafea64a8 100644 --- a/tests/subsys/fs/fat_fs_api/src/test_fat_rename.c +++ b/tests/subsys/fs/fat_fs_api/src/test_fat_rename.c @@ -26,7 +26,7 @@ static int create_file(const char *path) struct fs_file_t fp; int res = 0; if (!check_file_dir_exists(path)) { - res = fs_open(&fp, path); + res = fs_open(&fp, path, FS_O_CREATE | FS_O_RDWR); if (!res) { res = fs_close(&fp); } else { diff --git a/tests/subsys/fs/fat_fs_dual_drive/src/test_fat_dir.c b/tests/subsys/fs/fat_fs_dual_drive/src/test_fat_dir.c index cc9ecbd48b2..19a06777ca8 100644 --- a/tests/subsys/fs/fat_fs_dual_drive/src/test_fat_dir.c +++ b/tests/subsys/fs/fat_fs_dual_drive/src/test_fat_dir.c @@ -40,7 +40,7 @@ static int test_mkdir(const char *dir, const char *file) return res; } - res = fs_open(&filep, file); + res = fs_open(&filep, file, FS_O_CREATE | FS_O_RDWR); if (res) { TC_PRINT("Failed opening file [%d]\n", res); return res; diff --git a/tests/subsys/fs/fat_fs_dual_drive/src/test_fat_file.c b/tests/subsys/fs/fat_fs_dual_drive/src/test_fat_file.c index a05a294e2fe..734bcd5fc60 100644 --- a/tests/subsys/fs/fat_fs_dual_drive/src/test_fat_file.c +++ b/tests/subsys/fs/fat_fs_dual_drive/src/test_fat_file.c @@ -26,7 +26,7 @@ static int test_file_open(const char *path) } /* Verify fs_open() */ - res = fs_open(&filep, path); + res = fs_open(&filep, path, FS_O_CREATE | FS_O_RDWR); if (res) { TC_PRINT("Failed opening file [%d]\n", res); return res; diff --git a/tests/subsys/fs/littlefs/src/test_lfs_basic.c b/tests/subsys/fs/littlefs/src/test_lfs_basic.c index cfa3d1fd310..5cfeeeb1e5c 100644 --- a/tests/subsys/fs/littlefs/src/test_lfs_basic.c +++ b/tests/subsys/fs/littlefs/src/test_lfs_basic.c @@ -82,7 +82,8 @@ static int create_write_hello(const struct fs_mount_t *mp) zassert_equal(fs_open(&file, testfs_path_init(&path, mp, HELLO, - TESTFS_PATH_END)), + TESTFS_PATH_END), + FS_O_CREATE | FS_O_RDWR), 0, "open hello failed"); @@ -148,7 +149,8 @@ static int verify_hello(const struct fs_mount_t *mp) zassert_equal(fs_open(&file, testfs_path_init(&path, mp, HELLO, - TESTFS_PATH_END)), + TESTFS_PATH_END), + FS_O_CREATE | FS_O_RDWR), 0, "verify hello open failed"); @@ -178,7 +180,8 @@ static int seek_within_hello(const struct fs_mount_t *mp) zassert_equal(fs_open(&file, testfs_path_init(&path, mp, HELLO, - TESTFS_PATH_END)), + TESTFS_PATH_END), + FS_O_CREATE | FS_O_RDWR), 0, "verify hello open failed"); @@ -244,7 +247,8 @@ static int truncate_hello(const struct fs_mount_t *mp) zassert_equal(fs_open(&file, testfs_path_init(&path, mp, HELLO, - TESTFS_PATH_END)), + TESTFS_PATH_END), + FS_O_CREATE | FS_O_RDWR), 0, "verify hello open failed"); @@ -333,7 +337,8 @@ static int sync_goodbye(const struct fs_mount_t *mp) zassert_equal(fs_open(&file, testfs_path_init(&path, mp, GOODBYE, - TESTFS_PATH_END)), + TESTFS_PATH_END), + FS_O_CREATE | FS_O_RDWR), 0, "sync goodbye failed"); @@ -395,7 +400,8 @@ static int verify_goodbye(const struct fs_mount_t *mp) zassert_equal(fs_open(&file, testfs_path_init(&path, mp, GOODBYE, - TESTFS_PATH_END)), + TESTFS_PATH_END), + FS_O_CREATE | FS_O_RDWR), 0, "verify goodbye failed"); diff --git a/tests/subsys/fs/littlefs/src/test_lfs_dirops.c b/tests/subsys/fs/littlefs/src/test_lfs_dirops.c index 67a5903bac2..aaaefa58e0d 100644 --- a/tests/subsys/fs/littlefs/src/test_lfs_dirops.c +++ b/tests/subsys/fs/littlefs/src/test_lfs_dirops.c @@ -66,7 +66,8 @@ static int check_mkdir(struct fs_mount_t *mp) testfs_path_extend(testfs_path_copy(&fpath, &dpath), "file", - TESTFS_PATH_END)), + TESTFS_PATH_END), + FS_O_CREATE | FS_O_RDWR), 0, "creat in dir failed"); zassert_equal(fs_close(&file), 0, diff --git a/tests/subsys/fs/littlefs/src/test_lfs_perf.c b/tests/subsys/fs/littlefs/src/test_lfs_perf.c index 43b73dce5e3..456159a298f 100644 --- a/tests/subsys/fs/littlefs/src/test_lfs_perf.c +++ b/tests/subsys/fs/littlefs/src/test_lfs_perf.c @@ -77,7 +77,7 @@ static int write_read(const char *tag, TC_PRINT("creating and writing %zu %zu-byte blocks\n", nbuf, buf_size); - rc = fs_open(&file, path.path); + rc = fs_open(&file, path.path, FS_O_CREATE | FS_O_RDWR); if (rc != 0) { TC_PRINT("Failed to open %s for write: %d\n", path.path, rc); goto out_buf; @@ -116,7 +116,7 @@ static int write_read(const char *tag, (uint32_t)(total * 1000U / (t1 - t0)), (uint32_t)(total * 1000U / (t1 - t0) / 1024U)); - rc = fs_open(&file, path.path); + rc = fs_open(&file, path.path, FS_O_CREATE | FS_O_RDWR); if (rc != 0) { TC_PRINT("Failed to open %s for write: %d\n", path.path, rc); goto out_buf; diff --git a/tests/subsys/fs/littlefs/src/testfs_util.c b/tests/subsys/fs/littlefs/src/testfs_util.c index 2079ae6daec..d2b5233c3cf 100644 --- a/tests/subsys/fs/littlefs/src/testfs_util.c +++ b/tests/subsys/fs/littlefs/src/testfs_util.c @@ -235,7 +235,8 @@ int testfs_build(struct testfs_path *root, rc = fs_open(&file, testfs_path_extend(root, cp->name, - TESTFS_PATH_END)); + TESTFS_PATH_END), + FS_O_CREATE | FS_O_RDWR); TC_PRINT("create at %s with %u from 0x%02x: %d\n", root->path, cp->size, cp->value, rc); if (rc == 0) { @@ -295,7 +296,7 @@ static int check_layout_entry(struct testfs_path *pp, if (statp->type == FS_DIR_ENTRY_FILE) { struct fs_file_t file; - rc = fs_open(&file, pp->path); + rc = fs_open(&file, pp->path, FS_O_CREATE | FS_O_RDWR); if (rc < 0) { TC_PRINT("%s: content check open failed: %d\n", pp->path, rc); diff --git a/tests/subsys/fs/multi-fs/src/test_common_dir.c b/tests/subsys/fs/multi-fs/src/test_common_dir.c index b8fd5ef01ba..d36a5794b90 100644 --- a/tests/subsys/fs/multi-fs/src/test_common_dir.c +++ b/tests/subsys/fs/multi-fs/src/test_common_dir.c @@ -39,7 +39,7 @@ int test_mkdir(const char *dir_path, const char *file) return res; } - res = fs_open(&filep, file_path); + res = fs_open(&filep, file_path, FS_O_CREATE | FS_O_RDWR); if (res) { TC_PRINT("Failed opening file [%d]\n", res); return res; diff --git a/tests/subsys/fs/multi-fs/src/test_common_file.c b/tests/subsys/fs/multi-fs/src/test_common_file.c index a81aa3dc8f3..9cd696e46cd 100644 --- a/tests/subsys/fs/multi-fs/src/test_common_file.c +++ b/tests/subsys/fs/multi-fs/src/test_common_file.c @@ -22,7 +22,7 @@ int test_file_open(struct fs_file_t *filep, const char *file_path) } /* Verify fs_open() */ - res = fs_open(filep, file_path); + res = fs_open(filep, file_path, FS_O_CREATE | FS_O_RDWR); if (res) { TC_PRINT("Failed opening file [%d]\n", res); return res; diff --git a/tests/subsys/settings/fs/src/settings_test_fs.c b/tests/subsys/settings/fs/src/settings_test_fs.c index d6ee8f00f6a..a095fdabfee 100644 --- a/tests/subsys/settings/fs/src/settings_test_fs.c +++ b/tests/subsys/settings/fs/src/settings_test_fs.c @@ -151,7 +151,7 @@ int fsutil_read_file(const char *path, off_t offset, size_t len, void *dst, int rc; ssize_t r_len = 0; - rc = fs_open(&file, path); + rc = fs_open(&file, path, FS_O_CREATE | FS_O_RDWR); if (rc != 0) { return rc; } @@ -172,7 +172,7 @@ int fsutil_write_file(const char *path, const void *data, size_t len) struct fs_file_t file; int rc; - rc = fs_open(&file, path); + rc = fs_open(&file, path, FS_O_CREATE | FS_O_RDWR); if (rc != 0) { return rc; }