Discussion:
rename()
(слишком старое сообщение для ответа)
Pavel Gulchouck
2015-06-07 10:37:12 UTC
Permalink
Привет.

Есть ли какие-то объективные причины, почему до сих пор никто и ни в каком из
юниксов (насколько мне известно) не реализовал атомарное недеструктивное
переименование файла?

Вариант "link(src, dst) || unlink(src)", во-первых, неатомарен, а во-вторых, не
все файловые системы поддерживают хардлинки. Неужели только меня смущает
отсутствие возможности безопасно переименовать файл, скажем, на флешке?

В конце концов, как реализован rename в dosemu?

Lucky carrier,
Паша
aka ***@gul.kiev.ua
Valentin Nechayev
2015-06-07 13:11:30 UTC
Permalink
Hi,
PG> Есть ли какие-то объективные причины, почему до сих пор никто и ни в
PG> каком из юниксов (насколько мне известно) не реализовал атомарное
PG> недеструктивное переименование файла?

А что значит "недеструктивное"?

PG> Вариант "link(src, dst) || unlink(src)",

http://linux.die.net/man/2/rename
атомарно, если FS поддерживает.

http://www.freebsd.org/cgi/man.cgi?query=rename&sektion=2
то же самое.

https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPage
s_iPhoneOS/man2/rename.2.html
то же самое.

То ли ты не о том спрашиваешь, то ли ты недочитал.

Меня тут единственное что смущает - <stdio.h> - какой-то неадекватный
вариант размещения. Оно лучше ложится в <fcntl.h> или <unistd.h>.

PG> во-первых, неатомарен, а
PG> во-вторых, не все файловые системы поддерживают хардлинки. Hеужели
PG> только меня смущает отсутствие возможности безопасно переименовать
PG> файл, скажем, на флешке?

А ты проверял? Главное, чтобы драйвер fat умел соответствующее.
Hасколько я вижу по сырцам Linux и FreeBSD, они оба умеют такую поддержку
в структуре операций VFS.


-netch-

... Встрял перст в ноздрь...
Pavel Gulchouck
2015-06-07 13:33:00 UTC
Permalink
Hi Valentin!

07 Jun 15, Valentin Nechayev ==> Pavel Gulchouck:

PG>> Есть ли какие-то объективные причины, почему до сих пор никто и ни в
PG>> каком из юниксов (насколько мне известно) не реализовал атомарное
PG>> недеструктивное переименование файла?

VN> А что значит "недеструктивное"?

Значит, гарантированно ничего не уничтожающий.
Как, например, в dos и unix - если dst уже существует, завершиться с ошибкой
EEXIST. Предположу, что атомарный недеструктивный rename нужен чаще, чем
атомарный деструктивный. Собственно, я даже сходу не могу придумать, в каком
случае нужен именно атомарный деструктивный (почему может не подходить
"unlink(dst); safe_rename(src, dst)"), но есть много вариантов, когда нужен
атомарный недеструктивный.

Пусть когда-то давным-давно из каких-то соображений сделали rename()
деструктивным, прописали это в POSIX, и изменить это уже нельзя. Но ведь можно
сделать дополнительную функцию, реализующую недеструктивный rename()?

VN> То ли ты не о том спрашиваешь, то ли ты недочитал.

Смысл моего вопроса именно в слове "недеструктивное".

VN> Меня тут единственное что смущает - <stdio.h> - какой-то неадекватный
VN> вариант размещения. Оно лучше ложится в <fcntl.h> или <unistd.h>.

Хм, не обращал внимания. В самом деле странное место.
Но это не столь принципиально. "Это трудно понять, но нужно запомнить".

PG>> Hеужели только меня смущает отсутствие возможности безопасно
PG>> переименовать файл, скажем, на флешке?

VN> А ты проверял?

Я не вижу средств для того, чтобы это проверить.
Вызов rename() в соответствии с документацией может молча снести dst-файл, если
он существует, и даже никак не сообщит об этом.

