Dlaczego rmdir i rozłącza dwa osobne wywołania systemowe?

10

Oto coś, przez co zastanawiałem się przez chwilę:

[15:40:50][/tmp]$ mkdir a
[15:40:52][/tmp]$ strace rmdir a
execve("/usr/bin/rmdir", ["rmdir", "a"], [/* 78 vars */]) = 0
brk(0)                                  = 0x11bb000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff3772c3000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=245801, ...}) = 0
mmap(NULL, 245801, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff377286000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\36\3428<\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2100672, ...}) = 0
mmap(0x3c38e00000, 3924576, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3c38e00000
mprotect(0x3c38fb4000, 2097152, PROT_NONE) = 0
mmap(0x3c391b4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x3c391b4000
mmap(0x3c391ba000, 16992, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3c391ba000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff377285000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff377283000
arch_prctl(ARCH_SET_FS, 0x7ff377283740) = 0
mprotect(0x609000, 4096, PROT_READ)     = 0
mprotect(0x3c391b4000, 16384, PROT_READ) = 0
mprotect(0x3c38c1f000, 4096, PROT_READ) = 0
munmap(0x7ff377286000, 245801)          = 0
brk(0)                                  = 0x11bb000
brk(0x11dc000)                          = 0x11dc000
brk(0)                                  = 0x11dc000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106070960, ...}) = 0
mmap(NULL, 106070960, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff370d5a000
close(3)                                = 0
rmdir("a")                              = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
[15:40:55][/tmp]$ touch a
[15:41:16][/tmp]$ strace rm a
execve("/usr/bin/rm", ["rm", "a"], [/* 78 vars */]) = 0
brk(0)                                  = 0xfa8000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3b2388a000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=245801, ...}) = 0
mmap(NULL, 245801, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3b2384d000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\36\3428<\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2100672, ...}) = 0
mmap(0x3c38e00000, 3924576, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3c38e00000
mprotect(0x3c38fb4000, 2097152, PROT_NONE) = 0
mmap(0x3c391b4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x3c391b4000
mmap(0x3c391ba000, 16992, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3c391ba000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3b2384c000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3b2384a000
arch_prctl(ARCH_SET_FS, 0x7f3b2384a740) = 0
mprotect(0x60d000, 4096, PROT_READ)     = 0
mprotect(0x3c391b4000, 16384, PROT_READ) = 0
mprotect(0x3c38c1f000, 4096, PROT_READ) = 0
munmap(0x7f3b2384d000, 245801)          = 0
brk(0)                                  = 0xfa8000
brk(0xfc9000)                           = 0xfc9000
brk(0)                                  = 0xfc9000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106070960, ...}) = 0
mmap(NULL, 106070960, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3b1d321000
close(3)                                = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0664, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
geteuid()                               = 1000
newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0664, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
faccessat(AT_FDCWD, "a", W_OK)          = 0
unlinkat(AT_FDCWD, "a", 0)              = 0
lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Dlaczego istnieją oddzielne wezwania systemowe do usunięcia katalogu i plików? Dlaczego te dwie operacje byłyby semantycznie różne?

d33tah
źródło
3
Odpowiedzi tutaj: superuser.com/questions/430313/…
jlliagre

Odpowiedzi:

9

Katalogi są wyjątkowe w tym sensie, że w katalogu można mieć odniesienia do kilku plików i katalogów, więc jeśli usuniesz katalog nadrzędny, wszystkie te pliki utracą punkt odniesienia, z którego można uzyskać do nich dostęp, podobnie jak w procesie. W takich przypadkach rmdir()stosuj inne kontrole, które różnią się od unlink():

  • Jeśli katalog nie jest pusty. Jeśli katalog nie jest pusty, nie można go usunąć, dopóki zawartość nie zostanie unlinkusunięta.

       ENOTEMPTY
          pathname contains entries other than . and .. ; or, pathname has
          ..  as its final component.  POSIX.1-2001 also allows EEXIST for
          this condition.
    
  • Jeśli katalog jest w użyciu. Jeśli proces straci swój bieżący katalog, może to prowadzić do problemów i nieokreślonych zachowań. Lepiej im zapobiegać.

       EBUSY  pathname  is currently in use by the system or some process that
          prevents its removal.  On Linux this means pathname is currently
          used  as  a  mount point or is the root directory of the calling
          process.
    

W przypadku unlink()tych kontroli nie istnieje. W rzeczywistości możesz usunąć nazwę pliku, unlink()a proces, który nadal używa / odwołuje się do niego, może go modyfikować bez problemów. Plik istnieje, dopóki nie istnieje deskryptor pliku, po prostu niedostępny dla nowego procesu (chyba że wiesz, gdzie szukać). Jest to część magii tęczowych dłoni w systemach plików * NIX.

Teraz jest coś, unlinkat()co zachowuje się jak oba, unlink()lub rmdir(2)zależy od ścieżki, której się spodziewasz.

Braiam
źródło
Dobrze rm -rf "$PWD"działa i usuwa bieżący katalog. Myślę, że powód rmdir()jest prawdopodobnie historyczny (początkowo katalogi zostały odłączone (), a rmdir (polecenie) odłączał dir /., Dir / .. i dir, a kiedy to zostało przeniesione do jądra, musiało to być nowy syscall robi wszystkie 3 przynajmniej przez okres przejściowy lub coś takiego)
Stéphane Chazelas
@ StéphaneChazelas zgadza się, dlatego dodałem unlinkat.
Braiam
Jeśli dobrze przeczytam twoją odpowiedź, mówisz, że rmdir(dir)nie działa, jeśli dirjest używana. Nie dotyczy to przynajmniej Linuksa, gdzie rmdir(getcwd())działa dobrze (pod warunkiem, że bieżący katalog jest pusty).
Stéphane Chazelas,
@ StéphaneChazelas poprawne, używane przez proces lub jako punkt montowania: EBUSY ścieżka jest obecnie używana przez system lub jakiś proces, który uniemożliwia jego usunięcie . W systemie Linux oznacza to, że ścieżka jest obecnie używana jako punkt podłączenia lub jest katalogiem głównym procesu wywołującego.
Braiam
Nie jestem pewien, co rozumieją przez lub jest katalogiem głównym procesu wywoływania . mkdir test; sudo strace -e chroot,rmdir perl -e 'chroot("test"); rmdir("test")'pokazuje, że zarówno chroot, jak i rmdir odniosły sukces.
Stéphane Chazelas,