src/common.c: Fixed a race condition in check_create_dir().
authorSebastian Harl <sh@tokkee.org>
Tue, 24 Feb 2009 10:27:22 +0000 (11:27 +0100)
committerSebastian Harl <sh@tokkee.org>
Tue, 24 Feb 2009 10:37:17 +0000 (11:37 +0100)
Between checking for the existence of a directory using stat() and creating
the directory using mkdir(), another thread might have already created the
directory thus causing mkdir() to fail with errno == EEXIST. This case is now
handled sanely, no longer causing the function (and thus some write callback)
to fail in this case.

Most likely, this only happens during startup when creating the data
directories - later, no two threads should try to create the same directory.

Interestingly enough, I frequently encountered this issue on a single core
machine.

src/common.c

index 119d284..182f923 100644 (file)
@@ -433,7 +433,8 @@ int check_create_dir (const char *file_orig)
                 */
                if (fields[i][0] == '.')
                {
-                       ERROR ("Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
+                       ERROR ("Cowardly refusing to create a directory that "
+                                       "begins with a `.' (dot): `%s'", file_orig);
                        return (-2);
                }
 
@@ -448,32 +449,42 @@ int check_create_dir (const char *file_orig)
                        return (-1);
                }
 
-               if (stat (dir, &statbuf) == -1)
-               {
-                       if (errno == ENOENT)
+               while (42) {
+                       if (stat (dir, &statbuf) == -1)
                        {
-                               if (mkdir (dir, 0755) == -1)
+                               if (errno == ENOENT)
                                {
+                                       if (mkdir (dir, 0755) == 0)
+                                               break;
+
+                                       /* this might happen, if a different thread created
+                                        * the directory in the meantime
+                                        * => call stat() again to check for S_ISDIR() */
+                                       if (EEXIST == errno)
+                                               continue;
+
                                        char errbuf[1024];
                                        ERROR ("check_create_dir: mkdir (%s): %s", dir,
                                                        sstrerror (errno,
                                                                errbuf, sizeof (errbuf)));
                                        return (-1);
                                }
+                               else
+                               {
+                                       char errbuf[1024];
+                                       ERROR ("check_create_dir: stat (%s): %s", dir,
+                                                       sstrerror (errno, errbuf,
+                                                               sizeof (errbuf)));
+                                       return (-1);
+                               }
                        }
-                       else
+                       else if (!S_ISDIR (statbuf.st_mode))
                        {
-                               char errbuf[1024];
-                               ERROR ("stat (%s): %s", dir,
-                                               sstrerror (errno, errbuf,
-                                                       sizeof (errbuf)));
+                               ERROR ("check_create_dir: `%s' exists but is not "
+                                               "a directory!", dir);
                                return (-1);
                        }
-               }
-               else if (!S_ISDIR (statbuf.st_mode))
-               {
-                       ERROR ("stat (%s): Not a directory!", dir);
-                       return (-1);
+                       break;
                }
        }