我希望根据该文件是否已经存在来写一个文件,仅在不存在的情况下才写(实际上,我希望继续尝试文件,直到找到一个不存在的文件为止)。
下面的代码显示在其中一个潜在的攻击者可以插入一个符号,作为建议的方式这篇文章中该文件的测试和写入的文件之间。如果代码以足够高的权限运行,则可能会覆盖任意文件。
有什么办法解决这个问题?
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')
编辑 :另请参见DaveJones的回答:从Python3.3开始,您可以使用该x标志open()来提供此功能。
x
open()
作为参考,Python 3.3'x'在open()函数中实现了一种新模式来涵盖此用例(仅创建,如果文件存在则失败)。请注意,'x'模式是单独指定的。使用'wx'结果中的ValueErroras'w'是多余的(无论如何,如果调用成功,您唯一可以做的就是将其写入文件;如果调用成功,它就不存在):
'x'
'wx'
ValueError
'w'
>>> f1 = open('new_binary_file', 'xb') >>> f2 = open('new_text_file', 'x')
下面的原始答案
是的,但不使用Python的标准open()调用。您需要使用os.open()代替,它允许您为基础C代码指定标志。
os.open()
特别是您要使用O_CREAT | O_EXCL。从该名男子页open(2)下O_EXCL我的Unix系统:
O_CREAT | O_EXCL
open(2)
O_EXCL
确保此调用创建了文件:如果将此标志与一起指定O_CREAT,并且路径名已经存在,open()则将失败。O_EXCL如果O_CREAT未指定,则行为不确定。 当指定这两个标志时,将不遵循符号链接:如果pathname是符号链接,则open()无论符号链接指向何处都将失败。 O_EXCL 仅当在内核2.6或更高版本上使用NFSv3或更高版本时,NFS才支持该功能。在O_EXCL不提供NFS支持的环境中,依赖它执行锁定任务的程序将包含竞争条件。
确保此调用创建了文件:如果将此标志与一起指定O_CREAT,并且路径名已经存在,open()则将失败。O_EXCL如果O_CREAT未指定,则行为不确定。
O_CREAT
当指定这两个标志时,将不遵循符号链接:如果pathname是符号链接,则open()无论符号链接指向何处都将失败。
O_EXCL 仅当在内核2.6或更高版本上使用NFSv3或更高版本时,NFS才支持该功能。在O_EXCL不提供NFS支持的环境中,依赖它执行锁定任务的程序将包含竞争条件。
因此,这并不完美,但AFAIK是避免这种情况的最接近的方法。
编辑:使用os.open()而不是其他规则open()仍然适用。特别是,如果你想使用返回的文件描述符进行读取或写入,你需要的一个O_RDONLY,O_WRONLY或O_RDWR标志以及。
O_RDONLY
O_WRONLY
O_RDWR
所有O_*标志都在Python的os模块中,因此您需要import os使用os.O_CREAT等。
O_*
os
import os
os.O_CREAT
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!")