При определении того, существует файл или нет, каким образом использование оператора try позволяет избежать «состояния гонки»?

Я спрашиваю, потому что ответ (обновление: оно было удалено), похоже, подразумевает, что использование os.path.exists() создает возможность, которая не существовала бы в противном случае.

Пример приведен ниже:

try:
   with open(filename): pass
except IOError:
   print 'Oh dear.'

Но я не понимаю, как это позволяет избежать состояния гонки по сравнению с:

if not os.path.exists(filename):
    print 'Oh dear.'

Как вызов os.path.exists(filename) позволяет злоумышленнику сделать что-то с файлом, который он уже не может сделать?

28
Honest Abe 29 Янв 2013 в 06:05

3 ответа

Лучший ответ

Конечно, условие состязания находится между вашей программой и некоторым другим кодом, который работает с файлом (условие состязания всегда требует как минимум двух параллельных процессов или потоков, см. this для подробностей). Это означает, что использование open() вместо exists() может реально помочь только в двух ситуациях:

  1. Вы проверяете наличие файла, который создается или удаляется каким-либо фоновым процессом (однако, если вы запускаете на веб-сервере, это часто означает, что параллельно выполняется много копий вашего процесса для обработки HTTP-запросов, поэтому для веб-приложений не хватает условие возможно, даже если нет других программ).
  2. Может быть запущена какая-то вредоносная программа, которая пытается разрушить ваш код, уничтожая файл в моменты, когда вы ожидаете его существования.

exists() просто выполняет одну проверку. Если файл существует, он может быть удален через микросекунду после возврата exists() True. Если файл отсутствует, он может быть создан немедленно.

Тем не менее, open() не только проверяет наличие файла, но и открывает файл (и выполняет эти два действия атомарно, поэтому между проверкой и открытием ничего не может произойти). Обычно файлы не могут быть удалены, пока они кем-то открыты. Это означает, что внутри with вы можете быть полностью уверены: файл действительно существует сейчас, так как он открыт. Хотя это верно только внутри with, и файл все равно может быть удален сразу после выхода из блока with, размещение кода, которому требуется файл, для существования внутри with гарантирует, что код не потерпит неудачу.

33
Ellioh 29 Янв 2013 в 04:20

Я думаю, что вы спрашиваете об особом состоянии гонки, где:

  1. файл открыт
  2. контекст переключается и файл удаляется
  3. контекст переключается обратно, и файловые операции предпринимаются для «открытого» файла.

В этом случае вы «защищены», помещая весь код обработки файла в блок try, если в какой-то момент файл становится недоступным / поврежденным, ваши файловые операции могут завершиться «изящно» с помощью catch блок.

Обратите внимание, конечно, что в современных ОС это не может произойти в любом случае, когда файл «удален», удаление не произойдет, пока все открытые дескрипторы файла не будут разрешены (освобождены).

0
Mike 29 Янв 2013 в 02:23

Вот пример использования:

try:
    with open('filename') as f:
        do_stuff_that_depends_on_the_existence_of_the_file(f)
except IOError as e:
    print 'Trouble opening file'

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

try - это просто способ обнаружить ошибку или успех операции открытия файла, поскольку API-интерфейсы файлового ввода-вывода в Python обычно не имеют кодов возврата (вместо них используются исключения). Таким образом, чтобы действительно ответить на ваш вопрос, не try избегает условия гонки, а open. Это в основном то же самое в C (на котором основан Python), но без исключений. Прочтите это для получения дополнительной информации.

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

Вызов os.path.exists просто дает моментальный снимок в тот момент, когда файл может существовать или не существовать, и вы не знаете о существовании файла после возврата os.path.exists. Злонамеренный код или неожиданная логика могут удалить или изменить файл, если вы этого не ожидаете. Это все равно, что поворачивать голову, чтобы проверить, что дорога свободна, прежде чем въехать в нее. Как только вы поворачиваете голову назад, у вас нет ничего, кроме предположения о том, что происходит, куда вы больше не смотрите. Сохранение файла гарантирует расширенное согласованное состояние, что невозможно (хорошо или плохо) во время вождения. :)

Ваше предложение проверить, что файл не существует, а не использовать try/open, все еще недостаточно из-за природы снимка os.path.exists. К сожалению, я не знаю способа предотвратить создание файлов в каталоге во всех случаях, поэтому я считаю, что лучше всего проверить наличие файла, а не его отсутствие.

9
Community 23 Май 2017 в 11:47