]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tmpfiles: Try to take a BSD lock on files as well
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 28 Mar 2023 09:05:46 +0000 (11:05 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 30 Mar 2023 09:45:05 +0000 (11:45 +0200)
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.

docs/TEMPORARY_DIRECTORIES.md
man/systemd-tmpfiles.xml
man/tmpfiles.d.xml
src/tmpfiles/tmpfiles.c

index 4e815ed4d285aef530c62ee31aa747601048de58..d655b9ca77628a75a19747c105f86be0bf55f3c8 100644 (file)
@@ -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
index c2e32f9f3df4760e2c06b4607e025a85b9ad5c6c..49eda985b49bc723209cb7bae1a884ee0e8b01ce 100644 (file)
         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>
index b50423dc7759c75d5f69438000de38c0f24a8160..a23b9c8946e92a90c8afe301408cfeaf32141187 100644 (file)
@@ -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>
index c3741088c34aed46173bd31dc71ee79aeb1b1702..6cd76e8df844af9503b87d8977ef83fa9fd17603 100644 (file)
@@ -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)