diff --git a/src/tmpfiles.c b/src/tmpfiles.c index 46c8a24b..a209f51a 100644 --- a/src/tmpfiles.c +++ b/src/tmpfiles.c @@ -24,6 +24,7 @@ #include "config.h" /* Generated by configure script */ #include +#include #include #include #include @@ -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); diff --git a/src/util.c b/src/util.c index 161bfad8..72c59c1b 100644 --- a/src/util.c +++ b/src/util.c @@ -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; diff --git a/src/util.h b/src/util.h index 6e2cc053..7780dc49 100644 --- a/src/util.h +++ b/src/util.h @@ -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);