On Wed, Dec 26, 2018 at 12:40:09PM -0600, Peng Yu wrote: > Hi, > > I can not mkdir -p . in /tmp/ via the loadable mkdir. What is the > difference between /tmp/ and other directories? I am on Mac OS X. Is > this a bug in mkdir? > > $ cd /tmp > $ mkdir -p -- . > -bash: mkdir: .: Operation not permitted
I can reproduce this in Linux [Linux debian 4.18.0-3-amd64 #1 SMP Debian 4.18.20-2 (2018-11-23) x86_64 GNU/Linux] dualbus@debian:~/src/gnu/bash$ strace -e trace=%file -f ./bash -c 'enable -f examples/loadables/mkdir mkdir && mkdir -p -- /tmp' execve("./bash", ["./bash", "-c", "enable -f examples/loadables/mkd"...], 0x7fff7c9b1c18 /* 33 vars */) = 0 (...) stat("/home/dualbus/src/gnu/bash", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat("/home/dualbus", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat("/home/dualbus/src", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat("/home/dualbus/src/gnu", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat("/home/dualbus/src/gnu/bash", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat("/home/dualbus/src/iovisor/bcc", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 openat(AT_FDCWD, "examples/loadables/mkdir", O_RDONLY|O_CLOEXEC) = 3 getcwd("/home/dualbus/src/gnu/bash", 128) = 27 stat("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=4096, ...}) = 0 chmod("/tmp", 0755) = -1 EPERM (Operation not permitted) openat(AT_FDCWD, "/usr/share/locale/en_CA/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/local/share/locale/en_CA/LC_MESSAGES/bash.mo", O_RDONLY) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/usr/local/share/locale/en/LC_MESSAGES/bash.mo", O_RDONLY) = -1 ENOENT (No such file or directory) ./bash: line 0: mkdir: /tmp: Operation not permitted +++ exited with 1 +++ dualbus@debian:~/src/gnu/bash$ stat /tmp File: /tmp Size: 4096 Blocks: 8 IO Block: 4096 directory Device: fe01h/65025d Inode: 25427969 Links: 18 Access: (1777/drwxrwxrwt) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2018-12-26 11:08:30.062681076 -0800 Modify: 2018-12-26 11:37:56.826984422 -0800 Change: 2018-12-26 11:37:56.826984422 -0800 Birth: - If you look at the source, it calls the mkdir() function to create the directory. dualbus@debian:~/src/gnu/bash$ cat -n examples/loadables/mkdir.c | sed -n '174,180p' 174 if (stat (npath, &sb) != 0) 175 { 176 if (mkdir (npath, parent_mode)) <- this 177 { 178 builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); 179 umask (original_umask); 180 free (npath); Ref: http://git.savannah.gnu.org/cgit/bash.git/tree/examples/loadables/mkdir.c?h=6870125961b85baa9628ce4c5fbbca94e8046656#n174 Comparing to GNU coreutils' mkdir (which uses gnulib): dualbus@debian:~$ strace -e trace=%file -f mkdir -p /tmp/ execve("/bin/mkdir", ["mkdir", "-p", "/tmp/"], 0x7fff4f7a11b8 /* 33 vars */) = 0 (...) mkdir("/tmp/", 0777) = -1 EEXIST (File exists) stat("/tmp/", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=4096, ...}) = 0 +++ exited with 0 +++ My guess is that the "problem" has to do with how bash computes the `parent_mode': dualbus@debian:~/src/gnu/bash$ cat -n examples/loadables/mkdir.c | sed -n '106,114p' 106 /* Make the new mode */ 107 original_umask = umask (0); 108 umask (original_umask); 109 110 nmode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~original_umask; 111 parent_mode = nmode | (S_IWUSR|S_IXUSR); /* u+wx */ 112 113 /* Adjust new mode based on mode argument */ 114 nmode &= omode; Ref: http://git.savannah.gnu.org/cgit/bash.git/tree/examples/loadables/mkdir.c?h=6870125961b85baa9628ce4c5fbbca94e8046656#n106 In gnulib, this is what is used to create the parent directories: Ref: http://git.savannah.gnu.org/cgit/gnulib.git/tree/lib/mkdir-p.c?h=95c96b6dddd31f3676f72ed044d0c493ab5642d8#n116