strange return from os.mkdir

12 views
Skip to first unread message

zhiting zhu

unread,
Nov 1, 2019, 12:38:14 PM11/1/19
to OSv Development
 OSv:

>>> import os
>>> os.makedirs("/tmp/lambda_11111")
>>> os.mkdir("/tmp/lambda_11111/out_0/")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/lambda_11111/out_0/'
>>> os.mkdir("/tmp/lambda_11111/out_0")
>>>

On the host:
>>> import os
>>> os.makedirs("/tmp/lambda_11111")
>>> os.mkdir("/tmp/lambda_11111/out_0/")
>>>

Why I can't put the / in the path? I'm using the same python
Python 3.6.8 (default, Oct  7 2019, 12:59:55)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

from Ubuntu 18.04 LTS.

Best,
Zhiting

Waldek Kozaczuk

unread,
Nov 3, 2019, 12:16:36 PM11/3/19
to OSv Development
Indeed this looks like a bug in vfs layer. Thanks for finding it :-)

Here is a patch that fixes it (needs unit tests). 

diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
index 7e028ffb..eca89556 100644
--- a/fs/vfs/main.cc
+++ b/fs/vfs/main.cc
@@ -825,7 +825,7 @@ mkdir(const char *pathname, mode_t mode)
     mode = apply_umask(mode);
 
     trace_vfs_mkdir(pathname, mode);
-    if ((error = task_conv(t, pathname, VWRITE, path)) != 0)
+    if ((error = task_conv_remove_trailing_slash(t, pathname, VWRITE, path)) != 0)
         goto out_errno;
 
     error = sys_mkdir(path, mode);
@@ -853,7 +853,7 @@ int rmdir(const char *pathname)
     error = ENOENT;
     if (pathname == nullptr)
         goto out_errno;
-    if ((error = task_conv(t, pathname, VWRITE, path)) != 0)
+    if ((error = task_conv_remove_trailing_slash(t, pathname, VWRITE, path)) != 0)
         goto out_errno;
 
     error = sys_rmdir(path);
@@ -977,7 +977,7 @@ int chdir(const char *pathname)
     if (pathname == nullptr)
         goto out_errno;
 
-    if ((error = task_conv(t, pathname, VREAD, path)) != 0)
+    if ((error = task_conv_remove_trailing_slash(t, pathname, VREAD, path)) != 0)
         goto out_errno;
 
     /* Check if directory exits */
diff --git a/fs/vfs/vfs.h b/fs/vfs/vfs.h
index d86ef957..38d2a2ab 100644
--- a/fs/vfs/vfs.h
+++ b/fs/vfs/vfs.h
@@ -135,7 +135,8 @@ int sys_fchmod(int fd, mode_t mode);
 
 int task_alloc(struct task **pt);
 int task_conv(struct task *t, const char *path, int mode, char *full);
-int path_conv(char *wd, const char *cpath, char *full);
+int task_conv_remove_trailing_slash(struct task *t, const char *path, int mode, char *full);
 
 //int sec_file_permission(task_t task, char *path, int mode);
 int sec_vnode_permission(char *path);
diff --git a/fs/vfs/vfs_lookup.cc b/fs/vfs/vfs_lookup.cc
index ad03fe25..4569061b 100644
--- a/fs/vfs/vfs_lookup.cc
+++ b/fs/vfs/vfs_lookup.cc
@@ -85,7 +85,7 @@ namei_follow_link(struct dentry *dp, char *node, char *name, char *fp, size_t mo
     } else {
         strlcpy(t.get(), p, PATH_MAX);
         strlcpy(node, fp, mountpoint_len + c + 1);
-        path_conv(node, lp, fp);
+        path_conv(node, lp, fp, false);
         strlcat(fp, t.get(), PATH_MAX);
     }
     node[0] = 0;
diff --git a/fs/vfs/vfs_task.cc b/fs/vfs/vfs_task.cc
index 332c460d..178740a0 100644
--- a/fs/vfs/vfs_task.cc
+++ b/fs/vfs/vfs_task.cc
@@ -65,7 +65,7 @@ task_alloc(struct task **pt)
  * @full: full path to be returned
  */
 int
-path_conv(char *wd, const char *cpath, char *full)
+path_conv(char *wd, const char *cpath, char *full, bool truncate_trailing_slash)
 {
  char path[PATH_MAX];
  char *src, *tgt, *p, *end;
@@ -129,6 +129,10 @@ path_conv(char *wd, const char *cpath, char *full)
  }
  src = p + 1;
  }
+ if (truncate_trailing_slash &&
+ tgt != full && *(tgt - 1) == '/' && tgt - 1 != full) {
+ tgt--;
+ }
  *tgt = '\0';
 
  return (0);
@@ -141,8 +145,8 @@ path_conv(char *wd, const char *cpath, char *full)
  * @full: full path to be returned
  * @acc: access mode
  */
-int
-task_conv(struct task *t, const char *cpath, int acc, char *full)
+static int
+_task_conv(struct task *t, const char *cpath, int acc, char *full, bool truncate_trailing_slash)
 {
        int rc;
 
@@ -150,7 +154,7 @@ task_conv(struct task *t, const char *cpath, int acc, char *full)
                return (EFAULT);
        }
 
-       rc = path_conv(t->t_cwd, cpath, full);
+       rc = path_conv(t->t_cwd, cpath, full, truncate_trailing_slash);
        if (rc != 0) {
                return (rc);
        }
@@ -159,6 +163,32 @@ task_conv(struct task *t, const char *cpath, int acc, char *full)
        return (0); //sec_file_permission(t->t_taskid, full, acc);
 }
 
+/*
+ * Convert to full path from the cwd of task and path.
+ * @t:    task structure
+ * @path: target path
+ * @full: full path to be returned
+ * @acc: access mode
+ */
+int
+task_conv(struct task *t, const char *cpath, int acc, char *full)
+{
+    return _task_conv(t, cpath, acc, full, false);
+}
+
+/*
+ * Convert to full path from the cwd of task and path. Remove any trailing slash.
+ * @t:    task structure
+ * @path: target path
+ * @full: full path to be returned
+ * @acc: access mode
+ */
+int
+task_conv_remove_trailing_slash(struct task *t, const char *cpath, int acc, char *full)
+{
+    return _task_conv(t, cpath, acc, full, true);
+}
+
 /*
  * Safe copying function that checks for overflow.
  */


I will send a proper patch with unit tests later. I changes VFS in critical places so it would be nice to review it.

Also I am not 100% it fixes all the cases. Note I have not simply made a fix in central path_conv() function as t would break many non-directory cases where trailing slash matters. 
Reply all
Reply to author
Forward
0 new messages