Lucky carrier,
Паша
aka ***@gul.kiev.ua
Pavel Gulchouck
2015-06-07 13:50:44 UTC
Permalink
Hi Valentin!

07 Jun 15, Pavel Gulchouck ==> Valentin Nechayev:

PG>>> Есть ли какие-то объективные причины, почему до сих пор никто и ни в
PG>>> каком из юниксов (насколько мне известно) не реализовал атомарное
PG>>> недеструктивное переименование файла?

VN>> А что значит "недеструктивное"?

PG> Значит, гарантированно ничего не уничтожающий.
PG> Как, например, в dos и unix - если dst уже существует, завершиться с
PG> ошибкой EEXIST.

Сорри, оговорился - имелось ввиду, "в dos и windows".

Lucky carrier,
Паша
aka ***@gul.kiev.ua
Valentin Nechayev
2015-06-07 14:12:30 UTC
Permalink
Hi,
VN>> А что значит "недеструктивное"?
PG> Значит, гарантированно ничего не уничтожающий.
PG> Как, например, в dos и unix - если dst уже существует, завершиться с
PG> ошибкой EEXIST. Предположу, что атомарный недеструктивный rename нужен
PG> чаще, чем атомарный деструктивный. Собственно, я даже сходу не могу
PG> придумать, в каком случае нужен именно атомарный деструктивный (почему
PG> может не подходить "unlink(dst); safe_rename(src, dst)"), но есть
PG> много вариантов, когда нужен атомарный недеструктивный.

Случай применения атомарного деструктивного rename, по-моему, очевиден и
общеизвестен: это замена существующего файла в процессе апгрейда или иного
обновления - например, замены конфига.
Апгрейд пакета с заменой библиотек: до замены одна версия, после - другая,
и нет момента, когда такой библиотеки просто нет.
Аналогично с бинарником и конфигом.

А вот именно для недеструктивного применений меньше. Фактически это ситуация
соревнования за право занять некоторое имя с контролем возможности это сделать
и сохранением статус-кво при невозможности; то есть что-то вроде мьютекса.
Hо при возможности работы с локальной FS это можно делать и другими методами:
например, открыть целевой путь и, убедившись в успешности, выполнить привычный
rename(). Да, если между ними оборвать, останется мусорный файл; и против этого
есть методы (открывать его с правами 000 и проверять это странное состояние);
открывать промежуточный файл дороже; но оно таки работает.
Тут даже лока на файл не нужно, хотя с ним удобнее.

Какой ещё юзкейс на подобную функциональность ты предложишь?

PG> Смысл моего вопроса именно в слове "недеструктивное".

Теперь понятно.


-netch-

... И этот парашютист задолбал...
Pavel Gulchouck
2015-06-07 14:29:40 UTC
Permalink
Hi Valentin!

07 Jun 15, Valentin Nechayev ==> Pavel Gulchouck:

VN> Случай применения атомарного деструктивного rename, по-моему, очевиден и
VN> общеизвестен: это замена существующего файла в
VN> процессе апгрейда или иного обновления - например, замены конфига. Апгрейд
VN> пакета с заменой библиотек: до замены одна
VN> версия, после - другая, и нет момента, когда такой библиотеки просто нет.
VN> Аналогично с бинарником и конфигом.

Да, согласен, нередко бывает, что нужна либо старая версия файла, либо новая,
но не его отсутствие.
Хотя при обновлении более частый случай - необходимость атомарного
переименования группы файлов или целой директории. Атомарный rename ничем не
поможет, если старый бинарник не может работать с новым конфигом или наоборот.
Тут, собственно, вообще транзакции нужны, но это уже немного другой вопрос -
отсутствие транзакций объяснить проще.
То, что ошибка (например, отключение питания) в процессе обновления может
привести к неработоспособности обновлявшегося в этот момент софта, никого не
удивит, несмотря на атомарный rename и журналируемую FS. И к тому, что за время
обновления, например, перлового модуля mrtg может ругнуться на ошибку об
отсутствии этого модуля, все тоже относятся спокойно - понятно, что update
часто по сути состоит из deinstall + install.

