From e3ba6f952bda153cfc3389eb48c7645d71b2b094 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Mon, 31 Jul 2023 18:25:26 +0200 Subject: [PATCH 1/5] extmod/vfs_posix: Fix relative root path. A VfsPosix created with a relative root path would get confused when chdir() was called on it and become unable to properly resolve absolute paths, because changing directories effectively shifted its root. The simplest fix for that would be to say "don't do that", but since the unit tests themselves do it, fix it by making a relative path absolute before storing it. Signed-off-by: Christian Walther --- extmod/vfs_posix.c | 26 +++++++++++++++++++++++++- tests/extmod/vfs_posix.py | 13 +++++++++++++ tests/extmod/vfs_posix.py.exp | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index d63bb5be7bff4..1505682f1141b 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -46,6 +46,9 @@ #ifdef _MSC_VER #include // For mkdir etc. #endif +#ifdef _WIN32 +#include +#endif typedef struct _mp_obj_vfs_posix_t { mp_obj_base_t base; @@ -107,7 +110,28 @@ STATIC mp_obj_t vfs_posix_make_new(const mp_obj_type_t *type, size_t n_args, siz mp_obj_vfs_posix_t *vfs = mp_obj_malloc(mp_obj_vfs_posix_t, type); vstr_init(&vfs->root, 0); if (n_args == 1) { - vstr_add_str(&vfs->root, mp_obj_str_get_str(args[0])); + const char *root = mp_obj_str_get_str(args[0]); + // if the root is relative, make it absolute, otherwise we'll get confused by chdir + #ifdef _WIN32 + char buf[MICROPY_ALLOC_PATH_MAX + 1]; + DWORD result = GetFullPathNameA(root, sizeof(buf), buf, NULL); + if (result > 0 && result < sizeof(buf)) { + vstr_add_str(&vfs->root, buf); + } else { + mp_raise_OSError(GetLastError()); + } + #else + if (root[0] != '\0' && root[0] != '/') { + char buf[MICROPY_ALLOC_PATH_MAX + 1]; + const char *cwd = getcwd(buf, sizeof(buf)); + if (cwd == NULL) { + mp_raise_OSError(errno); + } + vstr_add_str(&vfs->root, cwd); + vstr_add_char(&vfs->root, '/'); + } + vstr_add_str(&vfs->root, root); + #endif vstr_add_char(&vfs->root, '/'); } vfs->root_len = vfs->root.len; diff --git a/tests/extmod/vfs_posix.py b/tests/extmod/vfs_posix.py index 05dc0f537e3ca..ebbd08a4a6859 100644 --- a/tests/extmod/vfs_posix.py +++ b/tests/extmod/vfs_posix.py @@ -99,6 +99,19 @@ def write_files_without_closing(): print(type(list(vfs.ilistdir("."))[0][0])) print(type(list(vfs.ilistdir(b"."))[0][0])) +# chdir should not affect absolute paths (regression test) +vfs.mkdir("/subdir") +vfs.mkdir("/subdir/micropy_test_dir") +with vfs.open("/subdir/micropy_test_dir/test2", "w") as f: + f.write("wrong") +vfs.chdir("/subdir") +with vfs.open("/test2", "r") as f: + print(f.read()) +os.chdir(curdir) +vfs.remove("/subdir/micropy_test_dir/test2") +vfs.rmdir("/subdir/micropy_test_dir") +vfs.rmdir("/subdir") + # remove os.remove(temp_dir + "/test2") print(os.listdir(temp_dir)) diff --git a/tests/extmod/vfs_posix.py.exp b/tests/extmod/vfs_posix.py.exp index 99922e621d4c2..bd1ec7bad67b2 100644 --- a/tests/extmod/vfs_posix.py.exp +++ b/tests/extmod/vfs_posix.py.exp @@ -10,6 +10,7 @@ next_file_no <= base_file_no True +hello [] remove OSError False From 5f7065f57a1ce445266a566685ad451edecc58c3 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Sun, 30 Jul 2023 17:26:04 +0200 Subject: [PATCH 2/5] extmod/vfs_posix: Fix accidentally passing tests. These tests test an unrealistic situation and only pass by accident due to a bug. The upcoming fix for the bug would make them fail. The unrealistic situation is that VfsPosix methods are called with relative paths while the current working directory is somewhere outside of the root of the VFS. In the intended use of VFS objects via os.mount() (as opposed to calling methods directly as the tests do), this never happens, as mp_vfs_lookup_path() directs incoming calls to the VFS that contains the CWD. Make the testing situation realistic by changing the working directory to the root of the VFS before calling methods on it, as the subsequent relative path accesses expect. Thanks to the preceding commit, the tests still pass, but still for the wrong reason. The following commit "Fix relative paths on non-root VFS" will make them pass for the correct reason. Signed-off-by: Christian Walther --- tests/extmod/vfs_posix.py | 6 ++++++ tests/extmod/vfs_posix_ilistdir_del.py | 9 +++++++++ tests/extmod/vfs_posix_ilistdir_filter.py | 7 +++++++ 3 files changed, 22 insertions(+) diff --git a/tests/extmod/vfs_posix.py b/tests/extmod/vfs_posix.py index ebbd08a4a6859..bc4c7c2014b6c 100644 --- a/tests/extmod/vfs_posix.py +++ b/tests/extmod/vfs_posix.py @@ -88,6 +88,9 @@ def write_files_without_closing(): # construct new VfsPosix with path argument vfs = os.VfsPosix(temp_dir) +# when VfsPosix is used the intended way via os.mount(), it can only be called +# with relative paths when the CWD is inside or at its root, so simulate that +os.chdir(temp_dir) print(list(i[0] for i in vfs.ilistdir("."))) # stat, statvfs (statvfs may not exist) @@ -112,6 +115,9 @@ def write_files_without_closing(): vfs.rmdir("/subdir/micropy_test_dir") vfs.rmdir("/subdir") +# done with vfs, restore CWD +os.chdir(curdir) + # remove os.remove(temp_dir + "/test2") print(os.listdir(temp_dir)) diff --git a/tests/extmod/vfs_posix_ilistdir_del.py b/tests/extmod/vfs_posix_ilistdir_del.py index edb50dfd6229c..8c8e6978b6540 100644 --- a/tests/extmod/vfs_posix_ilistdir_del.py +++ b/tests/extmod/vfs_posix_ilistdir_del.py @@ -11,7 +11,13 @@ def test(testdir): + curdir = os.getcwd() vfs = os.VfsPosix(testdir) + # When VfsPosix is used the intended way via os.mount(), it can only be called + # with relative paths when the CWD is inside or at its root, so simulate that. + # (Although perhaps calling with a relative path was an oversight in this case + # and the respective line below was meant to read `vfs.rmdir("/" + dname)`.) + os.chdir(testdir) vfs.mkdir("/test_d1") vfs.mkdir("/test_d2") vfs.mkdir("/test_d3") @@ -48,6 +54,9 @@ def test(testdir): vfs.open("/test", "w").close() vfs.remove("/test") + # Done with vfs, restore CWD. + os.chdir(curdir) + # We need an empty directory for testing. # Skip the test if it already exists. diff --git a/tests/extmod/vfs_posix_ilistdir_filter.py b/tests/extmod/vfs_posix_ilistdir_filter.py index c32d1249710de..54012746057a8 100644 --- a/tests/extmod/vfs_posix_ilistdir_filter.py +++ b/tests/extmod/vfs_posix_ilistdir_filter.py @@ -10,7 +10,11 @@ def test(testdir): + curdir = os.getcwd() vfs = os.VfsPosix(testdir) + # When VfsPosix is used the intended way via os.mount(), it can only be called + # with relative paths when the CWD is inside or at its root, so simulate that. + os.chdir(testdir) dirs = [".a", "..a", "...a", "a.b", "a..b"] @@ -24,6 +28,9 @@ def test(testdir): print(dirs) + # Done with vfs, restore CWD. + os.chdir(curdir) + # We need an empty directory for testing. # Skip the test if it already exists. From 0c4fb1687193a6c6f5edbb9404ecb7e5d6f3bec3 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Sun, 30 Jul 2023 14:48:22 +0200 Subject: [PATCH 3/5] extmod/vfs_posix: Fix relative paths on non-root VFS. The unwritten API contract expected of a VFS by mp_vfs_lookup_path() is that paths passed in are relative to the root of the VFS if they start with '/' and relative to the current directory of the VFS otherwise. This was not correctly implemented in VfsPosix for instances with a non-empty root - all paths were interpreted relative to the root. Fix that. Since VfsPosix tracks its CWD using the "external" CWD of the Unix process, the correct handling for relative paths is to pass them through unmodified. Also, when concatenating absolute paths, fix an off-by-one resulting in a harmless double slash (the root path already has a trailing slash). Signed-off-by: Christian Walther --- extmod/vfs_posix.c | 16 ++++--- tests/extmod/vfs_posix_paths.py | 73 +++++++++++++++++++++++++++++ tests/extmod/vfs_posix_paths.py.exp | 13 +++++ 3 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 tests/extmod/vfs_posix_paths.py create mode 100644 tests/extmod/vfs_posix_paths.py.exp diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index 1505682f1141b..6df91a2732fd6 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -58,21 +58,23 @@ typedef struct _mp_obj_vfs_posix_t { } mp_obj_vfs_posix_t; STATIC const char *vfs_posix_get_path_str(mp_obj_vfs_posix_t *self, mp_obj_t path) { - if (self->root_len == 0) { - return mp_obj_str_get_str(path); + const char *path_str = mp_obj_str_get_str(path); + if (self->root_len == 0 || path_str[0] != '/') { + return path_str; } else { - self->root.len = self->root_len; - vstr_add_str(&self->root, mp_obj_str_get_str(path)); + self->root.len = self->root_len - 1; + vstr_add_str(&self->root, path_str); return vstr_null_terminated_str(&self->root); } } STATIC mp_obj_t vfs_posix_get_path_obj(mp_obj_vfs_posix_t *self, mp_obj_t path) { - if (self->root_len == 0) { + const char *path_str = mp_obj_str_get_str(path); + if (self->root_len == 0 || path_str[0] != '/') { return path; } else { - self->root.len = self->root_len; - vstr_add_str(&self->root, mp_obj_str_get_str(path)); + self->root.len = self->root_len - 1; + vstr_add_str(&self->root, path_str); return mp_obj_new_str(self->root.buf, self->root.len); } } diff --git a/tests/extmod/vfs_posix_paths.py b/tests/extmod/vfs_posix_paths.py new file mode 100644 index 0000000000000..8cdad77068f29 --- /dev/null +++ b/tests/extmod/vfs_posix_paths.py @@ -0,0 +1,73 @@ +# Test for VfsPosix with relative paths + +try: + import os + + os.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +# We need a directory for testing that doesn't already exist. +# Skip the test if it does exist. +temp_dir = "vfs_posix_paths_test_dir" +try: + import os + + os.stat(temp_dir) + print("SKIP") + raise SystemExit +except OSError: + pass + +curdir = os.getcwd() +os.mkdir(temp_dir) + +# construct new VfsPosix with absolute path argument +temp_dir_abs = os.getcwd() + os.sep + temp_dir +vfs = os.VfsPosix(temp_dir_abs) +# when VfsPosix is used the intended way via os.mount(), it can only be called +# with relative paths when the CWD is inside or at its root, so simulate that +os.chdir(temp_dir_abs) +vfs.mkdir("subdir") +vfs.mkdir("subdir/one") +print('listdir("/"):', sorted(i[0] for i in vfs.ilistdir("/"))) +print('listdir("."):', sorted(i[0] for i in vfs.ilistdir("."))) +print('getcwd() in {"", "/"}:', vfs.getcwd() in {"", "/"}) +print('chdir("subdir"):', vfs.chdir("subdir")) +print("getcwd():", vfs.getcwd()) +print('mkdir("two"):', vfs.mkdir("two")) +f = vfs.open("file.py", "w") +f.write("print('hello')") +f.close() +print('listdir("/"):', sorted(i[0] for i in vfs.ilistdir("/"))) +print('listdir("/subdir"):', sorted(i[0] for i in vfs.ilistdir("/subdir"))) +print('listdir("."):', sorted(i[0] for i in vfs.ilistdir("."))) +try: + f = vfs.open("/subdir/file.py", "r") + print(f.read()) + f.close() +except Exception as e: + print(e) +import sys + +sys.path.insert(0, "") +try: + import file + + print(file) +except Exception as e: + print(e) +del sys.path[0] +vfs.remove("file.py") +vfs.rmdir("two") +vfs.rmdir("/subdir/one") +vfs.chdir("/") +vfs.rmdir("/subdir") + +# done with vfs, restore CWD +os.chdir(curdir) + +# rmdir +os.rmdir(temp_dir) +print(temp_dir in os.listdir()) diff --git a/tests/extmod/vfs_posix_paths.py.exp b/tests/extmod/vfs_posix_paths.py.exp new file mode 100644 index 0000000000000..5828453c92167 --- /dev/null +++ b/tests/extmod/vfs_posix_paths.py.exp @@ -0,0 +1,13 @@ +listdir("/"): ['subdir'] +listdir("."): ['subdir'] +getcwd() in {"", "/"}: True +chdir("subdir"): None +getcwd(): subdir +mkdir("two"): None +listdir("/"): ['subdir'] +listdir("/subdir"): ['file.py', 'one', 'two'] +listdir("."): ['file.py', 'one', 'two'] +print('hello') +hello + +False From be28829ae8d22e1a0373bda116022ee446fb2ed8 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Sun, 30 Jul 2023 14:48:22 +0200 Subject: [PATCH 4/5] extmod/vfs_posix: Fix getcwd() on non-root VFS. The unwritten API contract expected of a VFS.getcwd() by mp_vfs_getcwd() is that its return value should be either "" or "/" when the CWD is at the root of the VFS and otherwise start with a slash and not end with a slash. This was not correctly implemented in VfsPosix for instances with a non-empty root - the required leading slash, if any, was cut off because the root length includes a trailing slash. This would result in missing slashes in the middle of the return value of os.getcwd() or in uninitialized garbage from beyond a string's null terminator when the CWD was at the VFS root. Signed-off-by: Christian Walther --- extmod/vfs_posix.c | 9 ++++++++- tests/extmod/vfs_posix_paths.py | 19 +++++++++++++++++++ tests/extmod/vfs_posix_paths.py.exp | 12 +++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index 6df91a2732fd6..e29b47cccda83 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -186,7 +186,14 @@ STATIC mp_obj_t vfs_posix_getcwd(mp_obj_t self_in) { if (ret == NULL) { mp_raise_OSError(errno); } - ret += self->root_len; + if (self->root_len > 0) { + ret += self->root_len - 1; + #ifdef _WIN32 + if (*ret == '\\') { + *(char *)ret = '/'; + } + #endif + } return mp_obj_new_str(ret, strlen(ret)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_getcwd_obj, vfs_posix_getcwd); diff --git a/tests/extmod/vfs_posix_paths.py b/tests/extmod/vfs_posix_paths.py index 8cdad77068f29..5806a34521a23 100644 --- a/tests/extmod/vfs_posix_paths.py +++ b/tests/extmod/vfs_posix_paths.py @@ -68,6 +68,25 @@ # done with vfs, restore CWD os.chdir(curdir) +# some integration tests with a mounted VFS +os.mount(os.VfsPosix(temp_dir_abs), "/mnt") +os.mkdir("/mnt/dir") +print('chdir("/mnt/dir"):', os.chdir("/mnt/dir")) +print("getcwd():", os.getcwd()) +print('chdir("/mnt"):', os.chdir("/mnt")) +print("getcwd():", os.getcwd()) +print('chdir("/"):', os.chdir("/")) +print("getcwd():", os.getcwd()) +print('chdir("/mnt/dir"):', os.chdir("/mnt/dir")) +print("getcwd():", os.getcwd()) +print('chdir(".."):', os.chdir("..")) +print("getcwd():", os.getcwd()) +os.rmdir("/mnt/dir") +os.umount("/mnt") + +# restore CWD +os.chdir(curdir) + # rmdir os.rmdir(temp_dir) print(temp_dir in os.listdir()) diff --git a/tests/extmod/vfs_posix_paths.py.exp b/tests/extmod/vfs_posix_paths.py.exp index 5828453c92167..ecc13222aaaeb 100644 --- a/tests/extmod/vfs_posix_paths.py.exp +++ b/tests/extmod/vfs_posix_paths.py.exp @@ -2,7 +2,7 @@ listdir("/"): ['subdir'] listdir("."): ['subdir'] getcwd() in {"", "/"}: True chdir("subdir"): None -getcwd(): subdir +getcwd(): /subdir mkdir("two"): None listdir("/"): ['subdir'] listdir("/subdir"): ['file.py', 'one', 'two'] @@ -10,4 +10,14 @@ listdir("."): ['file.py', 'one', 'two'] print('hello') hello +chdir("/mnt/dir"): None +getcwd(): /mnt/dir +chdir("/mnt"): None +getcwd(): /mnt +chdir("/"): None +getcwd(): / +chdir("/mnt/dir"): None +getcwd(): /mnt/dir +chdir(".."): None +getcwd(): /mnt False From 7be16e05402a49a432523d6a55e614c7d87627b6 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Wed, 2 Aug 2023 13:15:21 +0200 Subject: [PATCH 5/5] extmod/vfs_posix: Additional tests for coverage of error cases. Signed-off-by: Christian Walther --- tests/extmod/vfs_posix_enoent.py | 42 ++++++++++++++++++++++++++++ tests/extmod/vfs_posix_enoent.py.exp | 2 ++ 2 files changed, 44 insertions(+) create mode 100644 tests/extmod/vfs_posix_enoent.py create mode 100644 tests/extmod/vfs_posix_enoent.py.exp diff --git a/tests/extmod/vfs_posix_enoent.py b/tests/extmod/vfs_posix_enoent.py new file mode 100644 index 0000000000000..e6010d79d58f0 --- /dev/null +++ b/tests/extmod/vfs_posix_enoent.py @@ -0,0 +1,42 @@ +# Test for VfsPosix error conditions + +try: + import os + import sys + + os.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +if sys.platform == "win32": + # Windows doesn't let you delete the current directory, so this cannot be + # tested. + print("SKIP") + raise SystemExit + +# We need an empty directory for testing. +# Skip the test if it already exists. +temp_dir = "vfs_posix_enoent_test_dir" +try: + os.stat(temp_dir) + print("SKIP") + raise SystemExit +except OSError: + pass + +curdir = os.getcwd() +os.mkdir(temp_dir) +os.chdir(temp_dir) +os.rmdir(curdir + "/" + temp_dir) +try: + print("getcwd():", os.getcwd()) +except OSError as e: + # expecting ENOENT = 2 + print("getcwd():", repr(e)) + +try: + print("VfsPosix():", os.VfsPosix("something")) +except OSError as e: + # expecting ENOENT = 2 + print("VfsPosix():", repr(e)) diff --git a/tests/extmod/vfs_posix_enoent.py.exp b/tests/extmod/vfs_posix_enoent.py.exp new file mode 100644 index 0000000000000..f2d9a0d551068 --- /dev/null +++ b/tests/extmod/vfs_posix_enoent.py.exp @@ -0,0 +1,2 @@ +getcwd(): OSError(2,) +VfsPosix(): OSError(2,) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy