Rename file without overwriting existing files

Peter Otten __peter__ at web.de
Mon Jan 30 05:39:49 EST 2017


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:

$ echo foo > foo
$ echo bar > bar
$ python3
Python 3.4.3 (default, Nov 17 2016, 01:08:31) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> def rename(source, dest):
...     os.link(source, dest)
...     os.unlink(source)
... 
>>> rename("foo", "baz")
>>> os.listdir()
['bar', 'baz']
>>> rename("bar", "baz")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in rename
FileExistsError: [Errno 17] File exists: 'bar' -> 'baz'





More information about the Python-list mailing list