Hi Valentin!
10 Jun 15, Valentin Nechayev ==> Pavel Gulchouck:
VN>>> А вот именно для недеструктивного применений меньше. Фактически
VN>>> это ситуация соревнования за право занять некоторое имя
VN>>> с контролем возможности это сделать и сохранением статус-кво при
VN>>> невозможности; то есть что-то вроде мьютекса.
PG>> Hесколько процессов могут принимать сообщения (файлы). После окончания
PG>> приёма файл переименовывается из временного имени в окончательное для
PG>> дальнейшей обработки. Hапример, обычная запись сообщения в maildir или
PG>> в spool. Для избежания коллизий и случайного удаления почты необходимо
PG>> строить полноценный IPC с мьютексами. А если процессы, пишущие в этот
PG>> maildir, работают на разных серверах? Hедеструктивный rename избавил
PG>> бы от этих проблем.
VN> Вот тут http://www.qmail.org/qmail-manual-html/man5/maildir.html расписан,
VN> кроме формата maildir, алгоритм работы с ним, который устраняет подобные
VN> проблемы.
После link() из tmp в new файл из tmp не удаляется?
Для меня новость. И не понимаю, зачем он там нужен.
VN> Там используется несколько подпорок
VN> вроде случайных имён и суточных таймаутов на переходы между состояниями,
VN> но, насколько я знаю, явных дыр в алгоритме не
VN> находили. Понятно, что это не радикальный метод, но устраняет жёсткую
VN> необходимость в недеструктивном rename.
Для этого алгоритма необходима поддержка link().
Да, если файловая система поддерживает хардлинки, без safe_rename() обычно
можно обойтись. Хотя даже тут возможны нюансы. Например, на NFS - если ответ об
успешном выполнении link потерялся, повторная попытка вернёт EEXIST, и доставка
будет признана неудачной, хотя на самом деле сообщение будет доставлено. То
есть, получится дубликат. Но там и с safe_rename() возможны подобные проблемы,
так что это не принципиально.
Но что делать без safe_rename(), если хардлинки не поддерживаются?
VN>>> Hо при
VN>>> возможности работы с локальной FS это можно делать и другими
VN>>> методами: например, открыть целевой путь и, убедившись
VN>>> в успешности, выполнить привычный rename(). Да, если между ними
VN>>> оборвать, останется мусорный файл; и против этого есть методы
VN>>> (открывать его с правами 000 и проверять это странное состояние);
VN>>> открывать промежуточный файл дороже; но оно таки работает.
PG>> Если без проверки на mode 000, то можем получить следующее.
PG>> 1. Процесс P1 создал файл F нулевой длины.
PG>> 2. Процесс R (обработчик) увидел файл F, обработал его и удалил.
PG>> Допустим, при этом ничего плохого не произошло - файл нулевой длины
PG>> был проигнорирован. 3. Процесс P2 создал файл F нулевой длины. 4.
PG>> Процесс P1 переименовал свои данные в файл F. 5. Процесс P2
PG>> переименовал свои данные в файл F, затерев данные процесса P1.
PG>> Чтобы этого не произошло, необходимо в обработчике смотреть не только
PG>> на наличие файла, но и на какие-то дополнительные признаки - размер
PG>> или права доступа или открыт ли он кем-то или что-то ещё. Hо это
PG>> делает невозможным применение обычной и распространённой схемы "нашли
PG>> файл - обрабатываем". Если же в обработчике смотреть на всякие
PG>> дополнительные признаки (например, в MUA - на права доступа файлов в
PG>> maildir), то можно сразу писать в окончательное имя с mode 000 без
PG>> всяких временных файлов, а после окончания приёма делать chmod. Это
PG>> возможное, но на мой взгляд не самое элегантное решение. Я не видел,
PG>> чтобы оно где-то применялось (а приём во временное имя с последующим
PG>> переименованием - сплошь и рядом).
VN> Или временным каталогом. Или суффиксом к имени "меня пока что не трогать".
Так в том-то и проблема, что временный каталог или суффикс не поможет. Потому
что как переименовать из временного каталога в постоянный или убрать суффикс,
случайно не удалив при этом ничего лишнего?
VN>>> Какой ещё юзкейс на подобную функциональность ты предложишь?
PG>> Я думаю, что во многих случаях перед rename() проверяется отсутствие
PG>> dst-файла. Даже при обычном создании нового документа. И это значит,
PG>> что запрос "override?" будет задан в большинстве случаев, но не всегда
PG>> - иногда этот override может быть сделан молча и случайно. Hаличие
PG>> атомарного safe_rename() избавило бы от этих проблем.
PG>> И ведь всякие linkat(), localtime_r(), fseeko() и прочие менее
PG>> необходимые (на мой взгляд) функции реализовали и добавили, почему ж
PG>> safe_rename() нигде нет? Как и атомарного tmpfile(), и flink(int fd,
PG>> const char *newpath), отсутствию которых я уже как-то удивлялся.
VN> Тут таки надо по пунктам.
VN> *at() это неизбежно для поддержки многонитевости (если текущий каталог
VN> остаётся общим на процесс, а не нить) и исключения
VN> дорогих ненужных namei lookups на каждый чих. Хотя я бы добавил к ним ещё
VN> walk flags, например, со следующими флагами:
VN> WF_NOFOLLOWLAST (аналогично нынешнему O_NOFOLLOW у open(2)) WF_NOFOLLOWMID
VN> (все симлинки не допускать) WF_ONESTEP (имя в
VN> path считается как одна компонента, независимо от '/' в ней) WF_FSPREFIX
VN> (в начале проверяется "${fs}:" аналогично именам
VN> дисков в RSX-11; набор стандартных имён fs позволяет уйти от проблем типа
VN> управления частичным маппингом devfs внутрь
VN> chroot/jail/LXC)
Неизбежно для исключения дорогих ненужных namei lookups - вот как-то не
убеждает. Если эффективность упирается в файловые операции, причём не
чтение/запись, а link/unlink/rename/open при namei lookups, то нужно менять
дизайн. Например, вместо FS использовать DB.
VN> *_r() это та же проблема исправления дефектов заточки под однонитевость.
Эта проблема обходится мьютексом проще, чем отсутствие safe_rename() -
хардлинками, локами, специальными правами доступа, уникальными именами,
суточными таймаутами и пр.
VN> fseeko() это проблема изначальной ошибки в размере предшественника off_t.
VN> Местами это до сих пор стреляет в виде необходимости всяких
VN> LARGEFILE64_SOURCE.
Тоже не вижу проблемы. Разве она не решается макросом типа такого?
#define correct_fseek(stream, offset, whence) \
(fflush(stream) || (lseek(fileno(stream), offset, whence) == -1 ? -1 : 0))
VN> У них всех не было внятного обхода проблемы (не считать же им, например,
VN> мьютекс вокруг chdir() или прыжков не более чем
VN> по 2**31-1 байт, при этом не имея возможности надёжно узнать текущую
VN> позицию).
Мне ни разу не приходилось использовать функции *at(), не возникало в них
потребности, возможно, поэтому я не чувствую их необходимость.
VN> У safe rename твоего стиля - скорее есть обход, чем нет. Пока что мы не
VN> нашли ни одного случая, когда совсем уж нельзя
VN> исправить дизайном...
Но ведь и реализовать-то, как мне кажется, должно быть нетрудно.
Или там проблема в том, что нужно менять требования к FS API?
Lucky carrier,
Паша
aka ***@gul.kiev.ua