Make a unique filesystem path, without creating the file

Cameron Simpson cs at zip.com.au
Sun Feb 21 15:35:36 EST 2016


On 16Feb2016 19:24, Alan Bawden <alan at csail.mit.edu> wrote:
>Ben Finney <ben+python at benfinney.id.au> writes:
>> Cameron Simpson <cs at zip.com.au> writes:
>>> I've been watching this for a few days, and am struggling to
>>> understand your use case.
>>
>> Yes, you're not alone. This surprises me, which is why I'm persisting.
>>
>>> Can you elaborate with a concrete example and its purpose which would
>>> work with a mktemp-ish official function?
>>
>> An example::
>
>Let me present another example that might strike some as more
>straightforward.
>
>If I want to create a temporary file, I can call mkstemp().
>If I want to create a temporary directory, I can call mkdtemp().
>
>Suppose that instead of a file or a directory, I want a FIFO or a
>socket.
>
>A FIFO is created by passing a pathname to os.mkfifo().  A socket is
>created by passing a pathname to an AF_UNIX socket's bind() method.  In
>both cases, the pathname must not name anything yet (not even a symbolic
>link), otherwise the call will fail.
>
>So in the FIFO case, I might write something like the following:
>
>    def make_temp_fifo(mode=0o600):
>        while True:
>            path = tempfile.mktemp()
>            try:
>                os.mkfifo(path, mode=mode)
>            except FileExistsError:
>                pass
>            else:
>                return path
>
>mktemp() is convenient here, because I don't have to worry about whether
>I should be using "/tmp" or "/var/tmp" or "c:\temp", or whether the
>TMPDIR environment variable is set, or whether I have permission to
>create entries in those directories.  It just gives me a pathname
>without making me think about the rest of that stuff.

Yes, that is highly desirable.

>Yes, I have to
>defend against the possibility that somebody else creates something with
>the same name first, but as you can see, I did that, and it wasn't
>rocket science.
>
>So is there something wrong with the above code?  Other than the fact
>that the documentation says something scary about mktemp()?

Well, it has a few shortcomings.

It relies on mkfifo reliably failing if the name exists. It shounds like mkfifo 
is reliable this way, but I can imagine analogous use cases without such a 
convenient core action, and your code only avoids mktemp's security issue 
_because_ mkfifo has that fortuitous aspect.

>It looks to me like mktemp() provides some real utility, packaged up in
>a way that is orthogonal to the type of file system entry I want to
>create, the permissions I want to give to that entry, and the mode I
>want use to open it.  It looks like a useful, albeit low-level,
>primitive that it is perfectly reasonable for the tempfile module to
>supply.

Secondly, why is your example better than::

  os.mkfifo(os.path.join(mkdtemp(), 'myfifo'))

On that basis, this example doesn't present a use case what can't be addressed 
by mkstemp or mkdtemp.

By contrast, Ben's example does look like it needs something like mktemp.

>And yet the documentation condemns it as "deprecated", and tells me I
>should use mkstemp() instead.

You _do_ understand the security issue, yes? I sure looked like you did, until 
here. 

>(As if that would be of any use in the
>situation above!)  It looks like anxiety that some people might use
>mktemp() in a stupid way has caused an over-reaction.

No, it is anxiety that mktemp's _normal_ use is inherently unsafe.

>Let the
>documentation warn about the problem and point to prepackaged solutions
>in the common cases of making files and directories, but I see no good
>reason to deprecate this useful utility.

I think it is like C's gets() function (albeit not as dangerous). It really 
shouldn't be used.

One of the things about mktemp() is its raciness, which is the core of the 
security issue. People look at the term "security issue" and think "Ah, it can 
be attacked." But the flipside is that it is simply unreliable. Its normal use 
was to make an ordinary temp file. Consider the case where two instances of the 
same task are running at the same time, doing that. They can easily, by 
accident, end us using the same scratch file!

This is by no means unlikely; any shell script running tasks in parallel can 
arrange it, any procmail script filing a message with a "copy" rule (which 
causes procmail simply to fork and proceed), etc.

This is neither weird nor even unlikely which is why kmtemp is strongly 
discouraged - naive (and standard) use is not safe.

That you have contrived a use case where you can _carefully_ use mktemp in 
safety in no way makes mktemp recommendable. In fact your use case isn't safe, 
because _another_ task using mktemp in conflict as a plain old temporary file 
may grab your fifo.

Cheers,
Cameron Simpson <cs at zip.com.au>



More information about the Python-list mailing list