Безопасно создать файл, если и только если он не существует в Python
Я хочу писать в файл на основе того, существует ли этот файл или нет, только писать, если он еще не существует (на практике я хочу продолжать пробовать файлы, пока не найду тот, который не существует).
следующий код показывает способ, которым потенциальный злоумышленник может вставить символическую ссылку, как это предлагается в этом сообщении между тестом для файла и записываемым файлом. Если код выполняется с достаточно высокими разрешениями, это может перезаписать произвольный файл.
есть ли способ решить эту проблему?
import os
import errno
file_to_be_attacked = 'important_file'
with open(file_to_be_attacked, 'w') as f:
f.write('Some important content!n')
test_file = 'testfile'
try:
with open(test_file) as f: pass
except IOError, e:
# symlink created here
os.symlink(file_to_be_attacked, test_file)
if e.errno != errno.ENOENT:
raise
else:
with open(test_file, 'w') as f:
f.write('Hello, kthxbye!n')
3 ответа:
Edit: см. также ответ Дейва Джонса: С Python 3.3, вы можете использовать
x
флагopen()
для обеспечения этой функции.оригинальный ответ ниже
да, но не используя стандарт Python
open()
звонок. Вам нужно будет использоватьos.open()
вместо этого, что позволяет указать флаги для базового кода C.в частности, вы хотите использовать
O_CREAT | O_EXCL
. Из справочной страницы дляopen(2)
подO_EXCL
в моей системе Unix:убедитесь, что этот вызов создает файла: если этот флаг указан в сочетании с
O_CREAT
, и путь уже существует, тоopen()
не удастся. ПоведениеO_EXCL
неопределено, еслиO_CREAT
не указан.когда эти два флага указаны, символические ссылки не следуют: если pathname является символической ссылкой, то
open()
сбой независимо от того, где находится символическая ссылка к.
O_EXCL
поддерживается только в NFS при использовании nfsv3 или более поздней версии ядра 2.6 или более поздней версии. В средах, где NFSO_EXCL
поддержка не предоставляется, программы, которые полагаются на нее для выполнения задач блокировки, будут содержать условие гонки.так что это не идеально, но AFAIK это самое близкое, что вы можете получить, чтобы избежать этого состояния гонки.
изменить: другие правила, используя
os.open()
вместоopen()
до сих пор применяется. В частности, если вы хотите используйте возвращенный дескриптор файла для чтения или записи, вам понадобится один изO_RDONLY
,O_WRONLY
илиO_RDWR
флаги, а также.все
O_*
флаги в Python этоos
модуль, так что вам нужноimport os
и использоватьos.O_CREAT
etc.пример:
import os import errno flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY try: file_handle = os.open('filename', flags) except OSError as e: if e.errno == errno.EEXIST: # Failed as the file already exists. pass else: # Something unexpected went wrong so reraise the exception. raise else: # No exception, so the file must have been created successfully. with os.fdopen(file_handle, 'w') as file_obj: # Using `os.fdopen` converts the handle to an object that acts like a # regular Python file object, and the `with` context manager means the # file will be automatically closed when we're done with it. file_obj.write("Look, ma, I'm writing to a new file!")
для справки, Python 3.3 реализует новый
'x'
режима вopen()
функция для покрытия этого варианта использования (только создать, сбой, если файл существует). Обратите внимание, что'x'
режим задается сам по себе. Используя'wx'
результатыValueError
как'w'
является избыточным (единственное, что вы можете сделать, если вызов завершится успешно, это записать в файл в любом случае; он не может существовать, если вызов завершится успешно):>>> f1 = open('new_binary_file', 'xb') >>> f2 = open('new_text_file', 'x')
для Python 3.2 и ниже (включая Python 2.х) обращайтесь к принято отвечать.