VN> А вот именно для недеструктивного применений меньше. Фактически это
VN> ситуация соревнования за право занять некоторое имя с
VN> контролем возможности это сделать и сохранением статус-кво при
VN> невозможности; то есть что-то вроде мьютекса.

Несколько процессов могут принимать сообщения (файлы). После окончания приёма
файл переименовывается из временного имени в окончательное для дальнейшей
обработки. Например, обычная запись сообщения в maildir или в spool.
Для избежания коллизий и случайного удаления почты необходимо строить
полноценный IPC с мьютексами. А если процессы, пишущие в этот maildir, работают
на разных серверах?
Недеструктивный rename избавил бы от этих проблем.

VN> Hо при
VN> возможности работы с локальной FS это можно делать и другими методами:
VN> например, открыть целевой путь и, убедившись в
VN> успешности, выполнить привычный rename(). Да, если между ними оборвать,
VN> останется мусорный файл; и против этого есть
VN> методы (открывать его с правами 000 и проверять это странное состояние);
VN> открывать промежуточный файл дороже; но оно таки
VN> работает.

Если без проверки на mode 000, то можем получить следующее.

1. Процесс P1 создал файл F нулевой длины.
2. Процесс R (обработчик) увидел файл F, обработал его и удалил. Допустим, при
этом ничего плохого не произошло - файл нулевой длины был проигнорирован.
3. Процесс P2 создал файл F нулевой длины.
4. Процесс P1 переименовал свои данные в файл F.
5. Процесс P2 переименовал свои данные в файл F, затерев данные процесса P1.

Чтобы этого не произошло, необходимо в обработчике смотреть не только на
наличие файла, но и на какие-то дополнительные признаки - размер или права
доступа или открыт ли он кем-то или что-то ещё. Но это делает невозможным
применение обычной и распространённой схемы "нашли файл - обрабатываем".
Если же в обработчике смотреть на всякие дополнительные признаки (например, в
MUA - на права доступа файлов в maildir), то можно сразу писать в окончательное
имя с mode 000 без всяких временных файлов, а после окончания приёма делать
chmod. Это возможное, но на мой взгляд не самое элегантное решение. Я не видел,
чтобы оно где-то применялось (а приём во временное имя с последующим
переименованием - сплошь и рядом).

VN> Тут даже лока на файл не нужно, хотя с ним удобнее.

Учитывая, что лок нельзя установаить одновременно с open(), принципиальных
удобств они не добавляют, т.к. между open() и flock() может произойти много
неожиданного, включая удаление этого файла.

VN> Какой ещё юзкейс на подобную функциональность ты предложишь?

Я думаю, что во многих случаях перед rename() проверяется отсутствие dst-файла.
Даже при обычном создании нового документа. И это значит, что запрос
"override?" будет задан в большинстве случаев, но не всегда - иногда этот
override может быть сделан молча и случайно.
Наличие атомарного safe_rename() избавило бы от этих проблем.

И ведь всякие linkat(), localtime_r(), fseeko() и прочие менее необходимые (на
мой взгляд) функции реализовали и добавили, почему ж safe_rename() нигде нет?
Как и атомарного tmpfile(), и flink(int fd, const char *newpath), отсутствию
которых я уже как-то удивлялся.

