Чи безпечно переміщувати файл, до якого додається?


28

У мене є процес node.js, який використовується fs.appendFileдля додавання рядків до file.log. Додаються лише повні рядки приблизно 40 символів на рядок, наприклад дзвінки схожі fs.appendFile("start-end"), а не 2 дзвінки, як fs.appendFile("start-")і fs.appendFile("end"). Якщо я переміщую цей файл, чи file2.logможу я бути впевненим, що жодні рядки частково не втрачені чи скопійовані?

Відповіді:


36

Поки ви не переміщуєте файл через межі файлової системи, робота повинна бути безпечною. Це пов'язано з механізмом того, як насправді робиться «переміщення».

Якщо ви mvзнаходитесь в одній файловій системі, файл насправді не торкається, а змінюється лише запис файлової системи.

$ mv foo bar

насправді робить щось подібне

$ ln foo bar
$ rm foo

Це створило б жорстке посилання (другий запис у каталозі) для файлу (власне, вкладеного пункту, вказаного файловою системою), fooназваного barта видалення fooзапису. Оскільки тепер при видаленні fooіснує друга запис файлової системи, що вказує на fooinode 's, видалення старого запису fooфактично не видаляє блоки, що належать до inode.

Ваша програма з радістю додасть файл у будь-який момент, оскільки його відкрита ручка файлу вказує на вкладення файлу, а не на запис файлової системи.

Примітка: Якщо ваша програма закриє і знову відкриє файл між записами, у вас з'явиться новий файл, створений зі старою записом файлової системи!

Перехресна файлова система рухається:

Якщо ви переміщуєте файл через межі файлової системи, все стає некрасивим. У цьому випадку ви не могли гарантувати постійне зберігання файлів, оскільки це mvбуло б насправді

  • створити новий файл у цільовій файловій системі
  • скопіюйте вміст старого файлу в новий файл
  • видаліть старий файл

або

$ cp /path/to/foo /path/to/bar
$ rm /path/to/foo

респ.

$ touch /path/to/bar
$ cat < /path/to/foo > /path/to/bar
$ rm /path/to/foo

Залежно від того, чи дійне копіювання до кінця файлу під час запису вашої заявки, може статися, що у вас в новій файлі є лише половина рядка.

Крім того, якщо ваша програма не закриє і не відкриє старий файл, він продовжує записувати до старого файлу, навіть якщо він видається видаленим: ядро ​​знає, які файли відкриті, і хоча воно видалило б запис файлової системи, воно не видалить inode старого файлу та пов’язані з ним блоки, поки ваша програма не закриє свою відкриту ручку файлу.


3
FYI, ранні версії Unix не мали rename()системного виклику. Таким чином, оригінальна версія mvфактично закликала link()створити жорстке посилання з подальшим unlink()видаленням оригінальної назви. rename()додано у FreeBSD, щоб реалізувати це атомно в ядрі.
Бармар

Вибачте, але що таке file-system borders?
laike9m

1
@ laike9m - Межі файлової системи стосуються того, що проста файлова система повинна знаходитися на одному розділі на одному пристрої пам'яті, як дисковий накопичувач. Якщо ви перейменовуєте файл у файловій системі, все, що змінюється, - це ім'я в записі каталогу. Він все ще має той самий inode - якщо він був у файловій системі на основі inode, для початку - як і більшість файлових систем Linux. Але, якщо ви перемістите файл в іншу файлову систему, фактичні дані потрібно перемістити, і файл отримає новий вклад з нової файлової системи. Це призведе до порушення будь-яких операцій над файлом, які тривали, коли це сталося.
Джо

9

Оскільки ви кажете, що ви використовуєте node.js, я припускаю, що ви б використовували fs.rename()(або fs.renameSync()) для перейменування файлів. Цей метод node.js задокументований для використання системного виклику rename (2) , який жодним чином не торкається самого файлу, а лише змінює ім'я, під яким він вказаний у файловій системі:

" перейменувати () перейменовує файл, переміщуючи його між каталогами, якщо потрібно. Будь-які інші жорсткі посилання на файл (створені за допомогою посилання (2) ) не впливають. Відкриті дескриптори файлів для oldpath також не впливають."

Зокрема, зверніть увагу на останнє речення, яке цитується вище, де сказано, що будь-які відкриті дескриптори файлів (наприклад, ваша програма використовує для запису у файл) продовжуватимуть вказувати на нього навіть після його перейменування. Таким чином, втрати даних або пошкодження не буде, навіть якщо файл буде перейменований під час одночасного запису.


Як зазначає у своїй відповіді Андреас Вайз, системний виклик перейменування (2) (і, таким чином, fs.rename()у node.js) не буде працювати через межі файлової системи. Таким чином, спроба перенести файл у іншу файлову систему таким чином просто не вдасться.

Команда Unix mvнамагається приховати це обмеження, виявивши помилку, і замість цього перемістивши файл, скопіювавши його вміст у новий файл та видаливши оригінал. На жаль, переміщення файлів , як це робить втрату даних ризиків , якщо файл буде переміщений у час його записи. Таким чином, якщо ви хочете безпечно перейменовувати файли, які можуть бути одночасно записані, ви не повинні використовувати mv(або, принаймні, слід переконатися, що новий і старий шлях знаходяться в одній файловій системі).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.