tmpfiles: set mode and owner of d/D directories through an fd

makedir() swallows EEXIST, so the chmod branch for directories that
already existed never ran, while chown() ran every time.  On finix
/var/empty is immutable, which gave a warning on every boot even
though the owner was already correct.

mksubsys() is the original of this code and has the same shape, the
dbus plugin uses it for /tmp/dbus.  Both now go through dirperm(),
which opens the directory and uses fstat/fchmod/fchown on the fd,
changing only what differs.  A failed chown in mksubsys() is a warning
now rather than err(1), PID 1 should not exit over a directory it
cannot adjust.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Sam Brkopac
2026-09-27 17:25:50 +02:00
committed by Joachim Wiberg
parent 7e2b154f9b
commit 3a72d1263c
3 changed files with 34 additions and 9 deletions
+5 -5
View File
@@ -586,11 +586,11 @@ static void tmpfiles(char *line)
if (gid < 0)
gid = 0;
rc = makedir(path, mode ?: 0755);
if (rc && errno == EEXIST)
rc = chmod(path, mode ?: 0755);
if (chown(path, uid, gid))
warn("Failed chown(%s, %d, %d)", path, uid, gid);
if (!mode)
mode = 0755;
rc = makedir(path, mode);
if (!rc && dirperm(path, mode, uid, gid))
warn("Failed setting mode/owner on %s", path);
}
umask(omask);
break;
+28 -4
View File
@@ -25,6 +25,7 @@
#include <ctype.h> /* isprint() */
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#ifdef HAVE_MNTENT_H
@@ -280,6 +281,31 @@ int getcgroup(char *buf, size_t len)
return 0;
}
/*
* Set mode and owner on a directory that may already exist. Works on
* an fd opened with O_NOFOLLOW | O_DIRECTORY, so the change lands on
* the directory itself and not on whatever a link at @path points to.
* Only what differs is touched, the directory may be immutable.
*/
int dirperm(const char *path, mode_t mode, uid_t uid, gid_t gid)
{
struct stat st;
int fd, rc;
fd = open(path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd < 0)
return -1;
rc = fstat(fd, &st);
if (!rc && (st.st_mode & 07777) != mode)
rc = fchmod(fd, mode);
if (!rc && (st.st_uid != uid || st.st_gid != gid))
rc = fchown(fd, uid, gid);
close(fd);
return rc;
}
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
{
mode_t omask;
@@ -295,10 +321,8 @@ int mksubsys(const char *dir, mode_t mode, char *user, char *group)
gid = 0;
rc = makedir(dir, mode);
if (rc && errno == EEXIST)
rc = chmod(dir, mode);
if (chown(dir, uid, gid))
err(1, "Failed chown(%s, %d, %d)", dir, uid, gid);
if (!rc && dirperm(dir, mode, uid, gid))
warn("Failed setting mode/owner on %s", dir);
}
umask(omask);
+1
View File
@@ -68,6 +68,7 @@ int getgroup (const char *group);
int getcuser (char *buf, size_t len);
int getcgroup (char *buf, size_t len);
int dirperm (const char *path, mode_t mode, uid_t uid, gid_t gid);
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
int fnread (char *buf, size_t len, char *fmt, ...) __attribute__ ((format (printf, 3, 4)));