2727#include <stdint.h>
2828#include <string.h>
2929
30+ #include "py/runtime0.h"
3031#include "py/runtime.h"
3132#include "py/objstr.h"
3233#include "py/mperrno.h"
@@ -50,8 +51,16 @@ mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) {
5051 ++ path ;
5152 is_abs = 1 ;
5253 }
54+ if (* path == '\0' ) {
55+ // path is "" or "/" so return virtual root
56+ return MP_VFS_ROOT ;
57+ }
5358 for (mp_vfs_mount_t * vfs = MP_STATE_VM (vfs_mount_table ); vfs != NULL ; vfs = vfs -> next ) {
5459 size_t len = vfs -> len - 1 ;
60+ if (len == 0 ) {
61+ * path_out = path - is_abs ;
62+ return vfs ;
63+ }
5564 if (strncmp (path , vfs -> str + 1 , len ) == 0 ) {
5665 if (path [len ] == '/' ) {
5766 * path_out = path + len ;
@@ -62,10 +71,9 @@ mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) {
6271 }
6372 }
6473 }
65- if (* path == '\0' ) {
66- // path was "" or "/" so return virtual root
67- return MP_VFS_ROOT ;
68- }
74+
75+ // if we get here then there's nothing mounted on /
76+
6977 if (is_abs ) {
7078 // path began with / and was not found
7179 return MP_VFS_NONE ;
@@ -162,13 +170,24 @@ mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args
162170
163171 // check that the destination mount point is unused
164172 const char * path_out ;
165- if (mp_vfs_lookup_path (mp_obj_str_get_str (pos_args [1 ]), & path_out ) != MP_VFS_NONE ) {
166- mp_raise_OSError (MP_EPERM );
173+ mp_vfs_mount_t * existing_mount = mp_vfs_lookup_path (mp_obj_str_get_str (pos_args [1 ]), & path_out );
174+ if (existing_mount != MP_VFS_NONE && existing_mount != MP_VFS_ROOT ) {
175+ if (vfs -> len != 1 && existing_mount -> len == 1 ) {
176+ // if root dir is mounted, still allow to mount something within a subdir of root
177+ } else {
178+ // mount point in use
179+ mp_raise_OSError (MP_EPERM );
180+ }
167181 }
168182
169183 // insert the vfs into the mount table
170184 mp_vfs_mount_t * * vfsp = & MP_STATE_VM (vfs_mount_table );
171185 while (* vfsp != NULL ) {
186+ if ((* vfsp )-> len == 1 ) {
187+ // make sure anything mounted at the root stays at the end of the list
188+ vfs -> next = * vfsp ;
189+ break ;
190+ }
172191 vfsp = & (* vfsp )-> next ;
173192 }
174193 * vfsp = vfs ;
@@ -228,10 +247,21 @@ MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_open_obj, 0, mp_vfs_open);
228247mp_obj_t mp_vfs_chdir (mp_obj_t path_in ) {
229248 mp_obj_t path_out ;
230249 mp_vfs_mount_t * vfs = lookup_path (path_in , & path_out );
231- if (vfs != MP_VFS_ROOT ) {
250+ MP_STATE_VM (vfs_cur ) = vfs ;
251+ if (vfs == MP_VFS_ROOT ) {
252+ // If we change to the root dir and a VFS is mounted at the root then
253+ // we must change that VFS's current dir to the root dir so that any
254+ // subsequent relative paths begin at the root of that VFS.
255+ for (vfs = MP_STATE_VM (vfs_mount_table ); vfs != NULL ; vfs = vfs -> next ) {
256+ if (vfs -> len == 1 ) {
257+ mp_obj_t root = mp_obj_new_str ("/" , 1 , false);
258+ mp_vfs_proxy_call (vfs , MP_QSTR_chdir , 1 , & root );
259+ break ;
260+ }
261+ }
262+ } else {
232263 mp_vfs_proxy_call (vfs , MP_QSTR_chdir , 1 , & path_out );
233264 }
234- MP_STATE_VM (vfs_cur ) = vfs ;
235265 return mp_const_none ;
236266}
237267MP_DEFINE_CONST_FUN_OBJ_1 (mp_vfs_chdir_obj , mp_vfs_chdir );
@@ -241,6 +271,10 @@ mp_obj_t mp_vfs_getcwd(void) {
241271 return MP_OBJ_NEW_QSTR (MP_QSTR__slash_ );
242272 }
243273 mp_obj_t cwd_o = mp_vfs_proxy_call (MP_STATE_VM (vfs_cur ), MP_QSTR_getcwd , 0 , NULL );
274+ if (MP_STATE_VM (vfs_cur )-> len == 1 ) {
275+ // don't prepend "/" for vfs mounted at root
276+ return cwd_o ;
277+ }
244278 const char * cwd = mp_obj_str_get_str (cwd_o );
245279 vstr_t vstr ;
246280 vstr_init (& vstr , MP_STATE_VM (vfs_cur )-> len + strlen (cwd ) + 1 );
@@ -267,8 +301,15 @@ mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) {
267301 // list the root directory
268302 mp_obj_t dir_list = mp_obj_new_list (0 , NULL );
269303 for (vfs = MP_STATE_VM (vfs_mount_table ); vfs != NULL ; vfs = vfs -> next ) {
270- mp_obj_list_append (dir_list , mp_obj_new_str_of_type (mp_obj_get_type (path_in ),
271- (const byte * )vfs -> str + 1 , vfs -> len - 1 ));
304+ if (vfs -> len == 1 ) {
305+ // vfs is mounted at root dir, delegate to it
306+ mp_obj_t root = mp_obj_new_str ("/" , 1 , false);
307+ mp_obj_t dir_list2 = mp_vfs_proxy_call (vfs , MP_QSTR_listdir , 1 , & root );
308+ dir_list = mp_binary_op (MP_BINARY_OP_ADD , dir_list , dir_list2 );
309+ } else {
310+ mp_obj_list_append (dir_list , mp_obj_new_str_of_type (mp_obj_get_type (path_in ),
311+ (const byte * )vfs -> str + 1 , vfs -> len - 1 ));
312+ }
272313 }
273314 return dir_list ;
274315 }
0 commit comments