From: Simon Kelley Date: Mon, 6 Jul 2015 20:48:49 +0000 (+0100) Subject: Fix inotify code to handle dangling symlinks better. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=362c9303da25640ce8e73ee0b2902005f862c84f;p=people%2Fms%2Fdnsmasq.git Fix inotify code to handle dangling symlinks better. --- diff --git a/CHANGELOG b/CHANGELOG index 76abc14..0eb3c0c 100644 --- 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 diff --git a/src/inotify.c b/src/inotify.c index 9422066..f3e5c3d 100644 --- a/src/inotify.c +++ b/src/inotify.c @@ -18,6 +18,7 @@ #ifdef HAVE_INOTIFY #include +#include /* 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 @@ -35,10 +36,56 @@ 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); + } }