Filed under the category: “useful programming tips for problems that bit my ass”
The wise men from old said: Thou shalt not check if a directory or file existeth before creating it.
Well, or something like that.
I often see code that looks like this:
if (!g_file_test (some_directory, G_FILE_TEST_EXISTS)) { if (g_mkdir (some_directory, 0700) != 0) { g_warning ("Unable to create `%s'", some_directory); return; } } else do_something_with_some_directory (some_directory);
or, worse, code that looks like this:
if (!g_file_test (some_file, G_FILE_TEST_EXISTS)) { symlink (some_other_file, some_file); }
Despite a BFW on the g_file_test()
API reference page.
These code snippes are wrong, because they contains an implicit race: what if the directory/file you are checking suddenly disappears (or is created)? Also, blindly invoking functions like g_mkdir()
or symlink
without checking the returned value (“hey, I just checked so how can it fail?”) is looking for troubles; finally: when a system call fails, you should always report the error message the OS sets: too many things can go wrong, and if you can get a meaningful message then you’ll be able to debug faster.
The solution for these problems is: shoot first, then ask the questions. Or, in other words, execute the function inside the if
block and act according to the results. For this to happen, you’ll often need to rely on errno.h
and g_strerror()
:
#include <errno.h> ... if (g_mkdir (some_directory, 0700) == -1) { if (errno != EEXIST) { g_warning ("Unable to create `%s': %s", some_directory, g_strerror (errno)); return; } } do_something_with_some_directory (some_directory);
and:
#include <errno.h> ... if (symlink (some_other_file, some_file) == -1) { if (errno != EEXIST) { g_warning ("Unable to create a link from`%s' to `%s': %s", some_other_file, some_file, g_strerror (errno)); return; } }
At the end of the if
block we are guaranteed that the file or the directory does indeed exist either because we successfully created it or because the system call failed and errno
was set to EEXIST
; inside the if
, instead, we can deal with failures, using various error trapping branches based on the value of errno
(just remember to store it if you’re going to use it multiple times).