Каковы причины для проверки ошибки при закрытии()?

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

Широко распространенная практика, с которой я склонен соглашаться, обычно относится к close как функции ресурса-освобождения для файловых дескрипторов, а не к потенциальной операции ввода-вывода со значимыми ошибками. И действительно, до решения проблемы 529, POSIX оставил состояние дескриптора файла (то есть, было ли оно еще выделено или не указано) неуказано после ошибок, что делает невозможным реагировать переносимо на ошибки каким-либо значимым образом.

Однако много программного обеспечения GNU подходит для проверки ошибок с close, а справочная страница Linux для close вызывает неспособность выполнить так что "общая, но тем не менее серьезная ошибка программирования". NFS и квоты приводятся как обстоятельства, при которых close может вызывать ошибку, но не дает подробностей.

Каковы ситуации, при которых close может потерпеть неудачу, в реальных системах, и актуальны ли они сегодня? Мне особенно интересно узнать, существуют ли какие-либо современные системы, где close терпит неудачу для любых не-NFS, не-устройств-node -специальных причин, а также для отказов NFS или устройств, при каких условиях (например, конфигурации), они могут быть видны.

Ответ 1

Когда-то (24 марта 2007 г.) Эрик Сосман имел следующую историю, чтобы поделиться в группе comp.lang.c:

(Позвольте мне начать с исповедания маленькой белой лжи: это не было fclose(), чей отказ не обнаружен, но POSIX close() функция; эта часть приложения использовала POSIX I/O. Ложь хотя и небезопасен, поскольку средства ввода-вывода C будут иметь не удалось точно таким же образом, и необнаруженный сбой имели те же последствия. Я расскажу, что произошло в члены C I/O, чтобы избежать слишком многого для POSIX.)

Ситуация была такой же, как описал Ричард Тобин. Приложение представляет собой систему управления документами, которая загружает файл документа в память, применяет изменения пользователя к копия памяти, а затем написал все в новый файл, когда он сказал для сохранения изменений. Он также поддерживал одноуровневую "старую версию", резервное копирование для безопасности: операция сохранения написана в соответствии с темпом файл, а затем, если это было удалено, удалена старая резервная копия, переименовал старый файл документа в имя резервной копии и переименовал temp в документ. bak → trash, doc → bak, tmp → doc.

В шаге write-to-temp проверяется почти все. fopen(), очевидно, но также и все fwrite() s и даже финальные fflush() были проверены на наличие ошибок - но fclose() не было. И на одной системе случилось, что последние несколько дисков блоки фактически не были распределены до тех пор, пока fclose() - ввод-вывод система сидела поверх оборудования доступа к файлам более низкого уровня VMS и в аранжировке было немного асинхронности.

В системе клиента были задействованы дисковые квоты, а жертва была близка к своему пределу. Он открыл документ, отредактировал какое-то время, сохранил его работу до сих пор и превысил его квота - которая не была обнаружена, потому что ошибка не появилась пока не будет отмечен fclose(). Думая, что сэкономление удалось, приложение отменило старую резервную копию, переименовало оригинал документ, чтобы стать резервным, и переименовал укороченный темп файл - новый документ. Пользователь работал немного дольше и сохранили снова - то же самое, за исключением того, что вы заметите, что на этот раз единственный сохранившийся полный файл был удален, и оба резервное копирование и файл основного документа усечены. Результат: весь файл документа стал мусором, а не только последней сессией работы, но все, что было раньше.

Как и Мерфи, жертва была боссом отдел, который купил несколько сотен лицензий для наших программное обеспечение, и я получил привилегию полета в Сент-Луис, чтобы быть брошенным львам.

[...]

В этом случае отказ fclose() будет (если обнаружен) остановил последовательность удаления и переименования. Пользователь был бы сказал: "Эй, проблема с сохранением документа, что-то сделать об этом и попробуйте еще раз. Между тем на диске ничего не изменилось". Даже если бы он не смог сохранить свою последнюю партию работы, он бы по крайней мере, не потеряли все, что было раньше.

Ответ 2

Рассмотрим обратный вопрос: "В каких ситуациях мы можем гарантировать, что close будет успешным?" Ответ:

  • когда вы его правильно назовете, и
  • когда вы знаете, что файловая система, на которой установлен файл, не возвращает ошибки из close в этой ОС и версии ядра

Если вы уверены, что у вас нет логических ошибок, и у вас есть полный контроль над ядром и файловой системой, вам не нужно проверять возвращаемое значение close.

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

  • Если кодер выполняет логическую ошибку и передает недопустимый fd на close, вы сможете быстро отследить его. Это может помочь поймать ошибку раньше, чем она вызовет проблемы.
  • Если пользователь запускает программу в среде, где close возвращает ошибку, когда (например) данные не были сброшены, вы сможете быстро диагностировать, почему данные были повреждены. Это легкий красный флаг, потому что вы знаете, что ошибка не должна возникать.