1

The .net framework has GetTempFileName, which goes straight to the Windows API procedure of the same name, and as far as I know is a threadsafe way to create a temporary file with a name not previously in use. It's hilariously outdated and limited, but as far as I know it works, as long as you keep your temp directory from filling up.

But I don't see anything that'll work even that well for creating temporary folders. In a contentious parallel environment such as a web server, I don't see any way to avoid race conditions between testing whether a folder name is in use, and creating it. This could I suppose be doable with unmanaged API code, but there may be institutional resistance to such a solution. Is there a better way?

And if there is, can it be extended back to temp file creation, so I don't have to use GetTempFileName with its four hex digits of variation?

-- To those who think this is a duplicate: the linked other question did not really address thread safety. And I have to develop for a situation where thread safety is a very serious concern -- we could have 32 cores running with every one of them attempting to create batches of a hundred or a thousand temp files apiece.

-- UPDATE: the book Secure Programming with Static Analysis by Chess and West says that GetTempFileName “suffers from an inherent underlying race condition”. So maybe that ain't safe either.

Paul Kienitz
  • 878
  • 6
  • 25
  • Possible duplicate of [Creating a temporary directory in Windows?](http://stackoverflow.com/questions/278439/creating-a-temporary-directory-in-windows) – nos Oct 31 '15 at 00:01
  • If this is a single process on a single server, any reason you don't just generate sequential names atomically using, say `Interlocked.Increment`? Nest them in a clean random directory (create once) if necessary. – Ian Mercer Oct 31 '15 at 00:53
  • NOT a single process. – Paul Kienitz Oct 31 '15 at 05:30

2 Answers2

4

If you want to create a unique temporary directory, a combination of Path.GetTempPath and Guid.NewGuid should do the trick quite nicely:

string GetUniqueTempPath()
{
    var tempPath = Path.GetTempPath();
    var uniqueDirectoryName = Guid.NewGuid().ToString();
    var uniqueTempPath = Path.Combine(tempPath, uniqueDirectoryName);

    return uniqueTempPath;
}

The fact that a GUID is being used as part of the path should eliminate any race condition as there's pretty much no chance that the composite directory name will exist so there's no (real) need to check prior to attempting to create it.

Rob
  • 45,296
  • 24
  • 122
  • 150
  • That would have a high probability of avoiding collisions, I guess, though no guarantee. But it does give me an idea for the file side of the question, though not the directory side: we can't atomically test-and-set at creation time except with `GetTempFileName`, but it looks like one operation that can do so is `RenameFile`. So if you create a unique name in the small space that GetTempFileName offers, it can be promoted into the large space offered by GUID names, with guarantee of noncollision, by renaming it. – Paul Kienitz Oct 30 '15 at 23:57
  • (continuing) That doesn't solve the directory case, though... and if I had threadsafe temp directories, that probably makes the problem of the namespace for files moot anyway, since I don't have to compete for names within my private folder. – Paul Kienitz Oct 30 '15 at 23:58
  • Referring to the documentation for `CoCreateGuid` (https://msdn.microsoft.com/library/windows/desktop/ms688568.aspx) which is what `Guid.NewGuid` wraps; it states "To a very high degree of certainty, this function returns a unique value – no other invocation, on the same or any other system (networked or not), should return the same value.". In short, you should be collision safe :) – Rob Oct 31 '15 at 00:02
  • I'd still feel a lot safer if we had an atomic way to actually test the filesystem, instead of just assuming that with enough entropy there'll probably be no matches there. – Paul Kienitz Oct 31 '15 at 00:21
  • @Paul using GUID should be more than enough but If you are such paranoid about this ,then see this answer http://stackoverflow.com/a/20483257/1030125 – ClearLogic Oct 31 '15 at 00:28
  • I'm paranoid because we've had real users encounter real errors from improbable collisions. – Paul Kienitz Oct 31 '15 at 00:28
  • On a further note, I've read that the number of GUIDs per second that a system can create is limited. This means it's probably safe to use at the directory level but maybe not at the file level. That's okay, though -- if I'm confident in the directory being distinct, the files inside it can use any old name scheme. – Paul Kienitz Nov 02 '15 at 20:20
0

@paul-kienitz FWIW: according to the documentation of CreateDirectory(), the function returns ERROR_ALREADY_EXISTS if the directory already existed, which gives you an atomic check for collisions. So you can safely create a new temp directory if you combine the random name approach (using CoCreateGuid()) with a limited number of retries (e.g. 9 retries), after which you fail. The retries will exponentially lower the collision probability to a value which even satisfies paranoids.

msp
  • 694
  • 6
  • 7