statefiles: simplify dhcpv6_ia_write_hostsfile()
authorDavid Härdeman <[email protected]>
Fri, 7 Nov 2025 16:25:40 +0000 (17:25 +0100)
committerÁlvaro Fernández Rojas <[email protected]>
Tue, 11 Nov 2025 07:29:38 +0000 (08:29 +0100)
Use a dirfd to keep track of the hostsfile directory, also make sure
that the directory is created if necessary (this is in preparation for
writing per-interface hostsfiles).

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
src/config.c
src/odhcpd.h
src/statefiles.c

index 39a6db530c43ceaeea76d9fd5744faac92bbbe9b..8bc89531ebdfabbbe18c4f548e575d07d3544ef7 100644 (file)
@@ -40,6 +40,7 @@ struct config config = {
        .dhcp_cb = NULL,
        .dhcp_statefile = NULL,
        .dhcp_hostsfile = NULL,
+       .dhcp_hostsdir_fd = -1,
        .ra_piofolder = NULL,
        .ra_piofolder_fd = -1,
        .uci_cfgdir = NULL,
@@ -2312,6 +2313,19 @@ void odhcpd_reload(void)
                mkdir_p(dirname(path), 0755);
        }
 
+       if (config.dhcp_hostsfile) {
+               char *dir = dirname(strdupa(config.dhcp_hostsfile));
+               char *file = basename(config.dhcp_hostsfile);
+
+               memmove(config.dhcp_hostsfile, file, strlen(file) + 1);
+               mkdir_p(dir, 0755);
+
+               close(config.dhcp_hostsdir_fd);
+               config.dhcp_hostsdir_fd = open(dir, O_PATH | O_DIRECTORY | O_CLOEXEC);
+               if (config.dhcp_hostsdir_fd < 0)
+                       error("Unable to open hostsdir '%s': %m", dir);
+       }
+
        if (config.ra_piofolder) {
                char *path = strdupa(config.ra_piofolder);
 
index d13d0782f5feef68e1cfc121e29c8b601646beef..54bb9730baf04256752aac88a58d213b9813c2d4 100644 (file)
@@ -203,6 +203,7 @@ struct config {
        char *dhcp_cb;
        char *dhcp_statefile;
        char *dhcp_hostsfile;
+       int dhcp_hostsdir_fd;
 
        char *ra_piofolder;
        int ra_piofolder_fd;
index 0dedbcad8f782b7932da4c18b429a1ae4a514552..a0954c45016c5d18e95c0dc6933bd872e453e24a 100644 (file)
@@ -147,52 +147,32 @@ static void dhcpv6_write_ia_addr(struct in6_addr *addr, int prefix, _unused uint
 static void dhcpv6_ia_write_hostsfile(time_t now)
 {
        struct write_ctxt ctxt;
+       size_t tmp_hostsfile_strlen;
+       char *tmp_hostsfile;
+       int fd;
 
-       unsigned hostsfile_strlen = strlen(config.dhcp_hostsfile) + 1;
-       unsigned tmp_hostsfile_strlen = hostsfile_strlen + 1; /* space for . */
-       char *tmp_hostsfile = alloca(tmp_hostsfile_strlen);
-
-       char *dir_hostsfile;
-       char *base_hostsfile;
-       char *pdir_hostsfile;
-       char *pbase_hostsfile;
-
-       int fd, ret;
-
-       dir_hostsfile = strndup(config.dhcp_hostsfile, hostsfile_strlen);
-       base_hostsfile = strndup(config.dhcp_hostsfile, hostsfile_strlen);
-
-       pdir_hostsfile = dirname(dir_hostsfile);
-       pbase_hostsfile = basename(base_hostsfile);
-
-       snprintf(tmp_hostsfile, tmp_hostsfile_strlen, "%s/.%s", pdir_hostsfile, pbase_hostsfile);
+       if (config.dhcp_hostsdir_fd < 0 || !config.dhcp_hostsfile)
+               return;
 
-       free(dir_hostsfile);
-       free(base_hostsfile);
+       tmp_hostsfile_strlen = strlen(config.dhcp_hostsfile) + 2;
+       tmp_hostsfile = alloca(tmp_hostsfile_strlen);
+       sprintf(tmp_hostsfile, ".%s", config.dhcp_hostsfile);
 
-       fd = open(tmp_hostsfile, O_CREAT | O_WRONLY | O_CLOEXEC, 0644);
+       fd = openat(config.dhcp_hostsdir_fd, tmp_hostsfile, O_CREAT | O_WRONLY | O_CLOEXEC, 0644);
        if (fd < 0)
-               return;
+               goto err;
 
-       ret = lockf(fd, F_LOCK, 0);
-       if (ret < 0) {
-               close(fd);
-               return;
-       }
+       if (lockf(fd, F_LOCK, 0) < 0)
+               goto err;
 
-       if (ftruncate(fd, 0) < 0) {}
+       if (ftruncate(fd, 0) < 0)
+               goto err;
 
        ctxt.fp = fdopen(fd, "w");
-       if (!ctxt.fp) {
-               close(fd);
-               return;
-       }
+       if (!ctxt.fp)
+               goto err;
 
        avl_for_each_element(&interfaces, ctxt.iface, avl) {
-               if (ctxt.iface->dhcpv6 != MODE_SERVER &&
-                               ctxt.iface->dhcpv4 != MODE_SERVER)
-                       continue;
-
                if (ctxt.iface->dhcpv6 == MODE_SERVER) {
                        list_for_each_entry(ctxt.c, &ctxt.iface->ia_assignments, head) {
                                if (!(ctxt.c->flags & OAF_BOUND))
@@ -232,8 +212,13 @@ static void dhcpv6_ia_write_hostsfile(time_t now)
        }
 
        fclose(ctxt.fp);
+       renameat(config.dhcp_hostsdir_fd, tmp_hostsfile,
+                config.dhcp_hostsdir_fd, config.dhcp_hostsfile);
+       return;
 
-       rename(tmp_hostsfile, config.dhcp_hostsfile);
+err:
+       error("Unable to write hostsfile: %m");
+       close(fd);
 }
 
 void dhcpv6_ia_write_statefile(void)
@@ -384,8 +369,7 @@ void dhcpv6_ia_write_statefile(void)
                if (memcmp(newmd5, statemd5, sizeof(newmd5))) {
                        memcpy(statemd5, newmd5, sizeof(statemd5));
 
-                       if (config.dhcp_hostsfile)
-                               dhcpv6_ia_write_hostsfile(now);
+                       dhcpv6_ia_write_hostsfile(now);
 
                        if (config.dhcp_cb) {
                                char *argv[2] = {config.dhcp_cb, NULL};