Skip to content

vfs.VfsPosix('.') gets confused if cwd changes #17012

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

Open
dubiousjim opened this issue Mar 26, 2025 · 2 comments
Open

vfs.VfsPosix('.') gets confused if cwd changes #17012

dubiousjim opened this issue Mar 26, 2025 · 2 comments
Labels

Comments

@dubiousjim
Copy link
Contributor

dubiousjim commented Mar 26, 2025

Port, board and/or hardware

Unix port, installed on Alpine Linux (uses musl libc)

MicroPython version

MicroPython v1.24.1 on 2025-03-14; linux [GCC 14.2.0] version

If I create a VfsPosix object rooted at '.', and query the working directory with the getcwd() method, I get a sane result (""). If I then change the working directory, either by using the chdir() method on that object, or using the os module, and query the working directory again through the VfsPosix object, I get an invalid result (just some trailing characters from the correct result). This invalid result persists (that is, it doesn't update) even if I chdir() a second time.

Reproduction

main0:~/micropython-work/test$ tree
.
└── foo

1 directories, 0 files

$ micropython
>>> import os,vfs; Vfs=vfs.VfsPosix
>>> os.getcwd()
'/home/bob/micropython-work/test'
>>> v=Vfs('.')
>>> v.getcwd()
''
>>> os.chdir('foo')
>>> os.getcwd() # output as expected
'/home/bob/micropython-work/test/foo'
>>> v.getcwd() # weird output
'oo'
>>> os.chdir('..')
>>> os.getcwd() # output as expected
'/home/bob/micropython-work/test'
>>> v.getcwd() # weird output persists
'oo'

Expected behaviour

Expected second v.getcwd() call to return something like "foo" or "./foo" or "/foo". Expected third v.getcwd() call to return something like "" again.

Observed behaviour

Output given in Reproduction.

Additional Information

No, I've provided everything above.

Code of Conduct

Yes, I agree

@dubiousjim dubiousjim added the bug label Mar 26, 2025
@Gadgetoid
Copy link
Contributor

Gadgetoid commented Mar 26, 2025

Off to a roaring start, the UNIX port on macOS gives me:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
UnicodeError: 

Where test.py is:

import os
from vfs import VfsPosix as Vfs

print(os.getcwd())
v=Vfs('.')
print(v.getcwd())

Looking at the VfsPosix source:

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, '/');

It looks like VfsPosix('.') would try to build a relative path quite naively and end up with:

/home/bob/micropython-work/test/./

And, indeed, if I specify a path like that I get the same UnicodeError.

However:

import os
from vfs import VfsPosix as Vfs

print(os.getcwd())
v=Vfs('/./')
print(v.getcwd())

Produces a similar broken path bug to yours, except it's chopped off the front...

./build-standard/micropython test.py
/Users/gadgetoid/Development/micropython/ports/unix
ers/gadgetoid/Development//micropython/ports/unix

There is something going on here!

Edit: under the hood this just calls the Posix chdir and getcwd.

Since VfsPosix("/some/path") sets the root of the object and doesn't actually chdir into that directory, you might find the following works more reliably:

vfs = VfsPosix()
vfs.chdir(os.getcwd())

Specifying a root to VfsPosix doesn't seem to materially affect how chdir and getcwd work, they don't seem to be relative to that root nor bounded by it. It does completely break getcwd() though since that tries to cut the root path off the front of the returned directory.

Some more info here: #12137

A particular tidbit of that is:

In the intended use of VFS objects via os.mount() (as opposed to calling methods directly as the tests do)

So perhaps there are more bugs in VfsPosix, but also they are broadly worked around when it's used with os.mount().

@dubiousjim
Copy link
Contributor Author

Right, I saw that VfsPosix() (or what seems to be equivalent, VfsPosix("") or VfsPosix("/")) works reliably in these respects.

I think this bug just manifests when non-empty and non-absolute roots are supplied to the constructor. I did get the impression from my (non-systematic, exploratory) testing that such roots did make a difference to some operations --- for instance that they constrained when a chdir("..") call to the vfs object would work. What I'm more sure of is that VfsPosix(".").ilistdir("/") is rooted in the . directory, not in the filesystem's / directory. Same with VfsPosix(".").stat("/"): it will give info about . not /. And VfsPosix(".").stat("/sub") will give info about ./sub, not /sub.

I agree there are workarounds. But since this seemed to be buggy behavior, that could potentially lead to corrupted data or worse, I thought it should be noted and fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants