Rename file without overwriting existing files

Jussi Piitulainen jussi.piitulainen at helsinki.fi
Mon Jan 30 05:55:45 EST 2017


Peter Otten writes:

> Steve D'Aprano wrote:
>
>> On Mon, 30 Jan 2017 03:33 pm, Cameron Simpson wrote:
>> 
>>> On 30Jan2017 13:49, Steve D'Aprano <steve+python at pearwood.info> wrote:
>>>>This code contains a Time Of Check to Time Of Use bug:
>>>>
>>>>    if os.path.exists(destination)
>>>>        raise ValueError('destination already exists')
>>>>    os.rename(oldname, destination)
>>>>
>>>>
>>>>In the microsecond between checking for the existence of the destination
>>>>and actually doing the rename, it is possible that another process may
>>>>create the destination, resulting in data loss.
>>>>
>>>>Apart from keeping my fingers crossed, how should I fix this TOCTOU bug?
>>> 
>>> For files this is a problem at the Python level. At the UNIX level you
>>> can play neat games with open(2) and the various O_* modes.
>>> 
>>> however, with directories things are more cut and dry. Do you have much
>>> freedom here? What's the wider context of the question?
>> 
>> The wider context is that I'm taking from 1 to <arbitrarily huge number>
>> path names to existing files as arguments, and for each path name I
>> transfer the file name part (but not the directory part) and then rename
>> the file. For example:
>> 
>> foo/bar/baz/spam.txt
>> 
>> may be renamed to:
>> 
>> foo/bar/baz/ham.txt
>> 
>> but only provided ham.txt doesn't already exist.
>
> Google finds
>
> http://stackoverflow.com/questions/3222341/how-to-rename-without-race-conditions
>
> and from a quick test it appears to work on Linux:

It doesn't seem to be documented. I looked at help(os.link) on Python
3.4 and the corresponding current library documentation on the web. I
saw no mention of what happens when dst exists already.

Also, creating a hard link doesn't seem to work between different file
systems, which may well be relevant to Steve's case. I get:

    OSError: [Errno 18] Invalid cross-device link: [snip]

And that also is not mentioned in the docs.


More information about the Python-list mailing list