1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-31 14:50:15 +03:00

Merge pull request #11482 from poettering/tmpfiles-bsd-lock

adds a fully safe way how apps can pin files into /tmp temporarily, excepting them from the tmpfiles aging algorithm, based on BSD file locks on dirs we descend into
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2019-02-20 18:34:03 +01:00 committed by GitHub
commit ac5c138137
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 28 deletions

View File

@ -545,6 +545,14 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
(ctime). Any of these three (or two) values will prevent cleanup
if it is more recent than the current time minus the age
field.</para>
<para>Note that while the aging algorithm is run a 'shared' BSD file lock (see <citerefentry
project='man-pages'><refentrytitle>flock</refentrytitle><manvolnum>2</manvolnum></citerefentry>) is
taken on each directory the algorithm descends into (and each directory below that, and so on). If the
aging algorithm finds a lock is already taken on some directory, it (and everything below it) is
skipped. Applications may use this to temporarily exclude certain directory subtrees from the aging
algorithm: the applications can take a BSD file lock themselves, and as long as they keep it aging of
the directory and everything below it is disabled.</para>
</refsect2>
<refsect2>

View File

@ -12,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <sysexits.h>
@ -48,6 +49,7 @@
#include "path-lookup.h"
#include "path-util.h"
#include "pretty-print.h"
#include "rlimit-util.h"
#include "rm-rf.h"
#include "selinux-util.h"
#include "set.h"
@ -525,7 +527,6 @@ static int dir_cleanup(
bool keep_this_level) {
struct dirent *dent;
struct timespec times[2];
bool deleted = false;
int r = 0;
@ -580,42 +581,45 @@ static int dir_cleanup(
}
if (S_ISDIR(s.st_mode)) {
_cleanup_closedir_ DIR *sub_dir = NULL;
if (mountpoint &&
streq(dent->d_name, "lost+found") &&
s.st_uid == 0) {
log_debug("Ignoring \"%s\".", sub_path);
log_debug("Ignoring directory \"%s\".", sub_path);
continue;
}
if (maxdepth <= 0)
log_warning("Reached max depth on \"%s\".", sub_path);
else {
_cleanup_closedir_ DIR *sub_dir;
int q;
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);
r = log_warning_errno(errno, "Opening directory \"%s\" failed, ignoring: %m", sub_path);
continue;
}
if (flock(dirfd(sub_dir), LOCK_EX|LOCK_NB) < 0) {
log_debug_errno(errno, "Couldn't acquire shared BSD lock on directory \"%s\", skipping: %m", p);
continue;
}
q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
if (q < 0)
r = q;
}
/* Note: if you are wondering why we don't
* support the sticky bit for excluding
* directories from cleaning like we do it for
* other file system objects: well, the sticky
* bit already has a meaning for directories,
* so we don't want to overload that. */
/* Note: if you are wondering why we don't support the sticky bit for excluding
* directories from cleaning like we do it for other file system objects: well, the
* sticky bit already has a meaning for directories, so we don't want to overload
* that. */
if (keep_this_level) {
log_debug("Keeping \"%s\".", sub_path);
log_debug("Keeping directory \"%s\".", sub_path);
continue;
}
@ -642,13 +646,11 @@ static int dir_cleanup(
log_debug("Removing directory \"%s\".", sub_path);
if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
if (!IN_SET(errno, ENOENT, ENOTEMPTY))
r = log_error_errno(errno, "rmdir(%s): %m", sub_path);
r = log_warning_errno(errno, "Failed to remove directory \"%s\", ignoring: %m", sub_path);
} else {
/* Skip files for which the sticky bit is
* set. These are semantics we define, and are
* unknown elsewhere. See XDG_RUNTIME_DIR
* specification for details. */
/* Skip files for which the sticky bit is set. These are semantics we define, and are
* unknown elsewhere. See XDG_RUNTIME_DIR specification for details. */
if (s.st_mode & S_ISVTX) {
log_debug("Skipping \"%s\": sticky bit set.", sub_path);
continue;
@ -675,8 +677,7 @@ static int dir_cleanup(
continue;
}
/* Keep files on this level around if this is
* requested */
/* Keep files on this level around if this is requested */
if (keep_this_level) {
log_debug("Keeping \"%s\".", sub_path);
continue;
@ -710,11 +711,10 @@ static int dir_cleanup(
continue;
}
log_debug("unlink \"%s\"", sub_path);
log_debug("Removing \"%s\".", sub_path);
if (unlinkat(dirfd(d), dent->d_name, 0) < 0)
if (errno != ENOENT)
r = log_error_errno(errno, "unlink(%s): %m", sub_path);
r = log_warning_errno(errno, "Failed to remove \"%s\", ignoring: %m", sub_path);
deleted = true;
}
@ -722,21 +722,22 @@ static int dir_cleanup(
finish:
if (deleted) {
usec_t age1, age2;
char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
/* Restore original directory timestamps */
times[0] = ds->st_atim;
times[1] = ds->st_mtim;
usec_t age1, age2;
age1 = timespec_load(&ds->st_atim);
age2 = timespec_load(&ds->st_mtim);
log_debug("Restoring access and modification time on \"%s\": %s, %s",
p,
format_timestamp_us(a, sizeof(a), age1),
format_timestamp_us(b, sizeof(b), age2));
if (futimens(dirfd(d), times) < 0)
log_error_errno(errno, "utimensat(%s): %m", p);
/* Restore original directory timestamps */
if (futimens(dirfd(d), (struct timespec[]) {
ds->st_atim,
ds->st_mtim }) < 0)
log_warning_errno(errno, "Failed to revert timestamps of '%s', ignoring: %m", p);
}
return r;
@ -3175,6 +3176,9 @@ static int run(int argc, char *argv[]) {
log_setup_service();
/* Descending down file system trees might take a lot of fds */
(void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
if (arg_user) {
r = user_config_paths(&config_dirs);
if (r < 0)