Lucky carrier,
Паша
aka ***@gul.kiev.ua
Valentin Nechayev
2015-06-10 08:52:58 UTC
Permalink
Hi,
PG> Да, согласен, нередко бывает, что нужна либо старая версия файла, либо
PG> новая, но не его отсутствие. Хотя при обновлении более частый случай -
PG> необходимость атомарного переименования группы файлов или целой
PG> директории. Атомарный rename ничем не поможет, если старый бинарник не
PG> может работать с новым конфигом или наоборот. Тут, собственно, вообще
PG> транзакции нужны, но это уже немного другой вопрос - отсутствие
PG> транзакций объяснить проще. То, что ошибка (например, отключение
PG> питания) в процессе обновления может привести к неработоспособности
PG> обновлявшегося в этот момент софта, никого не удивит, несмотря на
PG> атомарный rename и журналируемую FS. И к тому, что за время
PG> обновления, например, перлового модуля mrtg может ругнуться на ошибку
PG> об отсутствии этого модуля, все тоже относятся спокойно - понятно, что
PG> update часто по сути состоит из deinstall + install.

Это плохой, негодный update :) BSD xinstall (присутствующий в BSD системах как
/usr/bin/install) явно имеет ключ -S для режима создания нового файла и затем
rename() на старое место. В распространившиеся по миру более ранние версии это
не попало, включая и Linux coreutils. Зато пакетные менеджеры могут так делать
(тут уже давно не следил за особенностями).

VN>> А вот именно для недеструктивного применений меньше. Фактически
VN>> это ситуация соревнования за право занять некоторое имя
VN>> с контролем возможности это сделать и сохранением статус-кво при
VN>> невозможности; то есть что-то вроде мьютекса.

PG> Hесколько процессов могут принимать сообщения (файлы). После окончания
PG> приёма файл переименовывается из временного имени в окончательное для
PG> дальнейшей обработки. Hапример, обычная запись сообщения в maildir или
PG> в spool. Для избежания коллизий и случайного удаления почты необходимо
PG> строить полноценный IPC с мьютексами. А если процессы, пишущие в этот
PG> maildir, работают на разных серверах? Hедеструктивный rename избавил
PG> бы от этих проблем.

Вот тут http://www.qmail.org/qmail-manual-html/man5/maildir.html расписан,
кроме формата maildir, алгоритм работы с ним, который устраняет подобные
проблемы. Там используется несколько подпорок вроде случайных имён и суточных
таймаутов на переходы между состояниями, но, насколько я знаю, явных дыр в
алгоритме не находили. Понятно, что это не радикальный метод, но устраняет
жёсткую необходимость в недеструктивном 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>> Тут даже лока на файл не нужно, хотя с ним удобнее.
PG> Учитывая, что лок нельзя установаить одновременно с open(),

В BSD - можно. В остальных, насколько вижу по манам, нельзя.

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), отсутствию которых я уже как-то удивлялся.

Тут таки надо по пунктам.

*at() это неизбежно для поддержки многонитевости (если текущий каталог остаётся
общим на процесс, а не нить) и исключения дорогих ненужных namei lookups на
каждый чих. Хотя я бы добавил к ним ещё walk flags, например, со следующими
флагами:
WF_NOFOLLOWLAST (аналогично нынешнему O_NOFOLLOW у open(2))
WF_NOFOLLOWMID (все симлинки не допускать)
WF_ONESTEP (имя в path считается как одна компонента, независимо от '/' в ней)
WF_FSPREFIX (в начале проверяется "${fs}:" аналогично именам дисков в RSX-11;
набор стандартных имён fs позволяет уйти от проблем типа управления частичным
маппингом devfs внутрь chroot/jail/LXC)

*_r() это та же проблема исправления дефектов заточки под однонитевость.

fseeko() это проблема изначальной ошибки в размере предшественника off_t.
Местами это до сих пор стреляет в виде необходимости всяких LARGEFILE64_SOURCE.

У них всех не было внятного обхода проблемы (не считать же им, например,
мьютекс вокруг chdir() или прыжков не более чем по 2**31-1 байт, при этом не
имея возможности надёжно узнать текущую позицию).
У safe rename твоего стиля - скорее есть обход, чем нет. Пока что мы не нашли
ни одного случая, когда совсем уж нельзя исправить дизайном...


-netch-

