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>
(cherry picked from commit 3a72d1263c)
This commit is contained in:
Sam Brkopac
2026-09-27 17:26:08 +02:00
committed by Joachim Wiberg
parent 417abf9000
commit 9bfbdc6bac
3 changed files with 35 additions and 10 deletions
+5 -5
View File
@@ -557,11 +557,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;
+29 -5
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
@@ -374,6 +375,32 @@ int rmrf(const char *path)
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. uid
* -1 skips the chown.
*/
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 && uid != (uid_t)-1 && (st.st_uid != uid || st.st_gid != gid))
rc = fchown(fd, uid, gid);
close(fd);
return rc;
}
/*
* Like mksubsys() but with the ids already resolved, uid -1 skips the
* chown. Parents are created 0755, only the leaf gets @mode.
@@ -386,11 +413,8 @@ int mksubsysd(const char *dir, mode_t mode, uid_t uid, gid_t gid)
omask = umask(0);
rc = mkpath(dir, 0755);
if (!rc) {
rc = chmod(dir, mode);
if (!rc && uid != (uid_t)-1 && chown(dir, uid, gid))
err(1, "Failed chown(%s, %d, %d)", dir, (int)uid, (int)gid);
}
if (!rc && dirperm(dir, mode, uid, gid))
warn("Failed setting mode/owner on %s", dir);
umask(omask);
+1
View File
@@ -71,6 +71,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 mksubsysd (const char *dir, mode_t mode, uid_t uid, gid_t gid);
int chownr (const char *path, uid_t uid, gid_t gid);