Both plain opendir() and glob() will bump access time. Privileged
option O_NOATIME can be used to prevent the access time from being
updated. We already used it for subdirectories of the directories
which we were cleaning up. But for the directories specified directly
in the config files, we wouldn't do that. This means that,
paradoxically, our own temporary directories for PrivateTmp would stay
around forever, as long as one let systemd-tmpfiles-clean.service run
regularly, because they had their own glob patterns specified.
https://bugzilla.redhat.com/show_bug.cgi?id=
1183684
+ <refsect1>
+ <title>Unprivileged --cleanup operation</title>
+
+ <para><command>systemd-tmpfiles</command> tries to
+ avoid changing the access and modification times on
+ the directories it accesses, which requires
+ <constant>CAP_ADMIN</constant> privileges. When
+ running as non-root, directories which are checked for
+ files to clean up will have their access time bumped,
+ which might prevent their cleanup.
+ </para>
+ </refsect1>
+
<refsect1>
<title>Exit status</title>
<refsect1>
<title>Exit status</title>
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wshadow\"")
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wshadow\"")
+#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
+
#define REENABLE_WARNING \
_Pragma("GCC diagnostic pop")
#define REENABLE_WARNING \
_Pragma("GCC diagnostic pop")
+static DIR* xopendirat_nomod(int dirfd, const char *path) {
+ DIR *dir;
+
+ dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME);
+ if (!dir) {
+ log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m",
+ dirfd == AT_FDCWD ? "" : "sub", path);
+ if (errno == EPERM) {
+ dir = xopendirat(dirfd, path, O_NOFOLLOW);
+ if (!dir)
+ log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m",
+ dirfd == AT_FDCWD ? "" : "sub", path);
+ }
+ }
+
+ return dir;
+}
+
+static DIR* opendir_nomod(const char *path) {
+ return xopendirat_nomod(AT_FDCWD, path);
+}
+
static int dir_cleanup(
Item *i,
const char *p,
static int dir_cleanup(
Item *i,
const char *p,
_cleanup_closedir_ DIR *sub_dir;
int q;
_cleanup_closedir_ DIR *sub_dir;
int q;
- sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
+ sub_dir = xopendirat_nomod(dirfd(d), dent->d_name);
if (!sub_dir) {
if (errno != ENOENT)
r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path);
if (!sub_dir) {
if (errno != ENOENT)
r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path);
/* This returns the first error we run into, but nevertheless
* tries to go on */
/* This returns the first error we run into, but nevertheless
* tries to go on */
- d = opendir(path);
- if (!d) {
- log_debug_errno(errno, "Cannot open directory \"%s\": %m", path);
+ d = opendir_nomod(path);
+ if (!d)
return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
for (;;) {
_cleanup_free_ char *p = NULL;
for (;;) {
_cleanup_free_ char *p = NULL;
}
static int glob_item(Item *i, action_t action, bool recursive) {
}
static int glob_item(Item *i, action_t action, bool recursive) {
- _cleanup_globfree_ glob_t g = {};
+DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES
+DISABLE_WARNING_DECLARATION_AFTER_STATEMENT
+ _cleanup_globfree_ glob_t g = {
+ .gl_closedir = closedir,
+ .gl_readdir = readdir,
+ .gl_opendir = opendir_nomod,
+ .gl_lstat = lstat,
+ .gl_stat = stat,
+ };
+REENABLE_WARNING
+REENABLE_WARNING
int r = 0, k;
char **fn;
errno = 0;
int r = 0, k;
char **fn;
errno = 0;
- k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+ k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g);
if (k != 0 && k != GLOB_NOMATCH)
return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path);
if (k != 0 && k != GLOB_NOMATCH)
return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path);
+ d = opendir_nomod(instance);
if (!d) {
if (errno == ENOENT || errno == ENOTDIR) {
log_debug_errno(errno, "Directory \"%s\": %m", instance);
if (!d) {
if (errno == ENOENT || errno == ENOTDIR) {
log_debug_errno(errno, "Directory \"%s\": %m", instance);