... Ceterum censeo IPv6 esse delendam
Pavel Gulchouck
2015-06-10 10:49:38 UTC
Permalink
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
Serguei E. Leontiev
2015-06-07 19:20:29 UTC
Permalink
Привет Валентин,

От 7 июня 2015 г., 16:11:30 в fido7.ru.unix.prog ты писал:
VN> Меня тут единственное что смущает - <stdio.h> - какой-то
VN> неадекватный вариант размещения. Оно лучше ложится в
VN> <fcntl.h> или <unistd.h>.

Потому что, rename() входит в стандарты C/C++, а <fcntl.h> и <unistd.h>
не входят в эти стандарты.

--
Успехов, Сергей Леонтьев. E-mail: ***@CryptoPro.ru
Pavel Gulchouck
2015-06-07 19:57:36 UTC
Permalink
Hi Serguei!

07 Jun 15, Serguei E. Leontiev ==> Valentin Nechayev:

VN>> Меня тут единственное что смущает - <stdio.h> - какой-то
VN>> неадекватный вариант размещения. Оно лучше ложится в
VN>> <fcntl.h> или <unistd.h>.

SEL> Потому что, rename() входит в стандарты C/C++, а <fcntl.h> и <unistd.h>
SEL> не входят в эти стандарты.

А open() разве не входит в стандарты C? Или, скажем, unlink(), stat() и прочие
файловые операции?
Я привык думать, что <stdio.h> - это для "FILE *" и всяких относительно
высокоуровневых операций.

Lucky carrier,
Паша
aka ***@gul.kiev.ua
Serguei E. Leontiev
2015-06-07 22:34:39 UTC
Permalink
Привет Павел,

От 7 июня 2015 г., 22:57:36 в fido7.ru.unix.prog ты писал:
VN>>> Меня тут единственное что смущает - <stdio.h> -
VN>>> какой-то неадекватный вариант размещения. Оно лучше
VN>>> ложится в <fcntl.h> или <unistd.h>.
SEL>> Потому что, rename() входит в стандарты C/C++, а
SEL>> <fcntl.h> и <unistd.h> не входят в эти
SEL>> стандарты.
PG> А open() разве не входит в стандарты C? Или, скажем, unlink(),
PG> stat() и прочие файловые операции?

Hет, все эти функции не входят в стандарты языков C/C++, они входят
только в стандарты POSIX.

PG> Я привык думать, что <stdio.h> - это для "FILE *" и
PG> всяких относительно высокоуровневых операций.

Hе только, по стандартам C11/C++14 туда входит ещё несколько сервисных
не зависящих от платформы функций, типа: remove(), rename(), tmpnam(),
perror(), tmpnam_s().

<stdio.h> - это В/В C/C++ не зависящий от платформы.

--
Успехов, Сергей Леонтьев. E-mail: ***@CryptoPro.ru
Vladimir N. Oleynik
2015-06-10 09:43:13 UTC
Permalink
Здарофъ, Serguei


PG> Я привык думать, что <stdio.h> - это для "FILE *" и
PG> всяких относительно высокоуровневых операций.

SL> Hе только, по стандартам C11/C++14 туда входит ещё несколько сервисных
SL> не зависящих от платформы функций, типа: remove(), rename(), tmpnam(),
SL> perror(), tmpnam_s().

Занудства ради: perror(str) таки "FILE *" =
fprintf(stderr, "%s%s%s\n", str, (str[0] ? ": " : ""), strerror(errno));
и потому жутко неудобно, что и родило несовместимое GNU расширение
формата %m, которого нет даже в Bionic, но зато есть в syslog(3),
но не в syslog(2)... короче бардак.
tmpnam() as obsolete и слава богу - уродская функция.
под tmpnam_s() наверное имелось в виду tmpnam_r()?
То, что эти функции не в fcntl.h - понятно, там же одни сисколы.
То, что эти функции не в unistd.h - тоже понятно по префиксу uni :)
Так что правильно же было их запихать в stdlib.h imho.


