Как переименовать () без условий состязания?

Если я хочу переименовать A кому: B, но только если B не существует, наивная вещь проверила бы если B существует (с access("B", F_OK) или что-то как этот), и если это не делает продолжения rename. К сожалению, это открывает окно, во время которого некоторый другой процесс мог бы решить создать B, и затем это перезаписывается - и еще хуже нет никакого признака, что что-то как этот когда-либо происходило.

Другие функции доступа к файловой системе не страдают от этого - open имеет O_EXCL (настолько копирующие файлы безопасны), и недавно Linux получил все семейство *at syscalls, которые защищают от большинства других условий состязания - но не этот конкретный один (renameat существует, но защищает от совершенно другой проблемы).

Это имеет решение?

7
задан taw 11 July 2010 в 08:14
поделиться

3 ответа

Вы можете link() на существующий файл с новым именем, которое вам нужно, а затем удалить существующее имя.

link() должен успешно создать новую ссылку, только если новое имя пути еще не существует.

Что-то вроде:

int result = link( "A", "B");

if (result != 0) {
    // the link wasn't created for some reason (maybe because "B" already existed)
    // handle the failure however appropriate...
    return -1;
}

// at this point there are 2 filenames hardlinked to the contents of "A", 
//   filename "A" and filename "B"

// remove filename "A"
unlink( "A");

Эта техника обсуждается в документации к link() (см. обсуждение изменения файла passwd):

7
ответ дан 6 December 2019 в 06:35
поделиться

У вас должна получиться ссылка(2) на новое имя файла. Если ссылка не работает, то вы сдаетесь, потому что файл уже существует. Если ссылка удалась, ваш файл теперь существует и под старым, и под новым именем. Затем вы отсоединяете(2) старое имя. Нет возможного состояния гонки.

15
ответ дан 6 December 2019 в 06:35
поделиться

Из man-страницы rename:

Если newpath уже существует, он будет атомарно заменен (при соблюдении нескольких условий; см. ОШИБКИ ниже), так что не существует точки, в которой другой процесс, пытающийся получить доступ к newpath обнаружит его отсутствие.

Поэтому невозможно избежать переименования, когда файл B уже существует. Я думаю, что, возможно, у вас просто нет выбора, кроме как проверить существование (используйте stat(), а не access() для этого) перед попыткой переименования, если вы не хотите, чтобы переименование произошло, если файл уже существует. Игнорирование состояния гонки.

В противном случае представленное ниже решение с использованием link(), похоже, соответствует вашим требованиям.

1
ответ дан 6 December 2019 в 06:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: