#!/bin/bash # SPDX-License-Identifier: LGPL-2.1-or-later # # Basic tests for types creating/writing files set -eux set -o pipefail rm -fr /tmp/{f,F,w} mkdir /tmp/{f,F,w} touch /tmp/file-owned-by-root # # 'f' # systemd-tmpfiles --dry-run --create - <<EOF f /tmp/f/1 0644 - - - - f /tmp/f/2 0644 - - - This string should be written EOF test ! -e /tmp/f/1 test ! -e /tmp/f/2 systemd-tmpfiles --create - <<EOF f /tmp/f/1 0644 - - - - f /tmp/f/2 0644 - - - This string should be written EOF ### '1' should exist and be empty test -f /tmp/f/1; test ! -s /tmp/f/1 test "$(stat -c %U:%G:%a /tmp/f/1)" = "root:root:644" test "$(stat -c %U:%G:%a /tmp/f/2)" = "root:root:644" test "$(< /tmp/f/2)" = "This string should be written" ### The perms are supposed to be updated even if the file already exists. systemd-tmpfiles --create - <<EOF f /tmp/f/1 0666 daemon daemon - This string should not be written EOF # file should be empty test ! -s /tmp/f/1 test "$(stat -c %U:%G:%a /tmp/f/1)" = "daemon:daemon:666" ### But we shouldn't try to set perms on an existing file which is not a ### regular one. mkfifo /tmp/f/fifo chmod 644 /tmp/f/fifo (! systemd-tmpfiles --create -) <<EOF f /tmp/f/fifo 0666 daemon daemon - This string should not be written EOF test -p /tmp/f/fifo test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644" ### 'f' should not follow symlinks. ln -s missing /tmp/f/dangling ln -s /tmp/file-owned-by-root /tmp/f/symlink (! systemd-tmpfiles --create -) <<EOF f /tmp/f/dangling 0644 daemon daemon - - f /tmp/f/symlink 0644 daemon daemon - - EOF test ! -e /tmp/f/missing test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644" ### Handle read-only filesystem gracefully: we shouldn't fail if the target ### already exists and have the correct perms. mkdir /tmp/f/rw-fs mkdir /tmp/f/ro-fs touch /tmp/f/rw-fs/foo chmod 644 /tmp/f/rw-fs/foo mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs systemd-tmpfiles --create - <<EOF f /tmp/f/ro-fs/foo 0644 - - - - This string should not be written EOF test -f /tmp/f/ro-fs/foo; test ! -s /tmp/f/ro-fs/foo (! systemd-tmpfiles --create -) <<EOF f /tmp/f/ro-fs/foo 0666 - - - - EOF test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644" (! systemd-tmpfiles --create -) <<EOF f /tmp/f/ro-fs/bar 0644 - - - - EOF test ! -e /tmp/f/ro-fs/bar ### 'f' shouldn't follow unsafe paths. mkdir /tmp/f/daemon ln -s /root /tmp/f/daemon/unsafe-symlink chown -R --no-dereference daemon:daemon /tmp/f/daemon (! systemd-tmpfiles --create -) <<EOF f /tmp/f/daemon/unsafe-symlink/exploit 0644 daemon daemon - - EOF test ! -e /tmp/f/daemon/unsafe-symlink/exploit # # 'F' # echo "This should be truncated" >/tmp/F/truncated echo "This should be truncated" >/tmp/F/truncated-with-content systemd-tmpfiles --create - <<EOF F /tmp/F/created 0644 - - - - F /tmp/F/created-with-content 0644 - - - new content F /tmp/F/truncated 0666 daemon daemon - - F /tmp/F/truncated-with-content 0666 daemon daemon - new content EOF test -f /tmp/F/created; test ! -s /tmp/F/created test -f /tmp/F/created-with-content test "$(< /tmp/F/created-with-content)" = "new content" test -f /tmp/F/truncated; test ! -s /tmp/F/truncated test "$(stat -c %U:%G:%a /tmp/F/truncated)" = "daemon:daemon:666" test -s /tmp/F/truncated-with-content test "$(stat -c %U:%G:%a /tmp/F/truncated-with-content)" = "daemon:daemon:666" ### We shouldn't try to truncate anything but regular files since the behavior is ### unspecified in the other cases. mkfifo /tmp/F/fifo (! systemd-tmpfiles --create -) <<EOF F /tmp/F/fifo 0644 - - - - EOF test -p /tmp/F/fifo ### 'F' should not follow symlinks. ln -s missing /tmp/F/dangling ln -s /tmp/file-owned-by-root /tmp/F/symlink (! systemd-tmpfiles --create -) <<EOF f /tmp/F/dangling 0644 daemon daemon - - f /tmp/F/symlink 0644 daemon daemon - - EOF test ! -e /tmp/F/missing test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644" ### Handle read-only filesystem gracefully: we shouldn't fail if the target ### already exists and is empty. mkdir /tmp/F/rw-fs mkdir /tmp/F/ro-fs touch /tmp/F/rw-fs/foo chmod 644 /tmp/F/rw-fs/foo mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs systemd-tmpfiles --create - <<EOF F /tmp/F/ro-fs/foo 0644 - - - - EOF test -f /tmp/F/ro-fs/foo; test ! -s /tmp/F/ro-fs/foo echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo (! systemd-tmpfiles --create -) <<EOF F /tmp/F/ro-fs/foo 0644 - - - - EOF (! systemd-tmpfiles --create -) <<EOF F /tmp/F/ro-fs/foo 0644 - - - - This string should not be written EOF test -f /tmp/F/ro-fs/foo grep -q 'truncating is not allowed' /tmp/F/ro-fs/foo # Trying to change the perms should fail. : >/tmp/F/rw-fs/foo (! systemd-tmpfiles --create -) <<EOF F /tmp/F/ro-fs/foo 0666 - - - - EOF test "$(stat -c %U:%G:%a /tmp/F/ro-fs/foo)" = "root:root:644" ### Try to create a new file. (! systemd-tmpfiles --create -) <<EOF F /tmp/F/ro-fs/bar 0644 - - - - EOF test ! -e /tmp/F/ro-fs/bar ### 'F' shouldn't follow unsafe paths. mkdir /tmp/F/daemon ln -s /root /tmp/F/daemon/unsafe-symlink chown -R --no-dereference daemon:daemon /tmp/F/daemon (! systemd-tmpfiles --create -) <<EOF F /tmp/F/daemon/unsafe-symlink/exploit 0644 daemon daemon - - EOF test ! -e /tmp/F/daemon/unsafe-symlink/exploit # # 'w' # touch /tmp/w/overwritten touch /tmp/w/appended ### nop if the target does not exist. systemd-tmpfiles --dry-run --create - <<EOF w /tmp/w/unexistent 0644 - - - new content EOF test ! -e /tmp/w/unexistent systemd-tmpfiles --create - <<EOF w /tmp/w/unexistent 0644 - - - new content EOF test ! -e /tmp/w/unexistent ### no argument given -> fails. (! systemd-tmpfiles --create -) <<EOF w /tmp/w/unexistent 0644 - - - - EOF ### write into an empty file. systemd-tmpfiles --dry-run --create - <<EOF w /tmp/w/overwritten 0644 - - - old content EOF test -f /tmp/w/overwritten test -z "$(< /tmp/w/overwritten)" systemd-tmpfiles --create - <<EOF w /tmp/w/overwritten 0644 - - - old content EOF test -f /tmp/w/overwritten test "$(< /tmp/w/overwritten)" = "old content" ### old content is overwritten systemd-tmpfiles --dry-run --create - <<EOF w /tmp/w/overwritten 0644 - - - new content EOF test -f /tmp/w/overwritten test "$(< /tmp/w/overwritten)" = "old content" systemd-tmpfiles --create - <<EOF w /tmp/w/overwritten 0644 - - - new content EOF test -f /tmp/w/overwritten test "$(< /tmp/w/overwritten)" = "new content" ### append lines systemd-tmpfiles --create - <<EOF w+ /tmp/w/appended 0644 - - - 1 w+ /tmp/w/appended 0644 - - - 2\n w+ /tmp/w/appended 0644 - - - 3 EOF test -f /tmp/w/appended test "$(< /tmp/w/appended)" = "$(echo -ne '12\n3')" ### writing into an 'exotic' file should be allowed. systemd-tmpfiles --dry-run --create - <<EOF w /dev/null - - - - new content EOF systemd-tmpfiles --create - <<EOF w /dev/null - - - - new content EOF ### 'w' follows symlinks ln -s ./overwritten /tmp/w/symlink systemd-tmpfiles --create - <<EOF w /tmp/w/symlink - - - - $(readlink -e /tmp/w/symlink) EOF readlink -e /tmp/w/symlink test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten" ### 'w' shouldn't follow unsafe paths. mkdir /tmp/w/daemon ln -s /root /tmp/w/daemon/unsafe-symlink chown -R --no-dereference daemon:daemon /tmp/w/daemon (! systemd-tmpfiles --create -) <<EOF f /tmp/w/daemon/unsafe-symlink/exploit 0644 daemon daemon - - EOF test ! -e /tmp/w/daemon/unsafe-symlink/exploit