--w
vodz
Serguei E. Leontiev
2015-06-10 23:27:24 UTC
Permalink
Привет Владимир,

От 10 июня 2015 г., 12:43:13 в fido7.ru.unix.prog ты писал:
VO> Здарофъ, Serguei
PG>> Я привык думать, что <stdio.h> - это для "FILE *" и
PG>> всяких относительно высокоуровневых операций.
SL>> Hе только, по стандартам C11/C++14 туда входит ещё
SL>> несколько сервисных не зависящих от платформы функций,
SL>> типа: remove(), rename(), tmpnam(), perror(), tmpnam_s().
VO> Занудства ради: perror(str) таки "FILE *" =
VO> fprintf(stderr, "%s%s%s\n", str, (str[0] ? ": " : ""),
VO> strerror(errno)); и потому жутко неудобно, что и родило
VO> несовместимое GNU расширение формата %m, которого нет даже в
VO> Bionic, но зато есть в syslog(3), но не в syslog(2)... короче
VO> бардак. ??tmpnam() as obsolete и слава богу - уродская функция.

"obsolete" - интересно где это сказано?

VO> под tmpnam_s() наверное имелось в виду tmpnam_r()?

ISO/IEC 9899:2011 (С11) определяет именно tmpnam_s()

А tmpnam_r() - нестандартная функция, которая не входит даже в Linux
Standard Base ISO/IEC 23360, так же как и в POSIX (IEEE Std 1003.1, 2013
Edition) и С++14/С11.

Я ещё могу понять зачем комитету языка C по предложению MS надо было
ввести функцию:

errno_t tmpnam_s(char *s, rsize_t maxsize);

Hо зачем этим Linix-ойдам потребовалась функция:

char *
tmpnam_r(char *s)
{
return s ? tmpnam(s) : NULL;
}

Ума не приложу :)

--
Успехов, Сергей Леонтьев. E-mail: ***@CryptoPro.ru
Vladimir N. Oleynik
2015-06-11 13:51:35 UTC
Permalink
Здарофъ, Serguei


VO>> tmpnam() as obsolete и слава богу - уродская функция.
SL> "obsolete" - интересно где это сказано?

POSIX.1-2008 marks tmpnam() as obsolete.

SL> Hо зачем этим Linix-ойдам потребовалась функция tmpnam_r()
SL> Ума не приложу :)

Ещё б найти что её использует. Я вон затычки рисую в Bionic для mkstemp_r(3),
ибо util-linux таки использует уже.


--w
vodz
Serguei E. Leontiev
2015-06-11 18:32:48 UTC
Permalink
Привет Владимир,

От 11 июня 2015 г., 16:51:35 в fido7.ru.unix.prog ты писал:
VO>>> tmpnam() as obsolete и слава богу - уродская функция.
SL>> "obsolete" - интересно где это сказано?
VO> POSIX.1-2008 marks tmpnam() as obsolete.

Твоя правда, написали. Однако, не они породили, не им убивать, в C11
этого не написано. А лично я не верю, что POSIX сможет отказаться от
функции, которая есть в стандарте языка C.

SL>> Hо зачем этим Linix-ойдам потребовалась функция tmpnam_r()
SL>> Ума не приложу :)
VO> Ещё б найти что её использует. Я вон затычки рисую в Bionic для
VO> mkstemp_r(3), ибо util-linux таки использует уже.

mkstemp_r() - интересно, а это чей горячечный бред?

--
Успехов, Сергей Леонтьев. E-mail: ***@CryptoPro.ru
Valentin Nechayev
2015-06-12 04:24:22 UTC
Permalink
VO>>>> tmpnam() as obsolete и слава богу - уродская функция.
SL>>> "obsolete" - интересно где это сказано?
VO>> POSIX.1-2008 marks tmpnam() as obsolete.
SEL> Твоя правда, написали. Однако, не они породили, не им убивать, в C11
SEL> этого не написано. А лично я не верю, что POSIX сможет отказаться от
SEL> функции, которая есть в стандарте языка C.

