From 65adc53130a774f84ea945f17030f3f0b07b3d78 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 28 Feb 2026 21:30:57 +0000 Subject: [PATCH] Modify the inotify implementation so that inotify watches are only created after dnsmasq has changed permissions and userid. This means that the permissions used when creating the watches are the same as used for accessing watched files, which makes more sense and avoids odd and confusing error conditions. --- CHANGELOG | 6 ++++++ src/dnsmasq.c | 36 ++++++++++++++++++++++++++++-------- src/dnsmasq.h | 7 ++++++- src/inotify.c | 35 +++++++++++++++++++++++++---------- 4 files changed, 65 insertions(+), 19 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 485331b..b74f0d6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -28,6 +28,12 @@ version 2.93 relay is in use. Many thanks to Jørgen Søvik for help finding this bug. + Modify the inotify implementation so that inotify watches are + only created after dnsmasq has changed permissions and userid. + This means that the permissions used when creating the watches + are the same as used for accessing watched files, which makes + more sense and avoids odd and confusing error conditions. + version 2.92 Redesign the interaction between DNSSEC validation and per-domain diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 2bff016..4f9bf48 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -437,14 +437,6 @@ int main (int argc, char **argv) daemon->tcp_pipes[i] = -1; } -#ifdef HAVE_INOTIFY - if ((daemon->port != 0 && !option_bool(OPT_NO_RESOLV)) || - daemon->dynamic_dirs) - inotify_dnsmasq_init(); - else - daemon->inotifyfd = -1; -#endif - if (daemon->dump_file) #ifdef HAVE_DUMPFILE dump_init(); @@ -864,6 +856,14 @@ int main (int argc, char **argv) } #endif +#ifdef HAVE_INOTIFY + if ((daemon->port != 0 && !option_bool(OPT_NO_RESOLV)) || + daemon->dynamic_dirs) + inotify_dnsmasq_init(err_pipe[1]); + else + daemon->inotifyfd = -1; +#endif + /* Don't start logging malloc before logging is set up. */ daemon->log_malloc = option_bool(OPT_LOG_MALLOC); @@ -1545,6 +1545,26 @@ static void fatal_event(struct event_desc *ev, char *msg) /* fall through */ case EVENT_TIME_ERR: die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF); + + /* fall through */ + case EVENT_LINK_ERR: + die(_("cannot access path %s: %s" ), msg, EC_MISC); + + /* fall through */ + case EVENT_INOTFY_ERR: + die(_("failed to create inotify: %s" ), NULL, EC_MISC); + + /* fall through */ + case EVENT_TMSL_ERR: + die(_("too many symlinks following %s"), msg, EC_MISC); + + /* fall through */ + case EVENT_RESOLV_ERR: + die(_("directory %s for resolv-file is missing, cannot poll"), msg, EC_MISC); + + /* fall through */ + case EVENT_IFILE_ERR: + die(_("failed to create inotify for %s: %s"), msg, EC_MISC); } } diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 42cc4a7..510c88f 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -201,6 +201,11 @@ struct event_desc { #define EVENT_TIME_ERR 24 #define EVENT_SCRIPT_LOG 25 #define EVENT_TIME 26 +#define EVENT_LINK_ERR 27 +#define EVENT_INOTFY_ERR 28 +#define EVENT_TMSL_ERR 29 +#define EVENT_RESOLV_ERR 30 +#define EVENT_IFILE_ERR 31 /* Exit codes. */ #define EC_GOOD 0 @@ -1907,7 +1912,7 @@ int detect_loop(char *query, int type); /* inotify.c */ #ifdef HAVE_INOTIFY -void inotify_dnsmasq_init(void); +void inotify_dnsmasq_init(int errfd); int inotify_check(time_t now); void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz); #endif diff --git a/src/inotify.c b/src/inotify.c index f70daea..2ec6e39 100644 --- a/src/inotify.c +++ b/src/inotify.c @@ -40,7 +40,7 @@ static char *inotify_buffer; 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) +static char *my_readlink(int errfd, char *path) { ssize_t rc, size = 64; char *buf; @@ -59,7 +59,10 @@ static char *my_readlink(char *path) return NULL; } else - die(_("cannot access path %s: %s"), path, EC_MISC); + { + send_event(errfd, EVENT_LINK_ERR, errno, path); + _exit(0); + } } else if (rc < size-1) { @@ -85,15 +88,18 @@ static char *my_readlink(char *path) } } -void inotify_dnsmasq_init() +void inotify_dnsmasq_init(int errfd) { struct resolvc *res; inotify_buffer = safe_malloc(INOTIFY_SZ); daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); if (daemon->inotifyfd == -1) - die(_("failed to create inotify: %s"), NULL, EC_MISC); - + { + send_event(errfd, EVENT_INOTFY_ERR, errno, NULL); + _exit(0); + } + if (daemon->port == 0 || option_bool(OPT_NO_RESOLV)) return; @@ -105,10 +111,13 @@ void inotify_dnsmasq_init() strcpy(path, res->name); /* Follow symlinks until we reach a non-symlink, or a non-existent file. */ - while ((new_path = my_readlink(path))) + while ((new_path = my_readlink(errfd, path))) { if (links-- == 0) - die(_("too many symlinks following %s"), res->name, EC_MISC); + { + send_event(errfd, EVENT_TMSL_ERR, 0, res->name); + _exit(0); + } free(path); path = new_path; } @@ -124,12 +133,18 @@ void inotify_dnsmasq_init() *d = '/'; if (res->wd == -1 && errno == ENOENT) - die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC); + { + send_event(errfd, EVENT_RESOLV_ERR, 0, res->name); + _exit(0); + } } if (res->wd == -1) - die(_("failed to create inotify for %s: %s"), res->name, EC_MISC); - + { + send_event(errfd, EVENT_IFILE_ERR, errno, res->name); + _exit(0); + } + } }