Fix inotify code to handle dangling symlinks better.

This commit is contained in:
Simon Kelley
2015-07-06 21:48:49 +01:00
parent 5e95a552ee
commit 362c9303da
2 changed files with 71 additions and 17 deletions

View File

@@ -2,6 +2,9 @@ version 2.74
Fix reversion in 2.73 where --conf-file would attempt to Fix reversion in 2.73 where --conf-file would attempt to
read the default file, rather than no file. 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 version 2.73
Fix crash at startup when an empty suffix is supplied to Fix crash at startup when an empty suffix is supplied to

View File

@@ -18,6 +18,7 @@
#ifdef HAVE_INOTIFY #ifdef HAVE_INOTIFY
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/param.h> /* For MAXSYMLINKS */
/* the strategy is to set a inotify on the directories containing /* the strategy is to set a inotify on the directories containing
resolv files, for any files in the directory which are close-write resolv files, for any files in the directory which are close-write
@@ -35,10 +36,56 @@
static char *inotify_buffer; static char *inotify_buffer;
#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1) #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() void inotify_dnsmasq_init()
{ {
struct resolvc *res; struct resolvc *res;
inotify_buffer = safe_malloc(INOTIFY_SZ); inotify_buffer = safe_malloc(INOTIFY_SZ);
daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); 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) for (res = daemon->resolv_files; res; res = res->next)
{ {
char *d = NULL, *path; char *d, *new_path, *path = safe_malloc(strlen(res->name) + 1);
int links = MAXSYMLINKS;
if (!(path = realpath(res->name, NULL))) 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 if (links-- == 0)
dnsmasq copes with missing files, so fall back die(_("too many symlinks following %s"), res->name, EC_MISC);
and assume that symlinks are not in use in that case. */ free(path);
if (errno == ENOENT) path = new_path;
path = res->name;
else
die(_("cannot cannonicalise resolv-file %s: %s"), res->name, EC_MISC);
} }
res->wd = -1;
if ((d = strrchr(path, '/'))) if ((d = strrchr(path, '/')))
{ {
*d = 0; /* make path just directory */ *d = 0; /* make path just directory */
@@ -70,10 +120,11 @@ void inotify_dnsmasq_init()
if (res->wd == -1 && errno == ENOENT) if (res->wd == -1 && errno == ENOENT)
die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC); 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);
} }
} }