tmpfiles: apply mode and owner of e and f/F entries through an fd

Same treatment as d/D got: e went through fisdir() and a path based
chmod/chown, f/F did the chmod/chown by path after closing the file.
Both now work on the open fd, so the mode and owner end up on the
directory or file that was just checked or written.  f/F use open(2)
directly, which lets a plain f rely on O_EXCL for create-if-missing
instead of the earlier stat().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
(cherry picked from commit 75739fbeef)
This commit is contained in:
Joachim Wiberg
2026-09-27 17:30:16 +02:00
parent 9bfbdc6bac
commit 88a9e6575b
3 changed files with 68 additions and 48 deletions
+45 -37
View File
@@ -24,6 +24,7 @@
#include "config.h" /* Generated by configure script */
#include <dirent.h>
#include <fcntl.h>
#include <ftw.h>
#include <getopt.h>
#include <glob.h>
@@ -566,58 +567,65 @@ static void tmpfiles(char *line)
umask(omask);
break;
}
case 'e':
case 'e': {
int uid, gid;
if (glob(path, GLOB_NOESCAPE, NULL, &gl))
break;
uid = parse_uid(user);
gid = parse_gid(group);
if (gid < 0)
gid = 0;
for (size_t i = 0; i < gl.gl_pathc; i++) {
char *p = gl.gl_pathv[i];
int uid, gid;
/* e only adjusts existing directories */
if (!fisdir(p))
continue;
uid = parse_uid(user);
gid = parse_gid(group);
if (gid < 0)
gid = 0;
if (mode)
chmod(p, mode);
if (uid >= 0 && chown(p, uid, gid))
warn("Failed chown(%s, %d, %d)", p, uid, gid);
/* skip non-directories */
if (dirperm(p, mode, uid, gid) && errno != ENOTDIR)
warn("Failed setting mode/owner on %s", p);
}
break;
}
case 'f':
case 'F':
mkparent(path, 0755);
case 'F': {
int flags = O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC;
int fd, uid, gid;
if (type[1] == '+' || type[0] == 'F') {
/* f+/F will create or truncate the file */
fp = fopen(path, "w+");
flags |= O_TRUNC; /* create or truncate */
} else {
/* f will create the file if it doesn't exist */
if (strc)
fp = fopen(path, "w");
if (!strc)
break; /* create only if missing */
flags |= O_EXCL;
}
if (fp) {
int uid, gid;
write_arg(fp, arg);
rc = fclose(fp);
/* Apply mode and ownership */
if (mode)
chmod(path, mode);
uid = parse_uid(user);
gid = parse_gid(group);
if (gid < 0)
gid = 0;
if (uid >= 0 && chown(path, uid, gid))
warn("Failed chown(%s, %d, %d)", path, uid, gid);
mkparent(path, 0755);
fd = open(path, flags, 0666);
if (fd < 0) {
if (errno != EEXIST)
warn("Failed creating %s", path);
break;
}
fp = fdopen(fd, "w");
if (!fp) {
close(fd);
break;
}
write_arg(fp, arg);
uid = parse_uid(user);
gid = parse_gid(group);
if (gid < 0)
gid = 0;
if (fdperm(fd, mode, uid, gid))
warn("Failed setting mode/owner on %s", path);
rc = fclose(fp);
break;
}
case 'l': /* Finit extension, like 'L' but only if target exists */
if (!arg) {
paste(buf, sizeof(buf), "/usr/share/factory", path);
+22 -11
View File
@@ -376,26 +376,37 @@ int rmrf(const char *path)
}
/*
* 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.
* Set mode and owner on an open fd, only what differs is touched, the
* file may be immutable. mode 0 skips the chmod, uid -1 the chown.
*/
int fdperm(int fd, mode_t mode, uid_t uid, gid_t gid)
{
struct stat st;
int rc;
rc = fstat(fd, &st);
if (!rc && mode && (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);
return rc;
}
/*
* Same for a directory that may already exist. Opened with
* O_NOFOLLOW | O_DIRECTORY, so the change lands on the directory
* itself and not on whatever a link at @path points to.
*/
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);
rc = fdperm(fd, mode, uid, gid);
close(fd);
return rc;
+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 fdperm (int fd, mode_t mode, uid_t uid, gid_t gid);
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);