diff options
author | Daan De Meyer <daan.j.demeyer@gmail.com> | 2023-03-28 11:05:46 +0200 |
---|---|---|
committer | Daan De Meyer <daan.j.demeyer@gmail.com> | 2023-03-30 11:45:05 +0200 |
commit | 65e179a1e7021ab655c07dc392d5dd6fba2d2acb (patch) | |
tree | 7b460ed7fd3c049be03c9d7e996a2737b346c33f | |
parent | 5ccf06074abfd53e7f580764137eb17542830928 (diff) | |
download | systemd-65e179a1e7021ab655c07dc392d5dd6fba2d2acb.tar.gz |
tmpfiles: Try to take a BSD lock on files as well
Similar to what we do for directories, just before we remove a file,
let's try to take a BSD lock on it. If that fails, skip removing the
file.
-rw-r--r-- | docs/TEMPORARY_DIRECTORIES.md | 19 | ||||
-rw-r--r-- | man/systemd-tmpfiles.xml | 4 | ||||
-rw-r--r-- | man/tmpfiles.d.xml | 12 | ||||
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 10 |
4 files changed, 27 insertions, 18 deletions
diff --git a/docs/TEMPORARY_DIRECTORIES.md b/docs/TEMPORARY_DIRECTORIES.md index 4e815ed4d2..d655b9ca77 100644 --- a/docs/TEMPORARY_DIRECTORIES.md +++ b/docs/TEMPORARY_DIRECTORIES.md @@ -111,14 +111,13 @@ strategies to avoid these issues: towards unexpected program termination as there are never files on disk that need to be explicitly deleted. -3. 🥇 Operate below a sub-directory of `/tmp/` and `/var/tmp/` you created, and - take a BSD file lock ([`flock(dir_fd, - LOCK_SH)`](https://man7.org/linux/man-pages/man2/flock.2.html)) on that - sub-directory. This is particularly interesting when operating on more than - a single file, or on file nodes that are not plain regular files, for - example when extracting a tarball to a temporary directory. The ageing - algorithm will skip all directories (and everything below them) that are - locked through a BSD file lock. As BSD file locks are automatically released +3. 🥇 Take an exclusive or shared BSD file lock ([`flock()`]( + https://man7.org/linux/man-pages/man2/flock.2.html)) on files and directories + you don't want to be removed. This is particularly interesting when operating + on more than a single file, or on file nodes that are not plain regular files, + for example when extracting a tarball to a temporary directory. The ageing + algorithm will skip all directories (and everything below them) and files that + are locked through a BSD file lock. As BSD file locks are automatically released when the file descriptor they are taken on is closed, and all file descriptors opened by a process are implicitly closed when it exits, this is a robust mechanism that ensures all temporary files are subject to ageing @@ -127,9 +126,7 @@ strategies to avoid these issues: modification/access times, as extracted files are otherwise immediately candidates for deletion by the ageing algorithm. The [`flock`](https://man7.org/linux/man-pages/man1/flock.1.html) tool of the - `util-linux` packages makes this concept available to shell scripts. Note - that `systemd-tmpfiles` only checks for BSD file locks on directories, locks - on other types of file nodes (including regular files) are not considered. + `util-linux` packages makes this concept available to shell scripts. 4. Keep the access time of all temporary files created current. In regular intervals, use `utimensat()` or a related call to update the access time diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index c2e32f9f3d..49eda985b4 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -131,7 +131,9 @@ directories marked with <varname>D</varname> or <varname>R</varname>, and files or directories themselves marked with <varname>r</varname> or <varname>R</varname> are - removed.</para></listitem> + removed unless an exclusive or shared BSD lock is taken on them (see <citerefentry + project='man-pages'><refentrytitle>flock</refentrytitle><manvolnum>2</manvolnum></citerefentry>). + </para></listitem> </varlistentry> <varlistentry> diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index b50423dc77..a23b9c8946 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -647,13 +647,13 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para> # an hour ago in "/tmp/foo/bar", are subject to time-based cleanup. d /tmp/foo/bar - - - - bmA:1h -</programlisting></para> - <para>Note that while the aging algorithm is run a 'shared' BSD file lock (see <citerefentry + <para>Note that while the aging algorithm is run an exclusive 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> + taken on each directory/file the algorithm decides to remove. If the aging algorithm finds a lock ( + shared or exclusive) is already taken on some directory/file, 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/file and everything below it is disabled.</para> </refsect2> <refsect2> diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index c3741088c3..6cd76e8df8 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -754,6 +754,8 @@ static int dir_cleanup( r = log_warning_errno(errno, "Failed to remove directory \"%s\", ignoring: %m", sub_path); } else { + _cleanup_close_ int fd = -EBADF; + /* 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 (sx.stx_mode & S_ISVTX) { @@ -794,6 +796,14 @@ static int dir_cleanup( cutoff_nsec, sub_path, age_by_file, false)) continue; + fd = xopenat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME, 0); + if (fd < 0 && fd != -ENOENT) + log_warning_errno(fd, "Opening file \"%s\" failed, ignoring: %m", sub_path); + if (fd >= 0 && flock(fd, LOCK_EX|LOCK_NB) < 0 && errno == EAGAIN) { + log_debug_errno(errno, "Couldn't acquire shared BSD lock on file \"%s\", skipping: %m", p); + continue; + } + log_debug("Removing \"%s\".", sub_path); if (unlinkat(dirfd(d), de->d_name, 0) < 0) if (errno != ENOENT) |