Hello fellow nixers,
In this thread I'll describe a behavior I've noticed by mistake.
Maybe someone can enlighten me about it.
Here's the scenario:
Try to follow with me.
We have two users, user1 and user2.
user1 creates a directory, and check it's stats:
Code:
~ > mkdir will_disappear
~ > stat will_disappear
File: ‘will_disappear’
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: 801h/2049d Inode: 6947274 Links: 2
Access: (0775/drwxrwxr-x) Uid: ( 1000/ patrick) Gid: ( 1000/ patrick)
Access: 2016-09-29 11:21:16.086851928 +0300
Modify: 2016-09-29 11:21:14.426851879 +0300
Change: 2016-09-29 11:21:14.426851879 +0300
Birth: -
user1 creates a file the directory and also check the stats:
Code:
~/will_disappear > touch will_disappear/magic
~/will_disappear > stat will_disappear/magic
File: ‘magic’
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 801h/2049d Inode: 6947276 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ patrick) Gid: ( 1000/ patrick)
Access: 2016-09-29 11:21:28.070852284 +0300
Modify: 2016-09-29 11:21:28.070852284 +0300
Change: 2016-09-29 11:21:28.070852284 +0300
Birth: -
Now user2 enters the directory...
Code:
~/will_disappear > cd will_disappear
And user1 moves the directory while user2 is still inside of it.
Code:
~ > mv will_disappear{,.bak}
~ > stat will_disappear.bak
File: ‘will_disappear.bak’
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: 801h/2049d Inode: 6947274 Links: 2
Access: (0775/drwxrwxr-x) Uid: ( 1000/ patrick) Gid: ( 1000/ patrick)
Access: 2016-09-29 11:21:51.010852964 +0300
Modify: 2016-09-29 11:21:28.070852284 +0300
Change: 2016-09-29 11:22:04.338853360 +0300
Birth: -
The inode of the directory hasn't changed but the inode of the parent directory has, this makes sense.
Clarifications:
To user2 there doesn't seem to be any issue so far, he can't notice until he does something like this:
Code:
~/will_disappear > touch $(pwd)/test
touch: cannot touch ‘/home/patrick/will_disappear/test’: No such file or directory
OK...
Code:
~/will_disappear > pwd
/home/patrick/will_disappear
Somehow it thinks it's still inside "will_disappear".
Here's my investigation:
This is very weird, I first thought that the shell might be highjacking the `pwd` command and I wanted to make sure.
With user2 I ran:
Code:
~/will_disappear > strace pwd <
execve("/bin/pwd", ["pwd"], [/* 56 vars */]) = 0
brk(0) = 0x1d08000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c75169000
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=173895, ...}) = 0
mmap(NULL, 173895, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5c7513e000
close(3) = 0
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P \2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1840928, ...}) = 0
mmap(NULL, 3949248, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5c74b84000
mprotect(0x7f5c74d3e000, 2097152, PROT_NONE) = 0
mmap(0x7f5c74f3e000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ba000) = 0x7f5c74f3e000
mmap(0x7f5c74f44000, 17088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5c74f44000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c7513d000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c7513b000
arch_prctl(ARCH_SET_FS, 0x7f5c7513b740) = 0
mprotect(0x7f5c74f3e000, 16384, PROT_READ) = 0
mprotect(0x606000, 4096, PROT_READ) = 0
mprotect(0x7f5c7516b000, 4096, PROT_READ) = 0
munmap(0x7f5c7513e000, 173895) = 0
brk(0) = 0x1d08000
brk(0x1d29000) = 0x1d29000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=7221056, ...}) = 0
mmap(NULL, 7221056, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5c744a1000
close(3) = 0
getcwd("/home/patrick/will_disappear.bak", 4096) = 33
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 32), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c75168000
write(1, "/home/patrick/will_disappear.bak"..., 33/home/patrick/will_disappear.bak
) = 33
close(1) = 0
munmap(0x7f5c75168000, 4096) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
Discard everything and focus on this part:
Quote:getcwd("/home/patrick/will_disappear.bak", 4096) = 33
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 32), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c75168000
write(1, "/home/patrick/will_disappear.bak"..., 33/home/patrick/will_disappear.bak
) = 33
It's doing the system call correctly and even writing on the screen the right current directory.
Now why do I see wrong results, I'm not sure...
I did the test with bash and zsh so it might not be shell dependent.
The behavior is more or less the same when user1 removes the directory user2 is in.
I think I've found the answer:
Code:
~/will_disappear > which pwd
pwd: shell built-in command
~/will_disappear > type pwd
pwd is a shell builtin
~/will_disappear > /bin/pwd
/bin/pwd: couldn't find directory entry in ‘..’ with matching i-node
Lesson, alway use the absolute path for your commands, don't trust the shell.
Some info on pwd
More info on inode
More info on virtual filesystem
What do you think?