Пока вы не перемещаете файл за границы файловой системы, операция должна быть безопасной. Это связано с механизмом того, как на самом деле выполняется «перемещение».
Если вы 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, чтобы реализовать это атомарно в ядре.