RENAME(2) Руководство программиста Linux RENAME(2)

ИМЯ

rename, renameat, renameat2 - изменяет имя или расположение файла

ОБЗОР

#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
#include <fcntl.h>           /* определения констант AT_* */
#include <stdio.h>
int renameat(int olddirfd, const char *oldpath,
             int newdirfd, const char *newpath);
int renameat2(int olddirfd, const char *oldpath,
              int newdirfd, const char *newpath, unsigned int flags);

Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

renameat():

Начиная с glibc 2.10:
_POSIX_C_SOURCE >= 200809L
До glibc 2.10:
_ATFILE_SOURCE

renameat2():

_GNU_SOURCE

ОПИСАНИЕ

Вызов rename() переименовывает файл и, если требуется, перемещает его из одного каталога в другой. Все прочие жёсткие ссылки на файл (созданные с помощью link(2)), не изменяются. Открытые файловые дескрипторы на oldpath также не изменяются.

На успешность выполнения операции переименования влияют различные ограничения: смотрите ОШИБКИ далее.

Если newpath уже существует, то он будет атомарно перезаписан так, что другой процесс, пытающийся в этот момент обратиться к newpath, не сможет определить его временное отсутствие. Однако будет промежуток времени, когда oldpath и newpath указывают на один файл.

Если oldpath и newpath являются жёсткими ссылками на один и тот же файл, то rename() ничего не делает и возвращает код успешного выполнения.

Если newpath существует, но операция завершается ошибкой, то rename() гарантирует, что newpath останется нетронутым.

В oldpath может быть указан каталог. В этом случае каталог в newpath должен или не существовать, или должен быть пуст.

Если oldpath является символьной ссылкой, то она переименовывается; если newpath является символьной ссылкой, то будет вновь записан файл, на который она указывает.

Системный вызов renameat() работает также как системный вызов rename(), за исключением случаев, описанных далее.

Если в oldpath указан относительный путь, то он считается относительно каталога, на который ссылается файловый дескриптор olddirfd (а не относительно текущего рабочего каталога вызывающего процесса, как это делается в rename()).

Если в oldpath указан относительный путь и olddirfd равно специальному значению AT_FDCWD, то oldpath рассматривается относительно текущего рабочего каталога вызывающего процесса (как rename()).

Если в oldpath указан абсолютный путь, то olddirfd игнорируется.

Значение newpath интерпретируется как oldpath, за исключением того, что относительный путь интерпретируется относительно каталога, на который ссылается файловый дескриптор newdirfd.

Смотрите в openat(2) объяснение необходимости renameat().

Вызов renameat2() имеет дополнительный аргумент flags. Если значение flags равно нулю, то renameat2() эквивалентен renameat().

Аргумент flags является битовой маской, состоящей из нуля или более следующих флагов:

Атомарно обменять oldpath и newpath. Оба пути должны существовать, но могут быть различных типов (например, один может быть непустым каталогом, а другой символической ссылкой).
Не перезаписывать newpath. Возвращать ошибку, если newpath уже существует.
RENAME_NOREPLACE не может быть применен вместе с RENAME_EXCHANGE.
Для RENAME_NOREPLACE требуется поддержка в файловой системе; она есть только у нескольких файловых систем:
  • ext4 (начиная с Linux 3.15);
  • btrfs, shmem, и cifs (начиная с Linux 3.17);
  • xfs (начиная с Linux 4.0);
  • Поддержка для других файловых систем была добавлена в Linux 4.9: etx2, minix, reiserfs, jfs, vfat и bpf.
Эта операция применима только для реализаций оверлейных/объединённых файловых систем.
При указании RENAME_WHITEOUT для источника переименования одновременно с выполнением переименования создаётся «замазанный» объект (whiteout object). Вся операция атомарна, и при успешном выполнении переименования создаётся и замазка.
«Замазка» — это объект, имеющий специальное предназначение в оверлейных/объединённых файловых системах. В них существует несколько слоёв, и для изменения доступен только верхний. Замазка на верхнем слое эффективно скрывает файл из нижнего слоя, и кажется, что файл не существует.
Когда переименовывается файл, существующий в нижнем слое, то первым действием он переписывается (если его уже нет в верхнем слое) и переименовывается в верхнем, доступном на чтение-запись слое. Одновременно с этим, исходный файл требуется «замазать» (что исходный файл в нижнем слое отражался невидимым). Вся операция должна выполняться атомарно.
При отсутствии объединения/оверлея замазка появляется в виде символьного устройства с номером устройства {0,0} (заметим, что в других реализациях объединения/оверлея могут использоваться другие методы хранения элементов замазки; в частности, в BSD для объединения монтирования используется специальный тип иноды, DT_WHT, который, хотя и поддерживается некоторыми файловыми системами, доступными в Linux, например CODA и XFS, но игнорируется кодом поддержки замазки в ядре, по крайней мере, в Linux 4.19).
Для RENAME_WHITEOUT требуются те же права, что и для создания узла устройства (т. е., мандата CAP_MKNOD).
RENAME_WHITEOUT не может быть применён вместе с RENAME_EXCHANGE.
Для RENAME_WHITEOUT требуется поддержка в файловой системе. Такая поддержка имеется в tmpfs (начиная с Linux 3.18), ext4 (начиная с Linux 3.18), XFS (начиная с Linux 4.1), f2fs (начиная с Linux 4.2), btrfs (начиная с Linux 4.7) и ubifs (начиная с Linux 4.9).

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении возвращается 0. В случае ошибки возвращается -1, а errno устанавливается в соответствующее значение.

