当且仅当 Python 不存在文件时安全地创建文件 [英] Safely create a file if and only if it does not exist with Python

查看:35
本文介绍了当且仅当 Python 不存在文件时安全地创建文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望根据该文件是否已存在写入文件,仅在文件不存在时写入(实际上,我希望继续尝试文件,直到找到不存在的文件).

I wish to write to a file based on whether that file already exists or not, only writing if it doesn't already exist (in practice, I wish to keep trying files until I find one that doesn't exist).

以下代码显示了潜在攻击者可以插入符号链接的方法,如这篇文章中所建议的那样对文件和正在写入的文件的测试.如果代码以足够高的权限运行,这可能会覆盖任意文件.

The following code shows a way in which a potentially attacker could insert a symlink, as suggested in this post in between a test for the file and the file being written. If the code is run with high enough permissions, this could overwrite an arbitrary file.

有没有办法解决这个问题?

Is there a way to solve this problem?

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')

推荐答案

编辑:另见 Dave Jones' 答案:从 Python 3.3 开始,您可以使用 x 标志到 open() 来提供此功能.

Edit: See also Dave Jones' answer: from Python 3.3, you can use the x flag to open() to provide this function.

下面的原始答案

是的,但不使用 Python 的标准 open() 调用.您需要使用 os.open() 相反,它允许您为底层 C 代码指定标志.

Yes, but not using Python's standard open() call. You'll need to use os.open() instead, which allows you to specify flags to the underlying C code.

特别是,你想使用 O_CREAT |O_EXCL.从我的 Unix 系统上 O_EXCL 下的 open(2) 手册页:

In particular, you want to use O_CREAT | O_EXCL. From the man page for open(2) under O_EXCL on my Unix system:

确保此调用创建文件:如果此标志与 O_CREAT 一起指定,并且路径名已存在,则 open() 将失败.如果未指定 O_CREAT,则 O_EXCL 的行为未定义.

Ensure that this call creates the file: if this flag is specified in conjunction with O_CREAT, and pathname already exists, then open() will fail. The behavior of O_EXCL is undefined if O_CREAT is not specified.

当指定了这两个标志时,不遵循符号链接:如果路径名是符号链接,则无论符号链接指向何处,open() 都会失败.

When these two flags are specified, symbolic links are not followed: if pathname is a symbolic link, then open() fails regardless of where the symbolic link points to.

O_EXCL 仅在内核 2.6 或更高版本上使用 NFSv3 或更高版本时在 NFS 上受支持.在不提供 NFS O_EXCL 支持的环境中,依赖它执行锁定任务的程序将包含竞争条件.

O_EXCL is only supported on NFS when using NFSv3 or later on kernel 2.6 or later. In environments where NFS O_EXCL support is not provided, programs that rely on it for performing locking tasks will contain a race condition.

所以它并不完美,但 AFAIK 是您可以避免这种竞争条件的最接近的方法.

So it's not perfect, but AFAIK it's the closest you can get to avoiding this race condition.

使用 os.open() 而不是 open() 的其他规则仍然适用.特别是,如果您想使用返回的文件描述符进行读取或写入,则需要 O_RDONLYO_WRONLYO_RDWR 标志之一

the other rules of using os.open() instead of open() still apply. In particular, if you want use the returned file descriptor for reading or writing, you'll need one of the O_RDONLY, O_WRONLY or O_RDWR flags as well.

所有的 O_* 标志都在 Python 的 os 模块中,所以你需要 import os 并使用 os.O_CREAT

All the O_* flags are in Python's os module, so you'll need to import os and use 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 不存在文件时安全地创建文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