Czy bezpiecznie jest przenieść plik, do którego jest dołączany?


28

Mam proces node.js, który używa fs.appendFiledo dodawania wierszy file.log. Dołączane są tylko pełne linie po około 40 znaków na linie, np. Połączenia są podobne fs.appendFile("start-end"), a nie 2 połączenia jak fs.appendFile("start-")i fs.appendFile("end"). Jeśli przeniosę ten plik, file2.logczy mogę mieć pewność, że żadne linie nie zostaną zgubione lub skopiowane częściowo?

Odpowiedzi:


36

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 mvplik 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 foonazwie bari usunięcie foowpisu. Ponieważ teraz podczas usuwania foojest drugi wpis systemu plików wskazujący na fooi-węzeł, usunięcie starego wpisu footak 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ż mvtak 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.


3
Do Twojej wiadomości, wczesne wersje Uniksa nie miały rename()wywołania systemowego. Oryginalna wersja mvfaktycznie wywołała połączenie link()twarde, a następnie unlink()usunęła pierwotną nazwę. rename()został dodany w FreeBSD, aby zaimplementować to atomowo w jądrze.
Barmar

Przepraszam ale co to jest file-system borders?
laike9m

1
@ laike9m - Granice systemu plików odnoszą się do faktu, że prosty system plików musi znajdować się na jednej partycji na jednym urządzeniu pamięci, takim jak napęd dyskowy. Jeśli zmienisz nazwę pliku w systemie plików, zmieni się tylko nazwa we wpisie katalogu. Nadal ma ten sam i-węzeł - jeśli był w systemie plików opartym na i-węzłach na początek - jak większość systemów plików Linux. Ale jeśli plik zostanie przeniesiony do innego systemu plików, rzeczywiste dane muszą zostać przeniesione, a plik otrzyma nowy i-węzeł z nowego systemu plików. Spowodowałoby to zakłócenie wszelkich operacji na pliku, które były w toku, gdy to miało miejsce.
Joe

9

Ponieważ mówisz, że używasz node.js, zakładam, że użyjesz fs.rename()(lub fs.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:

rename () zmienia nazwę pliku, przenosząc go między katalogami, jeśli jest to wymagane. Nie ma to wpływu na inne twarde łącza do pliku (utworzone za pomocą link (2) ). Nie ma to również wpływu na otwarte deskryptory plików dla starej ścieżki ”.

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 mvpró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).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.