Совсем отказаться - нет. А вот громко жаловаться при линковке, и
потребовать специальный ключик "полной совместимости со стандартом" -
вполне.

SL>>> Hо зачем этим Linix-ойдам потребовалась функция tmpnam_r()
SL>>> Ума не приложу :)
VO>> Ещё б найти что её использует. Я вон затычки рисую в Bionic для
VO>> mkstemp_r(3), ибо util-linux таки использует уже.
SEL> mkstemp_r() - интересно, а это чей горячечный бред?

Присоединяюсь к вопросу. Зачем ей _r?


--netch--
Serguei E. Leontiev
2015-06-12 06:07:57 UTC
Permalink
Привет Валентин,

От 12 июня 2015 г., 7:24:22 в fido7.ru.unix.prog ты писал:
VO>>>>> tmpnam() as obsolete и слава богу - уродская
VO>>>>> функция.
SL>>>> "obsolete" - интересно где это сказано?
VO>>> POSIX.1-2008 marks tmpnam() as obsolete.
SEL>> Твоя правда, написали. Однако, не они породили, не им
SEL>> убивать, в C11 этого не написано. А лично я не верю, что
SEL>> POSIX сможет отказаться от функции, которая есть в
SEL>> стандарте языка C.
VN> Совсем отказаться - нет. А вот громко жаловаться при линковке, и
VN> потребовать специальный ключик "полной совместимости со
VN> стандартом" - вполне.

Паранойя косит ряды, например, в C11 (ISO/IEC 9899:2011) уже включили
опцию __STDC_WANT_LIB_EXT1__ (fopen_s(), bsearch_s(), memcpy_s(), ...).
Теперь в C++ и в POSIX её будут внедрять :)

SL>>>> Hо зачем этим Linix-ойдам потребовалась функция
SL>>>> tmpnam_r() Ума не приложу :)
VO>>> Ещё б найти что её использует. Я вон затычки рисую в
VO>>> Bionic для mkstemp_r(3), ибо util-linux таки использует
VO>>> уже.
SEL>> mkstemp_r() - интересно, а это чей горячечный бред?
VN> Присоединяюсь к вопросу. Зачем ей _r?

Я спросил у Яндекса, он нашёл только `char *_mktemp_r(void *reent, char
*path)', т.е. наверное это внутренняя функция.

--
Успехов, Сергей Леонтьев. E-mail: ***@CryptoPro.ru
Serguei E. Leontiev
2015-06-12 06:09:27 UTC
Permalink
P.S.

От 12 июня 2015 г., 9:07:57 в fido7.ru.unix.prog ты писал:
SL> Я спросил у Яндекса, он нашёл только `char *_mktemp_r(void
SL> *reent, char *path)', т.е. наверное это внутренняя функция.

int *_mkstemp_r(void *reent, char *path)

