statefiles: use dirfd in dhcpv6_ia_write_statefile()
authorDavid Härdeman <[email protected]>
Fri, 7 Nov 2025 16:53:22 +0000 (17:53 +0100)
committerÁlvaro Fernández Rojas <[email protected]>
Tue, 11 Nov 2025 07:30:00 +0000 (08:30 +0100)
Similarly to the hostsfile, use a dirfd to keep track of the directory
where the statefile is stored, simplifying the code a bit and avoiding
some more allocations.

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 8bc89531ebdfabbbe18c4f548e575d07d3544ef7..0cb6d55f4ab60c9e4b8dbcd0ac403ec2beefc40e 100644 (file)
@@ -39,6 +39,7 @@ struct config config = {
        .main_dhcpv4 = false,
        .dhcp_cb = NULL,
        .dhcp_statefile = NULL,
+       .dhcp_statedir_fd = -1,
        .dhcp_hostsfile = NULL,
        .dhcp_hostsdir_fd = -1,
        .ra_piofolder = NULL,
@@ -2308,9 +2309,16 @@ void odhcpd_reload(void)
        uci_unload(uci, system);
 
        if (config.dhcp_statefile) {
-               char *path = strdupa(config.dhcp_statefile);
+               char *dir = dirname(strdupa(config.dhcp_statefile));
+               char *file = basename(config.dhcp_statefile);
 
-               mkdir_p(dirname(path), 0755);
+               memmove(config.dhcp_statefile, file, strlen(file) + 1);
+               mkdir_p(dir, 0755);
+
+               close(config.dhcp_statedir_fd);
+               config.dhcp_statedir_fd = open(dir, O_PATH | O_DIRECTORY | O_CLOEXEC);
+               if (config.dhcp_statedir_fd < 0)
+                       error("Unable to open statedir: '%s': %m", dir);
        }
 
        if (config.dhcp_hostsfile) {
index 54bb9730baf04256752aac88a58d213b9813c2d4..337236d1361e170a8789dcbfd51adbe3b33b06c7 100644 (file)
@@ -201,7 +201,9 @@ struct config {
        bool enable_tz;
        bool main_dhcpv4;
        char *dhcp_cb;
+
        char *dhcp_statefile;
+       int dhcp_statedir_fd;
        char *dhcp_hostsfile;
        int dhcp_hostsdir_fd;
 
index 5cb4b5df0955397e8ecdad240e171ed3f719987e..0e8694002287c84fd71576e8a37995ec35fcab8d 100644 (file)
@@ -224,60 +224,38 @@ err:
 void dhcpv6_ia_write_statefile(void)
 {
        struct write_ctxt ctxt;
-       unsigned statefile_strlen = strlen(config.dhcp_statefile) + 1;
-       unsigned tmp_statefile_strlen = statefile_strlen + 1; /* space for . */
-       char *tmp_statefile = alloca(tmp_statefile_strlen);
-
-       char *dir_statefile;
-       char *base_statefile;
-       char *pdir_statefile;
-       char *pbase_statefile;
-
+       size_t tmp_statefile_strlen;
+       char *tmp_statefile;
        time_t now = odhcpd_time(), wall_time = time(NULL);
-       int fd, ret;
        char leasebuf[512];
+       int fd;
 
-       if (!config.dhcp_statefile)
+       if (config.dhcp_statedir_fd < 0 || !config.dhcp_statefile)
                return;
 
-       md5_begin(&ctxt.md5);
-       dir_statefile = strndup(config.dhcp_statefile, statefile_strlen);
-       base_statefile = strndup(config.dhcp_statefile, statefile_strlen);
-
-       pdir_statefile = dirname(dir_statefile);
-       pbase_statefile = basename(base_statefile);
-
-       snprintf(tmp_statefile, tmp_statefile_strlen, "%s/.%s", pdir_statefile, pbase_statefile);
-
-       free(dir_statefile);
-       free(base_statefile);
+       tmp_statefile_strlen = strlen(config.dhcp_statefile) + 2;
+       tmp_statefile = alloca(tmp_statefile_strlen);
+       sprintf(tmp_statefile, ".%s", config.dhcp_statefile);
 
-       fd = open(tmp_statefile, O_CREAT | O_WRONLY | O_CLOEXEC, 0644);
+       fd = openat(config.dhcp_statedir_fd, tmp_statefile, 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;
 
        ctxt.buf = leasebuf;
        ctxt.buf_len = sizeof(leasebuf);
+       md5_begin(&ctxt.md5);
 
        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))
@@ -364,7 +342,8 @@ void dhcpv6_ia_write_statefile(void)
        uint8_t newmd5[16];
        md5_end(newmd5, &ctxt.md5);
 
-       rename(tmp_statefile, config.dhcp_statefile);
+       renameat(config.dhcp_statedir_fd, tmp_statefile,
+                config.dhcp_statedir_fd, config.dhcp_statefile);
 
        if (memcmp(newmd5, statemd5, sizeof(newmd5))) {
                memcpy(statemd5, newmd5, sizeof(statemd5));
@@ -379,5 +358,10 @@ void dhcpv6_ia_write_statefile(void)
                        }
                }
        }
-}
 
+       return;
+
+err:
+       error("Unable to write statefile: %m");
+       close(fd);
+}