Пока вы не перемещаете файл за границы файловой системы, операция должна быть безопасной. Это связано с механизмом того, как на самом деле выполняется «перемещение».
Если вы mv
файл в той же файловой системе, файл фактически не затрагивается, а изменяется только запись файловой системы.
$ mv foo bar
на самом деле делает что-то вроде
$ ln foo bar
$ rm foo
Это позволит создать жесткую ссылку (вторая запись каталога) для файла ( на самом деле индексный дескриптор указываемого записи файловой системы) с foo
именем bar
и удалить foo
запись. Поскольку теперь при удалении foo
существует вторая запись файловой системы, указывающая на foo
inode, удаление старой записи 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
В зависимости от того, достигает ли конец файла конца записи во время записи приложения, может случиться так, что в новом файле будет только половина строки.
Кроме того, если ваше приложение не закроет и не откроет старый файл, оно продолжит запись в старый файл, даже если он кажется удаленным: ядро знает, какие файлы открыты, и хотя оно удалит запись файловой системы, оно не удалит инод старого файла и связанные с ним блоки, пока ваше приложение не закроет свой дескриптор открытого файла.
rename()
системного вызова. Таким образом, оригинальная версияmv
действительно требовалаlink()
создать жесткую ссылку, а затемunlink()
удалить оригинальное имя.rename()
был добавлен во FreeBSD, чтобы реализовать это атомарно в ядре.file-system borders
?Поскольку вы говорите, что используете node.js, я предполагаю, что вы будете использовать
fs.rename()
(илиfs.renameSync()
) для переименования файлов. Этот метод node.js документирован для использования системного вызова rename (2) , который никоим образом не затрагивает сам файл, а просто меняет имя, под которым он указан в файловой системе:В частности, обратите внимание на последнее предложение, приведенное выше, в котором говорится, что любые дескрипторы открытого файла (например, ваша программа будет использовать для записи в файл) будут продолжать указывать на него даже после того, как он был переименован. Таким образом, не будет потери или повреждения данных, даже если файл будет переименован во время одновременной записи.
Как отмечает в своем ответе Андреас Вайз, системный вызов rename (2) (и, следовательно,
fs.rename()
в файле node.js) не будет работать через границы файловой системы. Таким образом, попытка переместить файл в другую файловую систему таким способом просто потерпит неудачу.Команда Unix
mv
пытается скрыть это ограничение, обнаруживая ошибку и, вместо этого, перемещая файл, копируя его содержимое в новый файл и удаляя оригинал. К сожалению, перемещение таких файлов может привести к потере данных, если файл будет перемещен во время записи. Таким образом, если вы хотите безопасно переименовать файлы, в которые можно одновременно записывать, вы не должны их использоватьmv
(или, по крайней мере, вы должны быть абсолютно уверены, что новый и старый пути находятся в одной файловой системе).источник