ОШИБКИ

Запись в каталог, содержащий oldpath или newpath, запрещена, или в одном из каталогов пути oldpath или newpath нельзя производить поиск, или oldpath является каталогом, в который запрещена запись (требует обновления элемента ..); смотрите также path_resolution(7)).
Переименование завершилось неудачно, так как oldpath или newpath является каталогом, который используется другим процессом (возможно в качестве текущего рабочего каталога или в качестве корневого каталога, или он открыт на чтение), или используется системой (например, в качестве точки монтирования), и система считает это ошибкой (заметим, что нет требования возвращать EBUSY в таких случаях — нет ничего неправильного в таком переименовании — но разрешается возвращать EBUSY, если система не может иначе обработать такие ситуации).
Исчерпана пользовательская квота на дисковые блоки файловой системы.
Значения oldpath и newpath указывают за пределы доступного адресного пространства.
Новый путь содержит префикс старого пути или, в более общем смысле, выполняется попытка сделать каталог подкаталогом самого себя.
Каталог newpath уже существует, но oldpath не является каталогом.
Во время определения oldpath или newpath встретилось слишком много символьных ссылок.
В oldpath уже имеется максимальное количество ссылок, или каталог, содержащий newpath, уже имеет максимальное количество ссылок.
Слишком длинное значение аргумента oldpath или newpath.
Ссылка, на которую ссылается oldpath, не существует; компонент каталога в newpath не существует; в oldpath или newpath указана пустая строка.
Недостаточное количество памяти ядра.
На устройстве, содержащем файл, нет места для создания нового элемента каталога.
Компонент, используемый как каталог в oldpath или newpath, в действительности не является каталогом. Или oldpath является каталогом и существует newpath, который не является каталогом.
Значение newpath является непустым каталогом, то есть содержит элементы, отличные от «.» и «..».
Каталог, содержащийся в oldpath, имеет закрепляющий бит (S_ISVTX) и эффективный идентификатор процесса не совпадает с идентификатором пользователя удаляемого файла или каталога, его содержащего, и процесс не имеет прав (Linux: нет мандата CAP_FOWNER); или newpath является существующим файлом и каталог, содержащий его, имеет закрепляющий бит и эффективный идентификатор процесса не совпадает с идентификатором пользователя замещаемого файла или каталога, его содержащего, и процесс не имеет прав (Linux: нет мандата CAP_FOWNER); или файловая система, содержащая pathname, не поддерживает переименования запрашиваемого типа.
Файл расположен в файловой системе, доступной только для чтения.
Элементы oldpath и newpath находятся не в одной смонтированной файловой системе (Linux позволяет монтировать файловую систему в несколько точек, но rename() не работает с различающимися точками монтирования, даже если в них смонтирована идентичная файловая система).

В renameat() и renameat2() дополнительно могут возникнуть следующие ошибки:

Значение olddirfd или newdirfd не является правильным файловым дескриптором.
Значение oldpath содержит относительный путь и olddirfd содержит файловый дескриптор, указывающий на файл, а не на каталог; или произошло тоже самое с newpath и newdirfd.

В renameat2() дополнительно могут возникнуть следующие ошибки:

Значение flags содержит RENAME_NOREPLACE, а newpath уже существует.
В flags указан неверный флаг.
В flags указаны оба флага, RENAME_NOREPLACE и RENAME_EXCHANGE.
В flags указаны оба флага, RENAME_WHITEOUT и RENAME_EXCHANGE.
Файловая система не поддерживает один из флагов в flags.
В flags содержится RENAME_EXCHANGE, но newpath не существует.
В flags указан флаг RENAME_WHITEOUT , но вызывающий не имеет мандата CAP_MKNOD.

ВЕРСИИ

Системный вызов renameat() был добавлен в ядро Linux версии 2.6.16; поддержка в glibc доступна с версии 2.4.

Вызов renameat2() был добавлен в Linux 3.15; поддержка в glibc доступна с версии 2.28.

СООТВЕТСТВИЕ СТАНДАРТАМ

rename(): 4.3BSD, C89, C99, POSIX.1-2001, POSIX.1-2008.

renameat(): POSIX.1-2008.

Вызов renameat2() есть только в Linux.

ЗАМЕЧАНИЯ

Замечания по glibc

В старых ядрах, где renameat() отсутствует, обёрточная функция glibc использует rename(). Если oldpath и newpath являются относительными путями, то glibc собирает пути относительно символической ссылки в /proc/self/fd, которая соответствует аргументам olddirfd и newdirfd.

ДЕФЕКТЫ

При работе с файловыми системами NFS нельзя считать, что если операция завершилась неудачно, то имя файла не изменилось. Если сервер производит операцию переименования, а затем аварийно останавливает свою работу, то перепосланный пакет RPC будет вновь обработан при восстановлении работы сервера, что вызовет сообщение об ошибке. Приложение в этой ситуации должно работать корректно. Смотрите link(2), где описывается подобная проблема.

СМОТРИТЕ ТАКЖЕ

mv(1), chmod(2), link(2), symlink(2), unlink(2), path_resolution(7), symlink(7)

2019-03-06 Linux