Description
createDirectoryIfMissing True is a larger hammer than called for under many circumstances, but by being the only one in this module, it is in my experience very easy to reach for it without much thought. I suggest adding an alternative such as:
createDirectoryUnder
:: FilePath --^ a top directory, which must already exist
-> FilePath --^ create this directory, and any missing intermediate directories before the top
-> IO ()
Here are some use cases for createDirectoryUnder:
-
On Linux, it's common for removable drives to be mounted at eg, /media/user/foo. If a program that uses createDirectoryIfMissing True has been passed a directory under there, and is operating on it, when the removable drive gets disconnected, it will be unmounted, and the mount point removed. But, /media/user often remains, and is owned by the non-root user. So, the program may recreate /media/user/foo, and proceed with IO that was supposed to go to the removable drive. Data loss can result, things written there can prevent a later re-mount of the drive, etc.
-
A program that has generated a temporary directory in a secure manner (eg mode 700) is writing to paths under /tmp/foo.pid/. If the user or some automated process decides to clean up /tmp while the program is still running, a later call to createDirectoryIfMissing True "/tmp/foo.pid/bar/baz" will re-create /tmp/foo.pid/, but in doing so it has opened a security hole; the directory's mode is no longer secure and an attacker can read files that the program writes there. (Other security holes are also possible.)
I have an implementation of createDirectoryUnder in git-annex, but relies on some other code from it, and probably gets some of the race conditions wrong that createDirectoryIfMissing deals with, so would be at best a hint at an implementation suitable for directory.
While implementing it, I found it made sense for it to throw an exception if the top directory does not exist, but not to create the top directory. It also makes sense for it to fail if the directory to be created is not actually located under the top directory. And, either or both directories can be relative (to the current directory, not one-another!), which complicates the implementation.