Tak długo, jak nie przenosisz pliku poza granice systemu plików, operacja powinna być bezpieczna. Wynika to z mechanizmu, w jaki sposób „przenoszenie” jest faktycznie wykonywane.
Jeśli mv
plik znajduje się w tym samym systemie plików, plik nie jest tak naprawdę dotykany, ale zmieniany jest tylko wpis w systemie plików.
$ mv foo bar
faktycznie robi coś takiego
$ ln foo bar
$ rm foo
Spowoduje to utworzenie twardego łącza (drugiego wpisu katalogu) dla pliku (w rzeczywistości i-węzła wskazywanego przez wpis w systemie plików) o foo
nazwie bar
i usunięcie foo
wpisu. Ponieważ teraz podczas usuwania foo
jest drugi wpis systemu plików wskazujący na foo
i-węzeł, usunięcie starego wpisu foo
tak naprawdę nie usuwa żadnych bloków należących do i-węzła.
Twój program i tak chętnie dołączy do pliku, ponieważ jego otwarty uchwyt pliku wskazuje na i-węzeł pliku, a nie pozycję systemu plików.
Uwaga: Jeśli twój program zamyka się i ponownie otwiera plik między zapisami, skończyłoby się to utworzeniem nowego pliku przy użyciu starego wpisu systemu plików!
Przenoszenie między systemami plików:
Jeśli przeniesiesz plik poza granice systemu plików, sytuacja stanie się brzydka. W takim przypadku nie można zagwarantować, że plik będzie spójny, ponieważ mv
tak naprawdę byłoby
- utwórz nowy plik w docelowym systemie plików
- skopiuj zawartość starego pliku do nowego pliku
- usuń stary plik
lub
$ cp /path/to/foo /path/to/bar
$ rm /path/to/foo
odpowiednio
$ touch /path/to/bar
$ cat < /path/to/foo > /path/to/bar
$ rm /path/to/foo
W zależności od tego, czy kopiowanie osiąga koniec pliku podczas zapisu aplikacji, może się zdarzyć, że w nowym pliku jest tylko połowa wiersza.
Dodatkowo, jeśli twoja aplikacja nie zamknie się i nie otworzy ponownie starego pliku, kontynuuje zapisywanie do starego pliku, nawet jeśli wydaje się, że został usunięty: jądro wie, które pliki są otwarte i chociaż usunie pozycję systemu plików, nie usunie i-węzła starego pliku i powiązanych bloków, dopóki aplikacja nie zamknie otwartego uchwytu pliku.
rename()
wywołania systemowego. Oryginalna wersjamv
faktycznie wywołała połączenielink()
twarde, a następnieunlink()
usunęła pierwotną nazwę.rename()
został dodany w FreeBSD, aby zaimplementować to atomowo w jądrze.file-system borders
?Ponieważ mówisz, że używasz node.js, zakładam, że użyjesz
fs.rename()
(lubfs.renameSync()
) do zmiany nazw plików. Ta metoda node.js jest udokumentowana w celu użycia wywołania systemowego rename (2) , które nie dotyka samego pliku w żaden sposób, a jedynie zmienia nazwę, pod którą jest on wymieniony w systemie plików:W szczególności zwróć uwagę na ostatnie zdanie cytowane powyżej, które mówi, że wszelkie otwarte deskryptory plików (takie jak program używałby do zapisu do pliku) będą nadal wskazywały na to, nawet po zmianie jego nazwy. W ten sposób nie nastąpi utrata lub uszkodzenie danych, nawet jeśli nazwa pliku zostanie zmieniona podczas jednoczesnego zapisu.
Jak zauważa Andreas Weise w swojej odpowiedzi , wywołanie systemowe zmiany nazwy (2) (a tym samym
fs.rename()
w node.js) nie będzie działać ponad granicami systemu plików. W ten sposób próba przeniesienia pliku do innego systemu plików w ten sposób po prostu zakończy się niepowodzeniem.Komenda Unix
mv
próbuje ukryć to ograniczenie, wykrywając błąd, a zamiast tego przenosząc plik, kopiując jego zawartość do nowego pliku i usuwając oryginał. Niestety, przenoszenie plików jak to robi utracie danych ryzyko, jeśli plik jest przesuwany, gdy jest on zapisywany. Tak więc, jeśli chcesz bezpiecznie zmieniać nazwy plików, które mogą być jednocześnie na piśmie, należy nie używaćmv
(lub przynajmniej powinien mieć całkowitą pewność, że nowe i stare ścieżki są na tym samym systemie plików).źródło