From aeed1373724e862acec9fda972d352c9241ee901 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 13 Apr 2025 10:54:59 +0200 Subject: [PATCH 1/7] fs: exfat: Flush node before put in read() callback Make sure the node is never dirty before being released, flush the node first using exfat_flush_node() and only then release the node using exfat_put_node(). This now matches the behavior of exfat_fs_write() too. Fixes: b86a651b646c ("fs: exfat: Add U-Boot porting layer") Signed-off-by: Marek Vasut --- fs/exfat/io.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/exfat/io.c b/fs/exfat/io.c index 81e82829c72..12498a4a6ec 100644 --- a/fs/exfat/io.c +++ b/fs/exfat/io.c @@ -898,9 +898,7 @@ int exfat_fs_read(const char *filename, void *buf, loff_t offset, loff_t len, *actread = sz; - exfat_put_node(&ctxt.ef, node); - - return exfat_flush_node(&ctxt.ef, node); + err = exfat_flush_node(&ctxt.ef, node); exit: exfat_put_node(&ctxt.ef, node); return err; From 21b04b3d72cfea587a8b616410371a191768c5e7 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 13 Apr 2025 10:55:00 +0200 Subject: [PATCH 2/7] fs: exfat: Inhibit "impossible" print on write to bogus file Write into a bogus file, like '/.', triggers an "impossible" print from the exfat core code. That should not be printed in U-Boot, because U-Boot prints its own error message sooner. Inhibit this error message. The following command triggers the bogus print: " => save host 0:0 1000008 /. 0x10 " Fixes: b86a651b646c ("fs: exfat: Add U-Boot porting layer") Signed-off-by: Marek Vasut --- fs/exfat/lookup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/exfat/lookup.c b/fs/exfat/lookup.c index 9867aab95f3..1d9aae9e036 100644 --- a/fs/exfat/lookup.c +++ b/fs/exfat/lookup.c @@ -218,8 +218,9 @@ int exfat_split(struct exfat* ef, struct exfat_node** parent, exfat_put_node(ef, *parent); *parent = *node; } +#ifndef __UBOOT__ exfat_bug("impossible"); -#ifdef __UBOOT__ +#else return 0; #endif } From 01a8121b7daa68767fe88fa92d436b95a1662bed Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 13 Apr 2025 10:55:01 +0200 Subject: [PATCH 3/7] fs: exfat: Rework exfat_fs_readdir() to behave like exfat_fs_ls() The exfat_fs_readdir() depends on state created in exfat_fs_opendir(), but that state may be disrupted by fs_close() called by the FS layer in fs_opendir(), because exfat porting layer unmounts the filesystem in ->close() callback. To avoid this disruption, avoid creating state in exfat_fs_opendir(), cache only the directory name to list there, and rework exfat_fs_readdir() to work in a similar way to exfat_fs_ls(). That is, make exfat_fs_readdir() open the directory, look up specific entry, extract its properties to be reported to FS layer, and close the directory. This is slow, but avoids the disruption. The slowness does not affect regular 'ls' command, which uses exfat_fs_ls() fast path. Fixes: b86a651b646c ("fs: exfat: Add U-Boot porting layer") Signed-off-by: Marek Vasut --- fs/exfat/io.c | 115 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 46 deletions(-) diff --git a/fs/exfat/io.c b/fs/exfat/io.c index 12498a4a6ec..004ba5d3e45 100644 --- a/fs/exfat/io.c +++ b/fs/exfat/io.c @@ -597,15 +597,13 @@ ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, } #ifdef __UBOOT__ +#define PATH_MAX FS_DIRENT_NAME_LEN + struct exfat_dir_stream { + char dirname[PATH_MAX]; struct fs_dir_stream fs_dirs; struct fs_dirent dirent; - - struct exfat_node* node; - struct exfat_iterator it; - /* State tracker flags for emulated . and .. dirents */ - bool dot; - bool dotdot; + int offset; }; int exfat_fs_probe(struct blk_desc *fs_dev_desc, @@ -626,8 +624,6 @@ error: return ret; } -#define PATH_MAX FS_DIRENT_NAME_LEN - /* Adapted from uclibc 1.0.35 */ static char *exfat_realpath(const char *path, char got_path[]) { @@ -721,31 +717,31 @@ int exfat_lookup_realpath(struct exfat* ef, struct exfat_node** node, int exfat_fs_opendir(const char *filename, struct fs_dir_stream **dirsp) { struct exfat_dir_stream *dirs; + struct exfat_node *dnode; int err; + err = exfat_lookup_realpath(&ctxt.ef, &dnode, filename); + if (err) + return err; + + if (!(dnode->attrib & EXFAT_ATTRIB_DIR)) + err = -ENOTDIR; + + exfat_put_node(&ctxt.ef, dnode); + + if (err) + return err; + dirs = calloc(1, sizeof(*dirs)); if (!dirs) return -ENOMEM; - err = exfat_lookup_realpath(&ctxt.ef, &dirs->node, filename); - if (err) - goto err_out; - - if (!(dirs->node->attrib & EXFAT_ATTRIB_DIR)) { - err = -ENOTDIR; - goto err_out; - } - - err = exfat_opendir(&ctxt.ef, dirs->node, &dirs->it); - if (err) - goto err_out; + strcpy(dirs->dirname, filename); + dirs->offset = -1; *dirsp = &dirs->fs_dirs; return 0; -err_out: - free(dirs); - return err; } int exfat_fs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp) @@ -753,50 +749,77 @@ int exfat_fs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp) struct exfat_dir_stream *dirs = container_of(fs_dirs, struct exfat_dir_stream, fs_dirs); struct fs_dirent *dent = &dirs->dirent; - struct exfat_node* node; + struct exfat_node *dnode, *node; + struct exfat_iterator it; + int offset = 0; + int err; + + err = exfat_lookup_realpath(&ctxt.ef, &dnode, dirs->dirname); + if (err) + return err; + + if (!(dnode->attrib & EXFAT_ATTRIB_DIR)) { + err = -ENOTDIR; + goto err_out; + } /* Emulate current directory ./ */ - if (!dirs->dot) { - dirs->dot = true; + if (dirs->offset == -1) { + dirs->offset++; snprintf(dent->name, FS_DIRENT_NAME_LEN, "."); dent->type = FS_DT_DIR; *dentp = dent; - return 0; + goto err_out; } /* Emulate parent directory ../ */ - if (!dirs->dotdot) { - dirs->dotdot = true; + if (dirs->offset == 0) { + dirs->offset++; snprintf(dent->name, FS_DIRENT_NAME_LEN, ".."); dent->type = FS_DT_DIR; *dentp = dent; - return 0; + goto err_out; } + err = exfat_opendir(&ctxt.ef, dnode, &it); + if (err) + goto err_out; + + *dentp = NULL; + /* Read actual directory content */ - node = exfat_readdir(&dirs->it); - if (!node) { /* No more content, reset . and .. emulation */ - dirs->dot = false; - dirs->dotdot = false; - return 1; + while ((node = exfat_readdir(&it))) { + if (dirs->offset != ++offset) { + exfat_put_node(&ctxt.ef, node); + continue; + } + + exfat_get_name(node, dent->name); + if (node->attrib & EXFAT_ATTRIB_DIR) { + dent->type = FS_DT_DIR; + } else { + dent->type = FS_DT_REG; + dent->size = node->size; + } + exfat_put_node(&ctxt.ef, node); + *dentp = dent; + dirs->offset++; + break; } - exfat_get_name(node, dent->name); - if (node->attrib & EXFAT_ATTRIB_DIR) { - dent->type = FS_DT_DIR; - } else { - dent->type = FS_DT_REG; - dent->size = node->size; - } + exfat_closedir(&ctxt.ef, &it); - *dentp = dent; - - return 0; +err_out: + exfat_put_node(&ctxt.ef, dnode); + return err; } void exfat_fs_closedir(struct fs_dir_stream *fs_dirs) { - free(fs_dirs); + struct exfat_dir_stream *dirs = + container_of(fs_dirs, struct exfat_dir_stream, fs_dirs); + + free(dirs); } int exfat_fs_ls(const char *dirname) From e168a57c3532487b24348351f71f9f6ce7d671ee Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 13 Apr 2025 10:55:02 +0200 Subject: [PATCH 4/7] fs: exfat: Fix exfat_fs_exists() return value The exfat_fs_exists() should return 0 in case the path does not exist, and 1 in case the path does exist. Fix the inverted return value. This fixes 'test -e' command with exfat. Fixes: b86a651b646c ("fs: exfat: Add U-Boot porting layer") Signed-off-by: Marek Vasut --- fs/exfat/io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/exfat/io.c b/fs/exfat/io.c index 004ba5d3e45..43c05713ed0 100644 --- a/fs/exfat/io.c +++ b/fs/exfat/io.c @@ -875,11 +875,11 @@ int exfat_fs_exists(const char *filename) err = exfat_lookup_realpath(&ctxt.ef, &node, filename); if (err) - return err; + return 0; exfat_put_node(&ctxt.ef, node); - return 0; + return 1; } int exfat_fs_size(const char *filename, loff_t *size) From 1761c298afd53423377fad95ebce203e46c084de Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 13 Apr 2025 10:55:03 +0200 Subject: [PATCH 5/7] test_fs: Add test -e test Add test for the 'test -e' command to check for existence of files. This exercises struct fstype_info .exists callback. Signed-off-by: Marek Vasut --- test/py/tests/test_fs/test_basic.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/py/tests/test_fs/test_basic.py b/test/py/tests/test_fs/test_basic.py index 64a3b50f52a..88b163ce305 100644 --- a/test/py/tests/test_fs/test_basic.py +++ b/test/py/tests/test_fs/test_basic.py @@ -35,6 +35,19 @@ class TestFsBasic(object): '%sls host 0:0 invalid_d' % fs_cmd_prefix) assert('' == output) + with ubman.log.section('Test Case 1c - test -e'): + # Test Case 1 - test -e + output = ubman.run_command_list([ + 'host bind 0 %s' % fs_img, + 'test -e host 0:0 1MB.file && echo PASS']) + assert('PASS' in ''.join(output)) + + with ubman.log.section('Test Case 1d - test -e (invalid file)'): + # In addition, test with a nonexistent file to see if we crash. + output = ubman.run_command( + 'test -e host 0:0 2MB.file || echo PASS') + assert('PASS' in ''.join(output)) + def test_fs2(self, ubman, fs_obj_basic): """ Test Case 2 - size command for a small file From e5cbc3d2874ff5ec80f4a5c92b5db61769670d5f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 13 Apr 2025 10:55:04 +0200 Subject: [PATCH 6/7] fs: exfat: Implement trivial 'rename' support Implement exfat_fs_rename() to rename or move files. This is used by the 'mv' generic FS interface command. The rename implementation for other filesystems was added recently and was not part of exfat porting layer due to merge issue, which made 'mv' command crash, fix this by adding the missing implementation. Fixes: b86a651b646c ("fs: exfat: Add U-Boot porting layer") Signed-off-by: Marek Vasut --- fs/exfat/io.c | 5 +++++ fs/fs.c | 1 + include/exfat.h | 1 + 3 files changed, 7 insertions(+) diff --git a/fs/exfat/io.c b/fs/exfat/io.c index 43c05713ed0..c56f5675987 100644 --- a/fs/exfat/io.c +++ b/fs/exfat/io.c @@ -1013,6 +1013,11 @@ exit: return err; } +int exfat_fs_rename(const char *old_path, const char *new_path) +{ + return exfat_rename(&ctxt.ef, old_path, new_path); +} + void exfat_fs_close(void) { exfat_unmount(&ctxt.ef); diff --git a/fs/fs.c b/fs/fs.c index 0b62217fd59..1f36872fb9a 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -401,6 +401,7 @@ static struct fstype_info fstypes[] = { .ln = fs_ln_unsupported, .unlink = exfat_fs_unlink, .mkdir = exfat_fs_mkdir, + .rename = exfat_fs_rename, }, #endif { diff --git a/include/exfat.h b/include/exfat.h index 7e43beeb348..75fce5b6566 100644 --- a/include/exfat.h +++ b/include/exfat.h @@ -20,5 +20,6 @@ int exfat_fs_unlink(const char *filename); int exfat_fs_mkdir(const char *dirname); int exfat_fs_write(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actwrite); +int exfat_fs_rename(const char *old_path, const char *new_path); #endif /* _EXFAT_H */ From 6696f144279f9ee588463ad645e1f0c9b555ba84 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 13 Apr 2025 10:55:05 +0200 Subject: [PATCH 7/7] test_fs: Test 'mv' command on exfat and fs_generic Enable tests for the generic FS interface 'mv' command against both exfat and fs_generic. Signed-off-by: Marek Vasut --- test/py/tests/test_fs/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/py/tests/test_fs/conftest.py b/test/py/tests/test_fs/conftest.py index c73fb4abbcb..0205048e73a 100644 --- a/test/py/tests/test_fs/conftest.py +++ b/test/py/tests/test_fs/conftest.py @@ -17,7 +17,7 @@ supported_fs_fat = ['fat12', 'fat16'] supported_fs_mkdir = ['fat12', 'fat16', 'fat32', 'exfat', 'fs_generic'] supported_fs_unlink = ['fat12', 'fat16', 'fat32', 'exfat', 'fs_generic'] supported_fs_symlink = ['ext4'] -supported_fs_rename = ['fat12', 'fat16', 'fat32'] +supported_fs_rename = ['fat12', 'fat16', 'fat32', 'exfat', 'fs_generic'] # # Filesystem test specific setup