Search code examples
shelltemporary-files

Implementing mkstemp manually


Okay so after spending ages developing a script ready for testing, I've now learned the target environment doesn't have mkstemp (I foolishly believed every unix-y OS had that), nor does it seem to have any of the other common temp-file utilities. If there are any other widely available temp-file commands do let me know, but I don't think I have any to use.

So, this means I need to implement a form of mkstemp myself. Now the easiest way is something like tmp="/tmp/tmp.$$.$RANDOM" and while that works it doesn't guarantee there won't be a collision of file names, so I need to test for that, however the problem is that between testing for the file, and creating one, the file could end up unexpectedly being created, so it may not be a suitable method either, at least on it's own.

In the past I had to implement an equivalent to lockfile myself, which I was able to do by using a temporary file moved into place using mv as a cheat (if it returns an error then the lock already existed). I'm inclined to think I can maybe do something similar using some operation that will fail if the file already exists, but I'm not sure what the best way would be.

I know that use of /tmp/tmp.$$.$RANDOM is unlikely to result in collisions, but I'd like to implement this correctly if I can, as the script needs to create quite a lot of temporary files that are then moved into place, and I may need to do the same in other scripts later on, so it'd be nice to do it correctly!

EDIT: I just realised I've been referring to mktemp everywhere instead of mkstemp which is the one I really want to replicate (where a file is created safely for you). I think I've corrected mistaken mentions, please forgive the confusion!


Solution

  • Usually, the command line tool for creating temporary directories is called mktemp, not mkstemp. I'm not sure if it's safe to use on all platforms. If it isn't, you can attempt to make a directory with a strict umask and fail when the directory already exists.

    mkdtemp() {
        old_mask=$(umask)
        umask 077
    
        name="/tmp/tmp.$$.$RANDOM"
        mkdir "$name" && echo "$name"
        retval=$?
    
        umask $old_mask
    
        return $retval
    }
    

    Usage:

    tempdir=$(mkdtemp) || report_failure
    

    Since this tries to create the directory (an atomic operation) instead of checking whether it exists and fails when the name it generates is taken, this operation is safe. It is prone to a denial-of-service attack, though, where an attacker creates many temporary directories just to let the above function fail.