]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Fix inotify code to handle dangling symlinks better.
authorSimon Kelley <simon@thekelleys.org.uk>
Mon, 6 Jul 2015 20:48:49 +0000 (21:48 +0100)
committerSimon Kelley <simon@thekelleys.org.uk>
Mon, 6 Jul 2015 20:48:49 +0000 (21:48 +0100)
CHANGELOG
src/inotify.c

index 76abc142aa1595f94ce9898246813eb7f982c0f0..0eb3c0c987201a2d78f50fde237e90721400e577 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,7 +2,10 @@ version 2.74
             Fix reversion in 2.73 where --conf-file would attempt to
            read the default file, rather than no file.
 
-       
+           Fix inotify code to handle dangling symlinks better and
+           not SEGV in some circumstances.
+
+
 version 2.73
             Fix crash at startup when an empty suffix is supplied to
            --conf-dir, also trivial memory leak. Thanks to 
index 9422066257f55afceb13806910b1a083c2ef655f..f3e5c3d1689f1e66a95bce7e702c62b805ae8bf6 100644 (file)
@@ -18,6 +18,7 @@
 #ifdef HAVE_INOTIFY
 
 #include <sys/inotify.h>
+#include <sys/param.h> /* For MAXSYMLINKS */
 
 /* the strategy is to set a inotify on the directories containing
    resolv files, for any files in the directory which are close-write 
 static char *inotify_buffer;
 #define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
 
+/* If path is a symbolic link, return the path it
+   points to, made absolute if relative.
+   If path doesn't exist or is not a symlink, return NULL.
+   Return value is malloc'ed */
+static char *my_readlink(char *path)
+{
+  ssize_t rc;
+  size_t size = 64;
+  char *buf;
+
+  while (1)
+    {
+      buf = safe_malloc(size);
+      rc = readlink(path, buf, size);
+      
+      if (rc == -1)
+       {
+         /* Not link or doesn't exist. */
+         if (errno == EINVAL || errno == ENOENT)
+           return NULL;
+         else
+           die(_("cannot access path %s: %s"), path, EC_MISC);
+       }
+      else if (rc < size-1)
+       {
+         char *d;
+         
+         buf[rc] = 0;
+         if (buf[0] != '/' && ((d = strrchr(path, '/'))))
+           {
+             /* Add path to relative link */
+             char *new_buf = safe_malloc((d - path) + strlen(buf) + 2);
+             *(d+1) = 0;
+             strcpy(new_buf, path);
+             strcat(new_buf, buf);
+             free(buf);
+             buf = new_buf;
+           }
+         return buf;
+       }
+
+      /* Buffer too small, increase and retry */
+      size += 64;
+      free(buf);
+    }
+}
+
 void inotify_dnsmasq_init()
 {
   struct resolvc *res;
-
   inotify_buffer = safe_malloc(INOTIFY_SZ);
   daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
   
@@ -47,19 +94,22 @@ void inotify_dnsmasq_init()
   
   for (res = daemon->resolv_files; res; res = res->next)
     {
-      char *d = NULL, *path;
-      
-      if (!(path = realpath(res->name, NULL)))
+      char *d, *new_path, *path = safe_malloc(strlen(res->name) + 1);
+      int links = MAXSYMLINKS;
+
+      strcpy(path, res->name);
+
+      /* Follow symlinks until we reach a non-symlink, or a non-existant file. */
+      while ((new_path = my_readlink(path)))
        {
-         /* realpath will fail if the file doesn't exist, but
-            dnsmasq copes with missing files, so fall back 
-            and assume that symlinks are not in use in that case. */
-         if (errno == ENOENT)
-           path = res->name;
-         else
-           die(_("cannot cannonicalise resolv-file %s: %s"), res->name, EC_MISC); 
+         if (links-- == 0)
+           die(_("too many symlinks following %s"), res->name, EC_MISC);
+         free(path);
+         path = new_path;
        }
-      
+
+      res->wd = -1;
+
       if ((d = strrchr(path, '/')))
        {
          *d = 0; /* make path just directory */
@@ -70,10 +120,11 @@ void inotify_dnsmasq_init()
          
          if (res->wd == -1 && errno == ENOENT)
            die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
-         
-         if (res->wd == -1)
-           die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
-       }
+       }         
+        
+      if (res->wd == -1)
+       die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
+       
     }
 }