--
Успехов, Сергей Леонтьев. E-mail: ***@CryptoPro.ru
Valentin Nechayev
2015-06-13 05:47:03 UTC
Permalink
SEL> От 12 июня 2015 г., 9:07:57 в fido7.ru.unix.prog ты писал:
SL>> Я спросил у Яндекса, он нашёл только `char *_mktemp_r(void
SL>> *reent, char *path)', т.е. наверное это внутренняя функция.

SEL> int *_mkstemp_r(void *reent, char *path)

Объявление - это хорошо, но, может, коллега, Вы приведёте точный
источник этой цитаты? А то вдруг это, не сочтите за наезд, форум из
серии "как бы сломать API поизвращённее, чтобы все надолго
замучались".

Я почему ставлю вопрос именно в таком плане -

1) функции и так достаточно "реентрантны" при данном интерфейсе для
всех четырёх(!) смыслов понятия reentrant, при правильном их
использовании,
2) grep по комбинациям символов mktemp_r, mkstemp_r показал полное
отсутствие этих комбинаций в glibc repo по веткам master и
release/2.{10,17,21}/master, из чего я считаю, что если какой-то
безумец и ввёл их когда-то, то про них давно забыли;
3) поиск по манам на доступным мне системам (Ubuntu (14.04), CentOS
(6)) ничего не дал;
4) единственные реальные упоминания ведут вначале в так называемую
newlib имени RedHat, а там находится, например,
https://sourceware.org/ml/newlib/2009/msg00737.html с таким commit
message:

===
Add mkdtemp, mkstemps.
* libc/stdio/mktemp.c: Fix documentation.
(_gettemp): Add domkdir and length parameters. Check for
insufficient 'X' suffix. Drop cygwin-specific code.
(_mkstemp_r, _mktemp_r, mkstemp, mktemp): Adjust clients.
(_mkdtemp_r, _mkstemps_r, mkdtemp, mkstemps): New functions.
* libc/include/stdlib.h (_mkdtemp_r, _mkstemps_r, mkdtemp)
(mkstemps): Declare them.
===

что уже интереснее - cygwin-specific(!) (а нафига это было цигвину? и
когда собственно? данное письмо уже 6-летней давности)

Дальнейший поиск по "struct _reent" вывел на реализации с такой
структурой, которая содержит массу данных вроде errno, netdb (ok,
согласен), atexit список (а с каких пор это он не глобальный на
процесс?) и относится уже к Symbian!
http://devlib.symbian.slions.net/belle/GUID-C6E5F800-0637-419E-8FE5-1EBB40E725AA/GUID-47F20066-FC65-34CB-BCA5-0963716E8627.html

В общем, я не вижу причин не считать это всё маргинальными
извращениями для сверхстранных ситуаций, и никак не тем, что следует
учитывать в обычной работе.


--netch--
Serguei E. Leontiev
2015-06-13 09:20:42 UTC
Permalink
Валентин, привет,
Post by Valentin Nechayev
Post by Serguei E. Leontiev
Post by Serguei E. Leontiev
Я спросил у Яндекса, он нашёл только `char *_mktemp_r(void
*reent, char *path)', т.е. наверное это внутренняя функция.
int *_mkstemp_r(void *reent, char *path)
Объявление - это хорошо, но, может, коллега, Вы приведёте точный
источник этой цитаты? А то вдруг это, не сочтите за наезд, форум из
серии "как бы сломать API поизвращённее, чтобы все надолго
замучались".
По сути, это вопрос к Владимиру Олейнику, который ссылался на некий
util-linux и Bionic.
Post by Valentin Nechayev
Я почему ставлю вопрос именно в таком плане -
1) функции и так достаточно "реентрантны" при данном интерфейсе для
всех четырёх(!) смыслов понятия reentrant, при правильном их
использовании,
Строго говоря, это не обязательно так. Вероятно, реализация этих функций
основана на строчных функциях и/или на форматных функциях, которые зависят
от текущей locale. Кроме того, при N*M многопоточности, тоже могут быть
свои особенные заморочки с контекстами работы каналов.

В прочем, не суть, судя по символу '_' в начале имени это особенности
внутренней архитектуры некой библиотеки.
--
Успехов, Сергей Леонтьев, <http://www.cryptopro.ru> (NewsTap)
Serguei E. Leontiev
2015-06-07 19:30:30 UTC
Permalink
Привет Павел,

От 7 июня 2015 г., 13:37:12 в fido7.ru.unix.prog ты писал:
PG> Вариант "link(src, dst) || unlink(src)", во-первых, неатомарен,

Поясни, в чём выражается "неатомарность" данного выражения?

Hа мой непросвещённый взгляд, любое количество таких выражений
выполняемых параллельно, гарантировано выполнят успешно одно и только
одно переименование, и только в случае отсутствия dst. Или я не прав?

--
Успехов, Сергей Леонтьев. E-mail: ***@CryptoPro.ru
Loading...