Skip to content

extmod/vfs_posix: Fix relative paths on non-root VFS #12137

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 42 additions & 9 deletions extmod/vfs_posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
#ifdef _MSC_VER
#include <direct.h> // For mkdir etc.
#endif
#ifdef _WIN32
#include <windows.h>
#endif

typedef struct _mp_obj_vfs_posix_t {
mp_obj_base_t base;
Expand All @@ -55,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);
}
}
Expand Down Expand Up @@ -107,7 +112,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;
Expand Down Expand Up @@ -160,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);
Expand Down
19 changes: 19 additions & 0 deletions tests/extmod/vfs_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -99,6 +102,22 @@ 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")

# done with vfs, restore CWD
os.chdir(curdir)

# remove
os.remove(temp_dir + "/test2")
print(os.listdir(temp_dir))
Expand Down
1 change: 1 addition & 0 deletions tests/extmod/vfs_posix.py.exp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ next_file_no <= base_file_no True
<class 'tuple'>
<class 'str'>
<class 'bytes'>
hello
[]
remove OSError
False
Expand Down
42 changes: 42 additions & 0 deletions tests/extmod/vfs_posix_enoent.py
Original file line number Diff line number Diff line change
@@ -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))
2 changes: 2 additions & 0 deletions tests/extmod/vfs_posix_enoent.py.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
getcwd(): OSError(2,)
VfsPosix(): OSError(2,)
9 changes: 9 additions & 0 deletions tests/extmod/vfs_posix_ilistdir_del.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions tests/extmod/vfs_posix_ilistdir_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand All @@ -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.
Expand Down
92 changes: 92 additions & 0 deletions tests/extmod/vfs_posix_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# 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)

# 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())
23 changes: 23 additions & 0 deletions tests/extmod/vfs_posix_paths.py.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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
<module 'file' from 'file.py'>
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
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