31

在确定文件是否存在时,使用 try 语句如何避免“竞争条件”?

我之所以问,是因为一个高度赞成的答案(更新:它已被删除)似乎暗示 usingos.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)允许攻击者对文件执行他们无法执行的操作?

4

3 回答 3

36

竞争条件当然是在您的程序和在文件上运行的其他一些代码之间(竞争条件总是需要至少两个并行进程或线程,有关详细信息,请参阅内容)。这意味着使用open()而不是exists()可能仅在两种情况下真正有帮助:

  1. 您检查是否存在由某个后台进程创建或删除的文件(但是,如果您在 Web 服务器内运行,这通常意味着您的进程的许多副本并行运行以处理 HTTP 请求,因此对于 Web 应用程序来说即使没有其他程序也可能出现这种情况)。
  2. 可能有一些恶意程序正在运行,它试图通过在您期望它存在的时刻破坏文件来使您的代码崩溃。

exists()只执行一次检查。如果文件存在,它可能会在exists()返回后一微秒被删除True。如果文件不存在,它可能会立即创建。

但是,open()不仅测试文件是否存在,而且还打开文件(并以原子方式执行这两个操作,因此在检查和打开之间不会发生任何事情)。通常文件在有人打开时无法删除。这意味着在里面with你可能完全确定:文件现在确实存在,因为它是打开的。虽然只有 inside 才是正确的with,并且文件仍然可以在with块退出后立即删除,但将需要文件存在的代码放在里面with保证代码不会失败。

于 2013-01-29T04:08:56.297 回答
10

这是一个使用示例:

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只是一种检测打开文件行为的错误或成功的方法,因为 Python 中的文件 I/O API 通常没有返回码(使用异常代替)。所以要真正回答你的问题,不是try避免竞争条件的,而是open. 它在 C(Python 所基于)中基本相同,但没有例外。阅读内容以获取更多信息。

请注意,您可能希望执行取决于对 try 块内文件的访问权限的代码。关闭文件后,将无法保证其存在。

调用os.path.exists只是在文件可​​能存在或可能不存在的时刻给出快照,并且一旦os.path.exists返回,您就不知道文件的存在。恶意代码或意外逻辑可能会在您不期望的情况下删除或更改文件。这类似于在驶入道路之前转过头来检查道路是否畅通。一旦你转过头来,你就只能猜测你不再看的地方发生了什么。保持文件打开保证了扩展的一致状态,这在驾驶时是不可能的(无论好坏)。:)

try/open由于os.path.exists. _ 不幸的是,我不知道在所有情况下都无法阻止在目录中创建文件,所以我认为最好检查文件是否存在,而不是不存在。

于 2013-01-29T02:11:20.517 回答
0

我认为您要问的是特定的比赛条件:

  1. 文件被打开
  2. 上下文被切换,文件被删除
  3. 上下文被切换回并尝试对“打开的”文件进行文件操作

在这种情况下,您“受保护”的方式是将所有文件处理代码放在一个try块中,如果文件在任何时候变得不可访问/损坏,您的文件操作将能够通过该catch块“优雅地”失败。

请注意,现代操作系统无论如何都不会发生这种情况,当文件被“删除”时,删除不会发生,直到文件上的所有打开句柄都被解析(释放)

于 2013-01-29T02:23:02.923 回答