A System and Service Manager
CentOS Sources
2018-04-10 dd65c992f6594459af97003c5035b80ff8af1aaf
import systemd-219-57.el7
96 files added
19 files renamed
2 files modified
13500 ■■■■■ changed files
SOURCES/0499-tests-use-XFS-as-root-filesystem-for-system-tests.patch 50 ●●●●● patch | view | raw | blame | history
SOURCES/0500-tests-use-fdisk-instead-of-sfdisk.patch 60 ●●●●● patch | view | raw | blame | history
SOURCES/0501-Revert-udev-net_id-add-support-for-phys_port_name-at.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0502-core-unset-sysfs-path-after-transition-to-dead-state.patch 49 ●●●●● patch | view | raw | blame | history
SOURCES/0503-sysctl-fix-uninitialized-variable.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0504-udev-ignore-SIGCHLD-from-unexpected-processes-130653.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0505-compile-with-Werror.patch 83 ●●●●● patch | view | raw | blame | history
SOURCES/0506-myhostname-don-t-return-any-ipv6-entries-when-ipv6-i.patch 42 ●●●●● patch | view | raw | blame | history
SOURCES/0507-core-execute-fix-fork-fail-handling-in-exec_spawn.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0508-fix-compilation-after-commit-382877acc6c029e59e359a0.patch 26 ●●●●● patch | view | raw | blame | history
SOURCES/0509-Redefine-32bit-time_t-format-to-signed.patch 37 ●●●●● patch | view | raw | blame | history
SOURCES/0510-sd-bus-bus-kernel.c-fix-format-errors-on-ppc64le.patch 43 ●●●●● patch | view | raw | blame | history
SOURCES/0511-tmpfiles-with-e-don-t-attempt-to-set-permissions-whe.patch 65 ●●●●● patch | view | raw | blame | history
SOURCES/0512-units-introduce-getty-pre.target-6667.patch 112 ●●●●● patch | view | raw | blame | history
SOURCES/0513-units-order-container-and-console-getty-units-after-.patch 40 ●●●●● patch | view | raw | blame | history
SOURCES/0514-log-never-log-into-foreign-fd-2-in-PID-1-or-its-pre-.patch 76 ●●●●● patch | view | raw | blame | history
SOURCES/0515-nspawn-new-option-to-start-as-PID2.patch 491 ●●●●● patch | view | raw | blame | history
SOURCES/0516-journal-implicitly-flush-to-var-on-recovery-4028.patch 77 ●●●●● patch | view | raw | blame | history
SOURCES/0517-journal-add-use-flushed_flag_is_set-helper-4041.patch 38 ●●●●● patch | view | raw | blame | history
SOURCES/0518-journald-don-t-flush-to-var-log-journal-before-we-ge.patch 142 ●●●●● patch | view | raw | blame | history
SOURCES/0519-path-util-make-use-of-mnt_id-field-exported-in-proc-.patch 560 ●●●●● patch | view | raw | blame | history
SOURCES/0520-Revert-Revert-journald-allow-restarting-journald-wit.patch 546 ●●●●● patch | view | raw | blame | history
SOURCES/0521-journald-make-sure-we-retain-all-stream-fds-across-r.patch 34 ●●●●● patch | view | raw | blame | history
SOURCES/0522-Allow-systemd-tmpfiles-to-set-the-file-directory-att.patch 218 ●●●●● patch | view | raw | blame | history
SOURCES/0523-tmpfiles-rework-file-attribute-code.patch 326 ●●●●● patch | view | raw | blame | history
SOURCES/0524-tmpfiles-warn-if-we-get-an-argument-on-lines-that-do.patch 40 ●●●●● patch | view | raw | blame | history
SOURCES/0525-tmpfiles-substitute-specifiers-in-arguments-for-writ.patch 207 ●●●●● patch | view | raw | blame | history
SOURCES/0526-btrfs-util-introduce-btrfs_is_filesystem-and-make-us.patch 105 ●●●●● patch | view | raw | blame | history
SOURCES/0527-journal-don-t-force-FS_NOCOW_FL-on-new-journal-files.patch 86 ●●●●● patch | view | raw | blame | history
SOURCES/0528-tmpfiles-Add-C-attrib-to-the-journal-files-directori.patch 64 ●●●●● patch | view | raw | blame | history
SOURCES/0529-Revert-path-util-make-use-of-mnt_id-field-exported-i.patch 553 ●●●●● patch | view | raw | blame | history
SOURCES/0530-device-make-sure-to-remove-all-device-units-sharing-.patch 43 ●●●●● patch | view | raw | blame | history
SOURCES/0531-manager-when-reexecuting-try-to-connect-to-bus-only-.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0532-doc-document-service-exit-codes.patch 337 ●●●●● patch | view | raw | blame | history
SOURCES/0533-units-order-cryptsetup-pre.target-before-cryptsetup..patch 25 ●●●●● patch | view | raw | blame | history
SOURCES/0534-man-add-an-explicit-description-of-_netdev-to-system.patch 41 ●●●●● patch | view | raw | blame | history
SOURCES/0535-units-add-remote-cryptsetup.target-and-remote-crypts.patch 140 ●●●●● patch | view | raw | blame | history
SOURCES/0536-cryptsetup-generator-use-remote-cryptsetup.target-wh.patch 125 ●●●●● patch | view | raw | blame | history
SOURCES/0537-Support-rdma-as-a-ListenNetlink-argument-6626.patch 28 ●●●●● patch | view | raw | blame | history
SOURCES/0538-core-namespace-Protect-usr-instead-of-home-with-Prot.patch 29 ●●●●● patch | view | raw | blame | history
SOURCES/0539-udev-Use-parent-bus-id-for-virtio-disk-builtin-path-.patch 68 ●●●●● patch | view | raw | blame | history
SOURCES/0540-socket-util-socket_address_parse-should-not-log-erro.patch 105 ●●●●● patch | view | raw | blame | history
SOURCES/0541-test-fix-failing-test-socket-util-when-running-with-.patch 69 ●●●●● patch | view | raw | blame | history
SOURCES/0542-scsi_id-add-missing-options-to-getopt_long-6501.patch 25 ●●●●● patch | view | raw | blame | history
SOURCES/0543-unmount-Pass-in-mount-options-when-remounting-read-o.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0544-shutdown-don-t-remount-ro-network-filesystems.-6588.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0545-shutdown-fix-incorrect-fscanf-result-check-6806.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0546-path-util-make-use-of-mnt_id-field-exported-in-proc-.patch 407 ●●●●● patch | view | raw | blame | history
SOURCES/0547-support-ranges-when-parsing-CPUAffinity.patch 621 ●●●●● patch | view | raw | blame | history
SOURCES/0548-man-Update-man-page-documentation-for-CPUAffinity.patch 51 ●●●●● patch | view | raw | blame | history
SOURCES/0549-test-path-util-force-rm_rf.patch 28 ●●●●● patch | view | raw | blame | history
SOURCES/0550-Export-NVMe-WWID-udev-attribute-5348.patch 28 ●●●●● patch | view | raw | blame | history
SOURCES/0551-mount-make-sure-we-unmount-tmpfs-mounts-before-we-de.patch 122 ●●●●● patch | view | raw | blame | history
SOURCES/0552-journald-never-accept-fds-from-file-systems-with-man.patch 67 ●●●●● patch | view | raw | blame | history
SOURCES/0553-udev-builtin-keyboard-move-fetching-the-device-node-.patch 52 ●●●●● patch | view | raw | blame | history
SOURCES/0554-udev-builtin-keyboard-immediately-EVIOCSKEYCODE-when.patch 90 ●●●●● patch | view | raw | blame | history
SOURCES/0555-udev-builtin-keyboard-move-actual-key-mapping-to-a-h.patch 114 ●●●●● patch | view | raw | blame | history
SOURCES/0556-udev-builtin-keyboard-invert-a-condition.patch 90 ●●●●● patch | view | raw | blame | history
SOURCES/0557-udev-builtin-keyboard-add-support-for-EVDEV_ABS_.patch 240 ●●●●● patch | view | raw | blame | history
SOURCES/0558-hwdb-sync-60-evdev.hwdb-from-systemd-v235.patch 436 ●●●●● patch | view | raw | blame | history
SOURCES/0559-journal-ensure-open-journals-from-find_journal-3973.patch 266 ●●●●● patch | view | raw | blame | history
SOURCES/0560-journal-only-check-available-space-when-journal-is-o.patch 69 ●●●●● patch | view | raw | blame | history
SOURCES/0561-automount-if-an-automount-unit-is-masked-don-t-react.patch 158 ●●●●● patch | view | raw | blame | history
SOURCES/0562-units-add-Install-section-to-remote-cryptsetup.targe.patch 44 ●●●●● patch | view | raw | blame | history
SOURCES/0563-units-replace-remote-cryptsetup-pre.target-with-remo.patch 152 ●●●●● patch | view | raw | blame | history
SOURCES/0564-man-add-a-note-about-_netdev-usage.patch 41 ●●●●● patch | view | raw | blame | history
SOURCES/0565-units-make-remote-cryptsetup.target-also-after-crypt.patch 28 ●●●●● patch | view | raw | blame | history
SOURCES/0566-cryptsetup-generator-use-after-free.patch 28 ●●●●● patch | view | raw | blame | history
SOURCES/0567-manager-fix-connecting-to-bus-when-dbus-is-actually-.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0568-journal-remote-make-url-option-support-arbitrary-url.patch 40 ●●●●● patch | view | raw | blame | history
SOURCES/0569-journald-make-maximum-size-of-stream-log-lines-confi.patch 525 ●●●●● patch | view | raw | blame | history
SOURCES/0570-service-serialize-information-about-currently-execut.patch 304 ●●●●● patch | view | raw | blame | history
SOURCES/0571-tests-add-new-test-for-issue-518.patch 226 ●●●●● patch | view | raw | blame | history
SOURCES/0572-tests-in-RHEL-7-we-don-t-have-python3-by-default.patch 27 ●●●●● patch | view | raw | blame | history
SOURCES/0573-service-attempt-to-execute-next-main-command-only-fo.patch 81 ●●●●● patch | view | raw | blame | history
SOURCES/0574-timedatectl-stop-using-xstrftime.patch 155 ●●●●● patch | view | raw | blame | history
SOURCES/0575-Add-support-to-read-lz4-compressed-journals.patch 120 ●●●●● patch | view | raw | blame | history
SOURCES/0576-journald-never-block-when-sending-messages-on-NOTIFY.patch 449 ●●●●● patch | view | raw | blame | history
SOURCES/0577-journal-restore-watchdog-support.patch 184 ●●●●● patch | view | raw | blame | history
SOURCES/0578-cgroup-resource-property-setting-ignored-if-einval.patch 729 ●●●●● patch | view | raw | blame | history
SOURCES/0579-fileio-add-new-helper-call-read_line-as-bounded-getl.patch 177 ●●●●● patch | view | raw | blame | history
SOURCES/0580-def-add-new-constant-LONG_LINE_MAX.patch 28 ●●●●● patch | view | raw | blame | history
SOURCES/0581-fileio-rework-read_one_line_file-on-top-of-read_line.patch 55 ●●●●● patch | view | raw | blame | history
SOURCES/0582-cgroup-util-replace-one-use-of-fgets-by-read_line.patch 33 ●●●●● patch | view | raw | blame | history
SOURCES/0583-conf-parse-remove-4K-line-length-limit.patch 124 ●●●●● patch | view | raw | blame | history
SOURCES/0584-test-conf-parser-add-tests-for-config-parser.patch 204 ●●●●● patch | view | raw | blame | history
SOURCES/0585-fileio-use-_cleanup_-for-FILE-unlocking.patch 103 ●●●●● patch | view | raw | blame | history
SOURCES/0586-test-fileio-also-test-read_line-with-actual-files.patch 105 ●●●●● patch | view | raw | blame | history
SOURCES/0587-fileio-return-0-from-read_one_line_file-on-success.patch 36 ●●●●● patch | view | raw | blame | history
SOURCES/0588-man-fix-description-of-force-in-halt-8-7392.patch 33 ●●●●● patch | view | raw | blame | history
SOURCES/0589-journal-return-better-error-for-empty-files.patch 32 ●●●●● patch | view | raw | blame | history
SOURCES/0590-journalctl-continue-operation-even-if-we-run-into-an.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0591-journal-remove-error-check-that-never-happens.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0592-sd-journal-various-clean-ups-and-modernizations.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0593-journalctl-when-we-fail-to-open-a-journal-file-print.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0594-journald-fix-accuracy-of-watchdog-timer-event.patch 31 ●●●●● patch | view | raw | blame | history
SOURCES/0595-core-fix-the-reversed-sanity-check-when-setting-Star.patch 28 ●●●●● patch | view | raw | blame | history
SOURCES/0596-shared-dropin-ignore-ENAMETOOLONG-when-checking-drop.patch 37 ●●●●● patch | view | raw | blame | history
SOURCES/0597-cryptsetup-when-unlocking-always-put-path-to-the-obj.patch 52 ●●●●● patch | view | raw | blame | history
SOURCES/0598-cryptsetup-use-more-descriptive-name-for-the-variabl.patch 106 ●●●●● patch | view | raw | blame | history
SOURCES/0599-cryptsetup-generator-do-not-bind-to-the-decrypted-de.patch 31 ●●●●● patch | view | raw | blame | history
SOURCES/0600-shared-cgroup-utils-_CGROUP_CONTROLLER_MASK_ALL-does.patch 6 ●●●● patch | view | raw | blame | history
SOURCES/0601-automount-ack-automount-requests-even-when-already-m.patch 10 ●●●● patch | view | raw | blame | history
SOURCES/0602-udev-net_id-add-support-for-platform-bus-ACPI-mostly.patch 134 ●●●●● patch | view | raw | blame | history
SOURCES/0603-journald-native-Fix-typo-in-MANDLOCK-message.patch 24 ●●●●● patch | view | raw | blame | history
SOURCES/0604-process-util-make-our-freeze-routine-do-something-us.patch 45 ●●●●● patch | view | raw | blame | history
SOURCES/0605-dbus-propagate-errors-from-bus_init_system-and-bus_i.patch 131 ●●●●● patch | view | raw | blame | history
SOURCES/0606-bus-util.c-fix-TasksMax-property-assignment.patch 44 ●●●●● patch | view | raw | blame | history
SOURCES/0607-sparse-avoid-clash-with-__bitwise-and-__force-from-4.patch 89 ●●●●● patch | view | raw | blame | history
SOURCES/0608-core-Let-two-more-booleans-survive-a-daemon-reload.patch 76 ●●●●● patch | view | raw | blame | history
SOURCES/0609-core-don-t-choke-if-a-unit-another-unit-triggers-van.patch 212 ●●●●● patch | view | raw | blame | history
SOURCES/0610-sd-journal-properly-handle-inotify-queue-overflow.patch 10 ●●●● patch | view | raw | blame | history
SOURCES/0611-sd-journal-make-sure-it-s-safe-to-call-sd_journal_pr.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0612-journalctl-Periodically-call-sd_journal_process-in-j.patch 2 ●●● patch | view | raw | blame | history
SOURCES/0613-sd-journal-when-picking-up-a-new-file-compare-inode-.patch 2 ●●● patch | view | raw | blame | history
SOURCES/76-phys-port-name.rules 1 ●●●● patch | view | raw | blame | history
SPECS/systemd.spec 294 ●●●● patch | view | raw | blame | history
SOURCES/0499-tests-use-XFS-as-root-filesystem-for-system-tests.patch
New file
@@ -0,0 +1,50 @@
From e82e71d82496b7dd3268db62a89f215b4b38508f Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Mon, 24 Jul 2017 18:47:36 +0200
Subject: [PATCH] tests: use XFS as root filesystem for system tests
On RHEL-7 we don't have mount.ext3 in initramfs.
RHEL-only
Resolves: #1475870
---
 test/TEST-02-CRYPTSETUP/test.sh | 4 ++--
 test/test-functions             | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh
index 4be2365e2..6e5c53d8b 100755
--- a/test/TEST-02-CRYPTSETUP/test.sh
+++ b/test/TEST-02-CRYPTSETUP/test.sh
@@ -38,7 +38,7 @@ test_setup() {
     echo -n test >$TESTDIR/keyfile
     cryptsetup -q luksFormat ${LOOPDEV}p2 $TESTDIR/keyfile
     cryptsetup luksOpen ${LOOPDEV}p2 varcrypt <$TESTDIR/keyfile
-    mkfs.ext3 -L var /dev/mapper/varcrypt
+    mkfs.xfs -L var /dev/mapper/varcrypt
     mkdir -p $TESTDIR/root
     mount ${LOOPDEV}p1 $TESTDIR/root
     mkdir -p $TESTDIR/root/var
@@ -74,7 +74,7 @@ EOF
         cat $initdir/etc/crypttab | ddebug
         cat >>$initdir/etc/fstab <<EOF
-/dev/mapper/varcrypt    /var    ext3    defaults 0 1
+/dev/mapper/varcrypt    /var    xfs    defaults 0 1
 EOF
     )
     setup_nspawn_root
diff --git a/test/test-functions b/test/test-functions
index 901ff4860..f8950e31e 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -150,7 +150,7 @@ create_empty_image() {
 ,
 EOF
-    mkfs.ext3 -L systemd "${LOOPDEV}p1"
+    mkfs.xfs -L systemd "${LOOPDEV}p1"
 }
 check_result_nspawn() {
SOURCES/0500-tests-use-fdisk-instead-of-sfdisk.patch
New file
@@ -0,0 +1,60 @@
From 5655999840f9c3d8b55a40c1751df400b425178a Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Mon, 24 Jul 2017 13:25:19 +0200
Subject: [PATCH] tests: use fdisk instead of sfdisk
In RHEL7 we have an older version of sfdisk that exits with an error
when executed with sfdisk script that is used in upstream to create
partitions on root disk.
Let's use equivalent fdisk commands to achieve the (more less) same
result.
Also default size of disk image is bumped to 400M. Previous 300M doesn't
work, probably due to some fdisk bug. Size of second partiotion (/var in
TEST-02-CRYPTSETUP) is bumped to 50M to accommodate space requirements
of xfs filesystem.
RHEL-only
Resolves: #1475870
---
 test/test-functions | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/test/test-functions b/test/test-functions
index f8950e31e..cf5612370 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -141,15 +141,26 @@ install_missing_libraries() {
 create_empty_image() {
     rm -f "$TESTDIR/rootdisk.img"
     # Create the blank file to use as a root filesystem
-    dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek=300
+    dd if=/dev/null of="$TESTDIR/rootdisk.img" bs=1M seek=400
     LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img)
     [ -b "$LOOPDEV" ] || return 1
     echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
-    sfdisk "$LOOPDEV" <<EOF
-,290M
-,
+    fdisk "$LOOPDEV" <<EOF
+o
+n
+p
+1
+
++290M
+n
+p
+2
+
++50M
+w
+q
 EOF
-
+    partprobe "$LOOPDEV"
     mkfs.xfs -L systemd "${LOOPDEV}p1"
 }
SOURCES/0501-Revert-udev-net_id-add-support-for-phys_port_name-at.patch
File was renamed from SOURCES/0499-Revert-udev-net_id-add-support-for-phys_port_name-at.patch
@@ -1,4 +1,4 @@
From 6cbd97527f6d33a140072131785547e898ee4c7a Mon Sep 17 00:00:00 2001
From 8ad860fa8e344c71fb3bb00a15b25d41e3c61b35 Mon Sep 17 00:00:00 2001
From: Lukas Nykryn <lnykryn@redhat.com>
Date: Tue, 15 Aug 2017 12:30:03 +0200
Subject: [PATCH] Revert "udev: net_id: add support for phys_port_name
SOURCES/0502-core-unset-sysfs-path-after-transition-to-dead-state.patch
New file
@@ -0,0 +1,49 @@
From d5ab3fdc9bf9353478e7c0987b3830f14bbdefae Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Thu, 22 Jun 2017 14:26:39 +0200
Subject: [PATCH] core: unset sysfs path after transition to dead state
Device is gone and most likely it will get garbage collected. However in
cases when it doesn't get gc'ed (because it is referenced by some
other unit, e.g. mount from fstab) we need to unset sysfs. This is
because when device appears next time, possibly, with different sysfs
path we need to update the sysfs path. Current code could end up caching
stale sysfs path forever.
In reality this is not a problem for normal disks (unless you swap them
during system runtime). However this issue causes failures to mount
filesystems on LVM where sysfs path depends on activation
order (i.e. logical volumes from volume group that is activated first
get assigned lower dm-X numbers and corresponding syspaths).
Fixes #6126
(cherry picked from commit 0e139cac0318de09e6f4c1a4fc61388f7e541ebd)
Resolves: #1408916
---
 src/core/device.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/core/device.c b/src/core/device.c
index befbae83f..63a04bdd3 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -474,12 +474,16 @@ static void device_update_found_one(Device *d, bool add, DeviceFound found, bool
                  * now referenced by the kernel, then we assume the
                  * kernel knows it now, and udev might soon too. */
                 device_set_state(d, DEVICE_TENTATIVE);
-        else
+        else {
                 /* If nobody sees the device, or if the device was
                  * previously seen by udev and now is only referenced
                  * from the kernel, then we consider the device is
                  * gone, the kernel just hasn't noticed it yet. */
+
                 device_set_state(d, DEVICE_DEAD);
+                device_unset_sysfs(d);
+        }
+
 }
 static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) {
SOURCES/0503-sysctl-fix-uninitialized-variable.patch
File was renamed from SOURCES/0500-sysctl-fix-uninitialized-variable.patch
@@ -1,4 +1,4 @@
From 6fd8e5c41ffd14038accd866ccd81d9a35cb3291 Mon Sep 17 00:00:00 2001
From 75d982344e59e1dd916c214c5ccb6339c5c94254 Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Fri, 25 Aug 2017 13:13:50 +0200
Subject: [PATCH] sysctl: fix uninitialized variable
SOURCES/0504-udev-ignore-SIGCHLD-from-unexpected-processes-130653.patch
File was renamed from SOURCES/0501-udev-ignore-SIGCHLD-from-unexpected-processes-130653.patch
@@ -1,4 +1,4 @@
From 8b998d862e401c252c4323974698691e67846412 Mon Sep 17 00:00:00 2001
From 461c10112d74ab223226554f2bb73aabaef43c9a Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsynacek@redhat.com>
Date: Tue, 15 Aug 2017 13:29:51 +0200
Subject: [PATCH] udev: ignore SIGCHLD from unexpected processes (#1306539)
SOURCES/0505-compile-with-Werror.patch
New file
@@ -0,0 +1,83 @@
From 382877acc6c029e59e359a076d203ca03b4b9e9e Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsynacek@redhat.com>
Date: Wed, 3 May 2017 14:34:36 +0200
Subject: [PATCH] compile with -Werror
The maybe-uninitialized flag has to be disabled, because gcc on RHEL7
reports tons of obvious false-positive warnings when variables are
wrapped with _cleanup_.
Also, LTO is better to be disabled. According to gcc folks, it makes
debugging really hard and is not really recommended on RHEL7. Plus it
makes the compilation fail with
In function '__ppoll_alias',
    inlined from 'bus_poll' at src/libsystemd/sd-bus/sd-bus.c:2822:11:
/usr/include/bits/poll2.h:71:9: warning: call to '__ppoll_chk_warn' declared with attribute warning: ppoll called with fds buffer too small file nfds entries
  return __ppoll_chk (__fds, __nfds, __timeout, __ss, __bos (__fds));
That is also a gcc bug, already fixed in the gcc upstream
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61886).
Resolves: #1447937
---
 configure.ac               | 12 ++----------
 src/core/main.c            |  1 -
 src/login/logind-session.c |  2 +-
 3 files changed, 3 insertions(+), 12 deletions(-)
diff --git a/configure.ac b/configure.ac
index 2734368dc..def9fe5ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -187,7 +187,8 @@ CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\
         -Wno-unused-parameter \
         -Wno-missing-field-initializers \
         -Wno-unused-result \
-        -Werror=overflow \
+        -Werror \
+        -Wno-error=maybe-uninitialized \
         -Wdate-time \
         -Wnested-externs \
         -ffast-math \
@@ -208,15 +209,6 @@ AS_CASE([$CC], [*clang*],
                -Wno-gnu-variable-sized-type-not-at-end \
         ])])
-AC_ARG_ENABLE([lto], AS_HELP_STRING([--disable-lto], [Disable Link time optimization]))
-AS_IF([test "x$enable_lto" != "xno"], [
-AS_CASE([$CFLAGS], [*-O[[12345\ ]]*], [
-         CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [-flto -ffat-lto-objects])
-         CC_CHECK_FLAGS_APPEND([with_ldflags], [LDFLAGS],[-Wl,-fuse-ld=gold])
-         ],
-[AC_MSG_RESULT([skipping -flto, optimization not enabled])])
-])
-
 AC_SUBST([OUR_CFLAGS], "$with_cflags $sanitizer_cflags")
 AS_CASE([$CFLAGS], [*-O[[12345sz\ ]]*],
diff --git a/src/core/main.c b/src/core/main.c
index 50c9714f7..37e3ea0ce 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1205,7 +1205,6 @@ static int status_welcome(void) {
 static int write_container_id(void) {
         const char *c;
-        int r;
         c = getenv("container");
         if (isempty(c))
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 4575a029f..daf875a7d 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -183,7 +183,7 @@ int session_save(Session *s) {
                 "STATE=%s\n"
                 "REMOTE=%i\n"
                 "STOPPING=%i\n",
-                (unsigned long) s->user->uid,
+                s->user->uid,
                 s->user->name,
                 session_is_active(s),
                 session_state_to_string(session_get_state(s)),
SOURCES/0506-myhostname-don-t-return-any-ipv6-entries-when-ipv6-i.patch
New file
@@ -0,0 +1,42 @@
From 624fcda36dd376707e3af088b592fe3764b99acf Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsynacek@redhat.com>
Date: Tue, 2 May 2017 14:34:17 +0200
Subject: [PATCH] myhostname: don't return any ipv6 entries when ipv6 is
 disabled
This commit amends the rhel-only 6e5117b83af5998359916f276a9b32f755c0e6f4.
Resolves: #1444824
---
 src/nss-myhostname/nss-myhostname.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index e197cc752..144c83171 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -351,6 +351,8 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
                 *h_errnop = NO_DATA;
                 return NSS_STATUS_UNAVAIL;
         }
+        if (af == AF_INET6 && !socket_ipv6_is_supported())
+                return NSS_STATUS_UNAVAIL;
         if (is_localhost(name)) {
                 canonical = "localhost";
@@ -381,13 +383,9 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
                         return NSS_STATUS_NOTFOUND;
                 }
-                if (af == AF_INET6 && !socket_ipv6_is_supported()) {
+                n_addresses = local_addresses(NULL, 0, af, &addresses);
+                if (n_addresses < 0)
                         n_addresses = 0;
-                } else {
-                        n_addresses = local_addresses(NULL, 0, af, &addresses);
-                        if (n_addresses < 0)
-                                n_addresses = 0;
-                }
                 canonical = hn;
                 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
SOURCES/0507-core-execute-fix-fork-fail-handling-in-exec_spawn.patch
File was renamed from SOURCES/0509-core-execute-fix-fork-fail-handling-in-exec_spawn.patch
@@ -1,4 +1,4 @@
From 7ebf4a0faffecb3a0c8abe8bea47502044038d66 Mon Sep 17 00:00:00 2001
From 03118775f6a9bf505a65dd0b86a6d2de2e3493a3 Mon Sep 17 00:00:00 2001
From: lc85446 <lc85446@alibaba-inc.com>
Date: Thu, 26 Nov 2015 11:46:40 +0800
Subject: [PATCH] core:execute: fix fork() fail handling in exec_spawn()
SOURCES/0508-fix-compilation-after-commit-382877acc6c029e59e359a0.patch
New file
@@ -0,0 +1,26 @@
From 4d5e724a78803ed18033f04e7ffec6c8ea3bc922 Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsynacek@redhat.com>
Date: Thu, 7 Sep 2017 14:37:06 +0200
Subject: [PATCH] fix compilation after commit
 382877acc6c029e59e359a076d203ca03b4b9e9e
It turns out that explicit #warning macros work as explicit errors with -Werror.
Related: #1447937
---
 configure.ac | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index def9fe5ce..ee147e28e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -188,7 +188,7 @@ CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\
         -Wno-missing-field-initializers \
         -Wno-unused-result \
         -Werror \
-        -Wno-error=maybe-uninitialized \
+        -Wno-error=maybe-uninitialized -Wno-error=cpp \
         -Wdate-time \
         -Wnested-externs \
         -ffast-math \
SOURCES/0509-Redefine-32bit-time_t-format-to-signed.patch
New file
@@ -0,0 +1,37 @@
From 10a1adc237ada061f557a7ae422456aa7d8c2c05 Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsynacek@redhat.com>
Date: Thu, 7 Sep 2017 14:41:09 +0200
Subject: [PATCH] Redefine 32bit time_t format to signed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
It seems that it is signed both on i386 and arm.
Avoids a stupid gcc warning on arm:
src/udev/udevadm-monitor.c: In function ‘print_device’:
src/udev/udevadm-monitor.c:44:16: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 3 has type ‘__time_t {aka long int}’ [-Wformat=]
         printf("%-6s[%"PRI_TIME".%06ld] %-8s %s (%s)\n",
                ^
(cherry picked from commit 6307c39b94344b901c1d6e0df7ee58644a8809bf)
Related: #1447937
---
 src/shared/util.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shared/util.h b/src/shared/util.h
index f1b6c348f..80ad18c0a 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -72,7 +72,7 @@
 #if SIZEOF_TIME_T == 8
 #  define PRI_TIME PRIi64
 #elif SIZEOF_TIME_T == 4
-#  define PRI_TIME PRIu32
+#  define PRI_TIME "li"
 #else
 #  error Unknown time_t size
 #endif
SOURCES/0510-sd-bus-bus-kernel.c-fix-format-errors-on-ppc64le.patch
New file
@@ -0,0 +1,43 @@
From b5b6f19445904feff90d6b2f9651ba51ef405144 Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsynacek@redhat.com>
Date: Thu, 7 Sep 2017 14:43:07 +0200
Subject: [PATCH] sd-bus/bus-kernel.c: fix format errors on ppc64le
RHEL-only
Related: #1447937
---
 src/libsystemd/sd-bus/bus-kernel.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
index e90ee449d..d1c90858e 100644
--- a/src/libsystemd/sd-bus/bus-kernel.c
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -763,7 +763,7 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
                         break;
                 default:
-                        log_debug("Got unknown field from kernel %llu", d->type);
+                        log_debug("Got unknown field from kernel %llu", (unsigned long long) d->type);
                 }
         }
@@ -1244,7 +1244,7 @@ static int translate_id_change(
         assert(k);
         assert(d);
-        sprintf(owner, ":1.%llu", d->id_change.id);
+        sprintf(owner, ":1.%llu", (unsigned long long) d->id_change.id);
         return push_name_owner_changed(
                         bus, owner,
@@ -1317,7 +1317,7 @@ static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) {
                                 return -EBADMSG;
                         found = d;
                 } else
-                        log_debug("Got unknown field from kernel %llu", d->type);
+                        log_debug("Got unknown field from kernel %llu", (unsigned long long) d->type);
         }
         if (!found) {
SOURCES/0511-tmpfiles-with-e-don-t-attempt-to-set-permissions-whe.patch
New file
@@ -0,0 +1,65 @@
From 797dafce1bb9c3bb16da043f654391dc29075a1a Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Mon, 28 Aug 2017 17:33:24 +0200
Subject: [PATCH] tmpfiles: with "e" don't attempt to set permissions when file
 doesn't exist
tmpfiles.d option "e" when run through systemd-tmpfiles --create should
apply configured permissions (uid,gid) only to already existing
files. When file doesn't exist we bail out with error. Instead we should
silently ignore non-existing files.
$ useradd test
$ cat /etc/tmpfiles.d/foobar.conf
e /tmp/test - test test 1d
$ ls -l /tmp/test
ls: cannot access '/tmp/test': No such file or directory
Before:
$ systemd-tmpfiles --create /etc/tmpfiles.d/foobar.conf
Adjusting owner and mode for /tmp/test failed: No such file or directory
$ echo $?
1
After:
$ systemd-tmpfiles --create /etc/tmpfiles.d/foobar.conf
$ echo $?
0
(cherry picked from commit 3caf791a1702c97b99d2647c9d465af404f2913d)
Conflicts:
    src/tmpfiles/tmpfiles.c
Resolves: #1445732
---
 src/tmpfiles/tmpfiles.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index df7676b57..ed35b8cf0 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -585,8 +585,20 @@ static int path_set_perms(Item *i, const char *path) {
          * O_PATH. */
         fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
-        if (fd < 0)
-                return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path);
+        if (fd < 0) {
+                int level = LOG_ERR, r = -errno;
+
+                /* Option "e" operates only on existing objects. Do not
+                 * print errors about non-existent files or directories */
+                if (i->type == EMPTY_DIRECTORY && errno == ENOENT) {
+                        level = LOG_DEBUG;
+                        r = 0;
+                }
+
+                log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path);
+
+                return r;
+        }
         if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
                 return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
SOURCES/0512-units-introduce-getty-pre.target-6667.patch
New file
@@ -0,0 +1,112 @@
From d538b6082216f4867b4a50c8009abe2462aafbf4 Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekletar@users.noreply.github.com>
Date: Thu, 31 Aug 2017 11:20:14 +0200
Subject: [PATCH] units: introduce getty-pre.target (#6667)
This new target is a passive unit, hence it is supposed to be pulled in
to the transaction by the service that wants to block login on the
console (e.g. text version of initial-setup). Now both getty and
serial-getty are ordered after this target.
https://lists.freedesktop.org/archives/systemd-devel/2015-July/033754.html
(cherry picked from commit 175902541852fb9207f6e532d8da48c9a102340c)
Conflicts:
    units/meson.build
Resolves: #1173080
---
 Makefile.am                    |  1 +
 man/systemd.special.xml        | 12 ++++++++++++
 units/getty-pre.target         | 11 +++++++++++
 units/getty@.service.m4        |  2 +-
 units/serial-getty@.service.m4 |  2 +-
 5 files changed, 26 insertions(+), 2 deletions(-)
 create mode 100644 units/getty-pre.target
diff --git a/Makefile.am b/Makefile.am
index e9ceac98a..7c58fd050 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -462,6 +462,7 @@ dist_systemunit_DATA = \
     units/sysinit.target \
     units/basic.target \
     units/getty.target \
+    units/getty-pre.target \
     units/halt.target \
     units/kexec.target \
     units/local-fs.target \
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index 553197d66..eb464f9f8 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -61,6 +61,7 @@
     <filename>exit.target</filename>,
     <filename>final.target</filename>,
     <filename>getty.target</filename>,
+    <filename>getty-pre.target</filename>,
     <filename>graphical.target</filename>,
     <filename>halt.target</filename>,
     <filename>hibernate.target</filename>,
@@ -216,6 +217,17 @@
           </para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><filename>getty-pre.target</filename></term>
+        <listitem>
+          <para>A special passive target unit. Users of this target
+          are expected to pull it in the boot transaction via
+          a dependency (e.g. <varname>Wants=</varname>). Order your
+          unit before this unit if you want to make use of the console
+          just before <filename>getty</filename> is started.
+          </para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><filename>graphical.target</filename></term>
         <listitem>
diff --git a/units/getty-pre.target b/units/getty-pre.target
new file mode 100644
index 000000000..f6c78b6c2
--- /dev/null
+++ b/units/getty-pre.target
@@ -0,0 +1,11 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Login Prompts (Pre)
+Documentation=man:systemd.special(7) man:systemd-getty-generator(8)
+Documentation=http://0pointer.de/blog/projects/serial-console.html
diff --git a/units/getty@.service.m4 b/units/getty@.service.m4
index 46164ab9d..ad4bf2103 100644
--- a/units/getty@.service.m4
+++ b/units/getty@.service.m4
@@ -9,7 +9,7 @@
 Description=Getty on %I
 Documentation=man:agetty(8) man:systemd-getty-generator(8)
 Documentation=http://0pointer.de/blog/projects/serial-console.html
-After=systemd-user-sessions.service plymouth-quit-wait.service
+After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
 m4_ifdef(`HAVE_SYSV_COMPAT',
 After=rc-local.service
 )m4_dnl
diff --git a/units/serial-getty@.service.m4 b/units/serial-getty@.service.m4
index 4522d0d2b..6802333f7 100644
--- a/units/serial-getty@.service.m4
+++ b/units/serial-getty@.service.m4
@@ -10,7 +10,7 @@ Description=Serial Getty on %I
 Documentation=man:agetty(8) man:systemd-getty-generator(8)
 Documentation=http://0pointer.de/blog/projects/serial-console.html
 BindsTo=dev-%i.device
-After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service
+After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
 m4_ifdef(`HAVE_SYSV_COMPAT',
 After=rc-local.service
 )m4_dnl
SOURCES/0513-units-order-container-and-console-getty-units-after-.patch
New file
@@ -0,0 +1,40 @@
From b10c083e9b9de46b54873780f73dce57fa1b6d4f Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Tue, 5 Sep 2017 14:53:25 +0200
Subject: [PATCH] units: order container and console getty units after
 getty-pre.target
(cherry picked from commit 45e27532971ac84e835a2879df510a581f933fcd)
Related: #1173080
---
 units/console-getty.service.m4.in    | 2 +-
 units/container-getty@.service.m4.in | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/units/console-getty.service.m4.in b/units/console-getty.service.m4.in
index 413d94094..61ecf8951 100644
--- a/units/console-getty.service.m4.in
+++ b/units/console-getty.service.m4.in
@@ -11,7 +11,7 @@ Documentation=man:agetty(8)
 After=systemd-user-sessions.service plymouth-quit-wait.service
 ConditionPathExists=/dev/console
 m4_ifdef(`HAVE_SYSV_COMPAT',
-After=rc-local.service
+After=rc-local.service getty-pre.target
 )m4_dnl
 Before=getty.target
diff --git a/units/container-getty@.service.m4.in b/units/container-getty@.service.m4.in
index e126f3a48..4395ef5ce 100644
--- a/units/container-getty@.service.m4.in
+++ b/units/container-getty@.service.m4.in
@@ -10,7 +10,7 @@ Description=Container Getty on /dev/pts/%I
 Documentation=man:agetty(8) man:machinectl(1)
 After=systemd-user-sessions.service plymouth-quit-wait.service
 m4_ifdef(`HAVE_SYSV_COMPAT',
-After=rc-local.service
+After=rc-local.service getty-pre.target
 )m4_dnl
 Before=getty.target
 IgnoreOnIsolate=yes
SOURCES/0514-log-never-log-into-foreign-fd-2-in-PID-1-or-its-pre-.patch
New file
@@ -0,0 +1,76 @@
From 5a7f49bb38bc1d7965d497e775b7cc8053b0c465 Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsynacek@redhat.com>
Date: Fri, 18 Aug 2017 10:17:22 +0200
Subject: [PATCH] log: never log into foreign fd #2 in PID 1 or its
 pre-execve() children
(cherry picked from commit 48a601fe5de8aa0d89ba6dadde168769fa7ce992)
Resolves: #1420505
---
 src/core/main.c  | 11 +++++++++--
 src/shared/log.c |  7 ++++++-
 src/shared/log.h |  1 +
 3 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/core/main.c b/src/core/main.c
index 37e3ea0ce..66393ed6a 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1310,10 +1310,17 @@ int main(int argc, char *argv[]) {
         log_show_color(isatty(STDERR_FILENO) > 0);
         log_set_upgrade_syslog_to_journal(true);
-        /* Disable the umask logic */
-        if (getpid() == 1)
+        if (getpid() == 1) {
+                /* Disable the umask logic */
                 umask(0);
+                /* Always reopen /dev/console when running as PID 1 or one of its pre-execve() children. This is
+                 * important so that we never end up logging to any foreign stderr, for example if we have to log in a
+                 * child process right before execve()'ing the actual binary, at a point in time where socket
+                 * activation stderr/stdout area already set up. */
+                log_set_always_reopen_console(true);
+        }
+
         if (getpid() == 1 && detect_container(NULL) <= 0) {
                 /* Running outside of a container as PID 1 */
diff --git a/src/shared/log.c b/src/shared/log.c
index 646a1d638..349142030 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -52,6 +52,7 @@ static bool show_color = false;
 static bool show_location = false;
 static bool upgrade_syslog_to_journal = false;
+static bool always_reopen_console = false;
 /* Akin to glibc's __abort_msg; which is private and we hence cannot
  * use here. */
@@ -75,7 +76,7 @@ static int log_open_console(void) {
         if (console_fd >= 0)
                 return 0;
-        if (getpid() == 1) {
+        if (always_reopen_console) {
                 console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
                 if (console_fd < 0)
                         return console_fd;
@@ -1061,3 +1062,7 @@ void log_received_signal(int level, const struct signalfd_siginfo *si) {
 void log_set_upgrade_syslog_to_journal(bool b) {
         upgrade_syslog_to_journal = b;
 }
+
+void log_set_always_reopen_console(bool b) {
+        always_reopen_console = b;
+}
diff --git a/src/shared/log.h b/src/shared/log.h
index 2889e1e77..3c9448f1a 100644
--- a/src/shared/log.h
+++ b/src/shared/log.h
@@ -210,3 +210,4 @@ LogTarget log_target_from_string(const char *s) _pure_;
 void log_received_signal(int level, const struct signalfd_siginfo *si);
 void log_set_upgrade_syslog_to_journal(bool b);
+void log_set_always_reopen_console(bool b);
SOURCES/0515-nspawn-new-option-to-start-as-PID2.patch
New file
@@ -0,0 +1,491 @@
From 41e91ccdf9fa3097d7b90718cc83e743f4dc8d6b Mon Sep 17 00:00:00 2001
From: Jan Rybar <jrybar@redhat.com>
Date: Thu, 17 Aug 2017 18:01:42 +0200
Subject: [PATCH] nspawn: new option to start as PID2
Cherry-picked from: 7732f92
Resolves: #1417387
---
 Makefile.am                   |   2 +
 man/systemd-nspawn.xml        |  65 ++++++++++++--
 src/nspawn/nspawn-stub-pid1.c | 196 ++++++++++++++++++++++++++++++++++++++++++
 src/nspawn/nspawn-stub-pid1.h |  22 +++++
 src/nspawn/nspawn.c           |  56 ++++++++++--
 5 files changed, 328 insertions(+), 13 deletions(-)
 create mode 100644 src/nspawn/nspawn-stub-pid1.c
 create mode 100644 src/nspawn/nspawn-stub-pid1.h
diff --git a/Makefile.am b/Makefile.am
index 7c58fd050..0e2f8d561 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2658,6 +2658,8 @@ systemd_cgtop_LDADD = \
 # ------------------------------------------------------------------------------
 systemd_nspawn_SOURCES = \
     src/nspawn/nspawn.c \
+    src/nspawn/nspawn-stub-pid1.c \
+    src/nspawn/nspawn-stub-pid1.h \
     src/core/mount-setup.c \
     src/core/mount-setup.h \
     src/core/loopback-setup.c \
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index cbd44d4ab..d0eddaacc 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -241,16 +241,69 @@
         <option>--ephemeral</option>.</para></listitem>
       </varlistentry>
+      <varlistentry>
+      <term><option>-a</option></term>
+        <term><option>--as-pid2</option></term>
+
+        <listitem><para>Invoke the shell or specified program as process ID (PID) 2 instead of PID 1 (init). By
+        default, if neither this option nor <option>--boot</option> is used, the selected binary is run as process with
+        PID 1, a mode only suitable for programs that are aware of the special semantics that the process with PID 1
+        has on UNIX. For example, it needs to reap all processes reparented to it, and should implement
+        <command>sysvinit</command> compatible signal handling (specifically: it needs to reboot on SIGINT, reexecute
+        on SIGTERM, reload configuration on SIGHUP, and so on). With <option>--as-pid2</option> a minimal stub init
+        process is run as PID 1 and the selected binary is executed as PID 2 (and hence does not need to implement any
+        special semantics). The stub init process will reap processes as necessary and react appropriately to
+        signals. It is recommended to use this mode to invoke arbitrary commands in containers, unless they have been
+        modified to run correctly as PID 1. Or in other words: this switch should be used for pretty much all commands,
+        except when the command refers to an init or shell implementation, as these are generally capable of running
+        correctly as PID 1). This option may not be combined with <option>--boot</option> or
+        <option>--share-system</option>.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-b</option></term>
         <term><option>--boot</option></term>
-        <listitem><para>Automatically search for an init binary and
-        invoke it instead of a shell or a user supplied program. If
-        this option is used, arguments specified on the command line
-        are used as arguments for the init binary. This option may not
-        be combined with <option>--share-system</option>.
-        </para></listitem>
+        <listitem><para>Automatically search for an init binary and invoke it as PID 1, instead of a shell or a user
+        supplied program. If this option is used, arguments specified on the command line are used as arguments for the
+        init binary. This option may not be combined with <option>--as-pid2</option> or
+        <option>--share-system</option>.</para>
+
+        <para>The following table explains the different modes of invocation and relationship to
+        <option>--as-pid2</option> (see above):</para>
+
+        <table>
+          <title>Invocation Mode</title>
+          <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+            <colspec colname="switch" />
+            <colspec colname="explanation" />
+            <thead>
+              <row>
+                <entry>Switch</entry>
+                <entry>Explanation</entry>
+              </row>
+            </thead>
+            <tbody>
+              <row>
+                <entry>Neither <option>--as-pid2</option> nor <option>--boot</option> specified</entry>
+                <entry>The passed parameters are interpreted as command line, which is executed as PID 1 in the container.</entry>
+              </row>
+
+              <row>
+                <entry><option>--as-pid2</option> specified</entry>
+                <entry>The passed parameters are interpreted as command line, which are executed as PID 2 in the container. A stub init process is run as PID 1.</entry>
+              </row>
+
+              <row>
+                <entry><option>--boot</option> specified</entry>
+                <entry>An init binary as automatically searched and run as PID 1 in the container. The passed parameters are used as invocation parameters for this process.</entry>
+              </row>
+
+            </tbody>
+          </tgroup>
+        </table>
+        </listitem>
       </varlistentry>
       <varlistentry>
diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c
new file mode 100644
index 000000000..11c11560c
--- /dev/null
+++ b/src/nspawn/nspawn-stub-pid1.c
@@ -0,0 +1,196 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/reboot.h>
+#include <sys/unistd.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+
+#include "log.h"
+#include "nspawn-stub-pid1.h"
+#include "util.h"
+#include "time-util.h"
+#include "def.h"
+
+static int reset_environ(const char *new_environment, size_t length) {
+        unsigned long start, end;
+
+        start = (unsigned long) new_environment;
+        end = start + length;
+
+        if (prctl(PR_SET_MM, PR_SET_MM_ENV_START, start, 0, 0) < 0)
+                return -errno;
+
+        if (prctl(PR_SET_MM, PR_SET_MM_ENV_END, end, 0, 0) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int stub_pid1(sd_id128_t uuid) {
+        enum {
+                STATE_RUNNING,
+                STATE_REBOOT,
+                STATE_POWEROFF,
+        } state = STATE_RUNNING;
+
+        sigset_t fullmask, oldmask, waitmask;
+        usec_t quit_usec = USEC_INFINITY;
+        pid_t pid;
+        int r;
+
+        /* The new environment we set up, on the stack. */
+        char new_environment[] =
+                "container=systemd-nspawn\0"
+                "container_uuid=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
+
+        /* Implements a stub PID 1, that reaps all processes and processes a couple of standard signals. This is useful
+         * for allowing arbitrary processes run in a container, and still have all zombies reaped. */
+
+        assert_se(sigfillset(&fullmask) >= 0);
+        assert_se(sigprocmask(SIG_BLOCK, &fullmask, &oldmask) >= 0);
+
+        pid = fork();
+        if (pid < 0)
+                return log_error_errno(errno, "Failed to fork child pid: %m");
+
+        if (pid == 0) {
+                /* Return in the child */
+                assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) >= 0);
+                setsid();
+                return 0;
+        }
+
+        reset_all_signal_handlers();
+
+        log_close();
+        close_all_fds(NULL, 0);
+        log_open();
+
+        /* Flush out /proc/self/environ, so that we don't leak the environment from the host into the container. Also,
+         * set $container= and $container_uuid= so that clients in the container that query it from /proc/1/environ
+         * find them set. */
+        sd_id128_to_string(uuid, new_environment + sizeof(new_environment) - SD_ID128_STRING_MAX);
+        reset_environ(new_environment, sizeof(new_environment));
+
+        rename_process("STUBINIT");
+
+        assert_se(sigemptyset(&waitmask) >= 0);
+
+        sigset_add_many(&waitmask,
+                        SIGCHLD,          /* posix: process died */
+                        SIGINT,           /* sysv: ctrl-alt-del */
+                        SIGRTMIN+3,       /* systemd: halt */
+                        SIGRTMIN+4,       /* systemd: poweroff */
+                        SIGRTMIN+5,       /* systemd: reboot */
+                        SIGRTMIN+6,       /* systemd: kexec */
+                        SIGRTMIN+13,      /* systemd: halt */
+                        SIGRTMIN+14,      /* systemd: poweroff */
+                        SIGRTMIN+15,      /* systemd: reboot */
+                        SIGRTMIN+16,      /* systemd: kexec */
+                        -1);
+
+        /* Note that we ignore SIGTERM (sysv's reexec), SIGHUP (reload), and all other signals here, since we don't
+         * support reexec/reloading in this stub process. */
+
+        for (;;) {
+                siginfo_t si;
+                usec_t current_usec;
+
+                si.si_pid = 0;
+                r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG);
+                if (r < 0) {
+                        r = log_error_errno(errno, "Failed to reap children: %m");
+                        goto finish;
+                }
+
+                current_usec = now(CLOCK_MONOTONIC);
+
+                if (si.si_pid == pid || current_usec >= quit_usec) {
+
+                        /* The child we started ourselves died or we reached a timeout. */
+
+                        if (state == STATE_REBOOT) { /* dispatch a queued reboot */
+                                (void) reboot(RB_AUTOBOOT);
+                                r = log_error_errno(errno, "Failed to reboot: %m");
+                                goto finish;
+
+                        } else if (state == STATE_POWEROFF)
+                                (void) reboot(RB_POWER_OFF); /* if this fails, fall back to normal exit. */
+
+                        if (si.si_pid == pid && si.si_code == CLD_EXITED)
+                                r = si.si_status; /* pass on exit code */
+                        else
+                                r = 255; /* signal, coredump, timeout, … */
+
+                        goto finish;
+                }
+                if (si.si_pid != 0)
+                        /* We reaped something. Retry until there's nothing more to reap. */
+                        continue;
+
+                if (quit_usec == USEC_INFINITY)
+                        r = sigwaitinfo(&waitmask, &si);
+                else {
+                        struct timespec ts;
+                        r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec));
+                }
+                if (r < 0) {
+                        if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */
+                                continue;
+                        if (errno == EAGAIN) /* timeout reached */
+                                continue;
+
+                        r = log_error_errno(errno, "Failed to wait for signal: %m");
+                        goto finish;
+                }
+
+                if (si.si_signo == SIGCHLD)
+                        continue; /* Let's reap this */
+
+                if (state != STATE_RUNNING)
+                        continue;
+
+                /* Would love to use a switch() statement here, but SIGRTMIN is actually a function call, not a
+                 * constant… */
+
+                if (si.si_signo == SIGRTMIN+3 ||
+                    si.si_signo == SIGRTMIN+4 ||
+                    si.si_signo == SIGRTMIN+13 ||
+                    si.si_signo == SIGRTMIN+14)
+
+                        state = STATE_POWEROFF;
+
+                else if (si.si_signo == SIGINT ||
+                         si.si_signo == SIGRTMIN+5 ||
+                         si.si_signo == SIGRTMIN+6 ||
+                         si.si_signo == SIGRTMIN+15 ||
+                         si.si_signo == SIGRTMIN+16)
+
+                        state = STATE_REBOOT;
+                else
+                        assert_not_reached("Got unexpected signal");
+
+                /* (void) kill_and_sigcont(pid, SIGTERM); */
+                quit_usec = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC;
+        }
+
+finish:
+        _exit(r < 0 ? EXIT_FAILURE : r);
+}
diff --git a/src/nspawn/nspawn-stub-pid1.h b/src/nspawn/nspawn-stub-pid1.h
new file mode 100644
index 000000000..be0f1af4c
--- /dev/null
+++ b/src/nspawn/nspawn-stub-pid1.h
@@ -0,0 +1,22 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int stub_pid1(sd_id128_t uuid);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index d0003d379..ea365b3f9 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -99,6 +99,7 @@
 #include "in-addr-util.h"
 #include "fw-util.h"
 #include "local-addresses.h"
+#include "nspawn-stub-pid1.h"
 #ifdef HAVE_SECCOMP
 #include "seccomp-util.h"
@@ -129,6 +130,14 @@ typedef enum Volatile {
         VOLATILE_STATE,
 } Volatile;
+typedef enum StartMode {
+        START_PID1, /* Run parameters as command line as process 1 */
+        START_PID2, /* Use stub init process as PID 1, run parameters as command line as process 2 */
+        START_BOOT, /* Search for init system, pass arguments as parameters */
+        _START_MODE_MAX,
+        _START_MODE_INVALID = -1
+} StartMode;
+
 static char *arg_directory = NULL;
 static char *arg_template = NULL;
 static char *arg_user = NULL;
@@ -139,7 +148,7 @@ static const char *arg_selinux_apifs_context = NULL;
 static const char *arg_slice = NULL;
 static bool arg_private_network = false;
 static bool arg_read_only = false;
-static bool arg_boot = false;
+static StartMode arg_start_mode = START_PID1;
 static bool arg_ephemeral = false;
 static LinkJournal arg_link_journal = LINK_AUTO;
 static bool arg_link_journal_try = false;
@@ -200,6 +209,7 @@ static void help(void) {
                "  -x --ephemeral            Run container with snapshot of root directory, and\n"
                "                            remove it after exit\n"
                "  -i --image=PATH           File system device or disk image for the container\n"
+               "  -a --as-pid2              Maintain a stub init as PID1, invoke binary as PID2\n"
                "  -b --boot                 Boot up full system (i.e. invoke init)\n"
                "  -u --user=USER            Run the command under specified user or uid\n"
                "  -M --machine=NAME         Set the machine name for the container\n"
@@ -304,6 +314,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "ephemeral",             no_argument,       NULL, 'x'                   },
                 { "user",                  required_argument, NULL, 'u'                   },
                 { "private-network",       no_argument,       NULL, ARG_PRIVATE_NETWORK   },
+                { "as-pid2",               no_argument,       NULL, 'a'                   },
                 { "boot",                  no_argument,       NULL, 'b'                   },
                 { "uuid",                  required_argument, NULL, ARG_UUID              },
                 { "read-only",             no_argument,       NULL, ARG_READ_ONLY         },
@@ -340,7 +351,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
-        while ((c = getopt_long(argc, argv, "+hD:u:bL:M:jS:Z:qi:xp:n", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:n", options, NULL)) >= 0)
                 switch (c) {
@@ -421,7 +432,21 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 case 'b':
-                        arg_boot = true;
+                        if (arg_start_mode == START_PID2) {
+                                log_error("--boot and --as-pid2 may not be combined.");
+                                return -EINVAL;
+                        }
+
+                        arg_start_mode = START_BOOT;
+                        break;
+
+                case 'a':
+                        if (arg_start_mode == START_BOOT) {
+                                log_error("--boot and --as-pid2 may not be combined.");
+                                return -EINVAL;
+                        }
+
+                        arg_start_mode = START_PID2;
                         break;
                 case ARG_UUID:
@@ -741,7 +766,7 @@ static int parse_argv(int argc, char *argv[]) {
         if (arg_share_system)
                 arg_register = false;
-        if (arg_boot && arg_share_system) {
+        if (arg_start_mode != START_PID1 && arg_share_system) {
                 log_error("--boot and --share-system may not be combined.");
                 return -EINVAL;
         }
@@ -3586,6 +3611,10 @@ int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
+        /* Make sure rename_process() in the stub init process can work */
+        saved_argv = argv;
+        saved_argc = argc;
+
         r = parse_argv(argc, argv);
         if (r <= 0)
                 goto finish;
@@ -3694,7 +3723,7 @@ int main(int argc, char *argv[]) {
                         }
                 }
-                if (arg_boot) {
+                if (arg_start_mode == START_BOOT) {
                         if (path_is_os_tree(arg_directory) <= 0) {
                                 log_error("Directory %s doesn't look like an OS root directory (os-release file is missing). Refusing.", arg_directory);
                                 r = -EINVAL;
@@ -4109,7 +4138,19 @@ int main(int argc, char *argv[]) {
                         if (!barrier_place_and_sync(&barrier))
                                 _exit(EXIT_FAILURE);
-                        if (arg_boot) {
+                        if (arg_start_mode == START_PID2) {
+                                r = stub_pid1(arg_uuid);
+                                if (r < 0)
+                                {
+                                        log_error_errno(r, "Failed to start as PID2: %m");
+                                        _exit(EXIT_FAILURE);
+                                }
+                        }
+
+                        log_close();
+                        (void) fdset_close_others(fds);
+
+                        if (arg_start_mode == START_BOOT) {
                                 char **a;
                                 size_t l;
@@ -4135,6 +4176,7 @@ int main(int argc, char *argv[]) {
                                 execle("/bin/sh", "-sh", NULL, env_use);
                         }
+                        log_open();
                         log_error_errno(errno, "execv() failed: %m");
                         _exit(EXIT_FAILURE);
                 }
@@ -4210,7 +4252,7 @@ int main(int argc, char *argv[]) {
                                         goto finish;
                                 }
-                                if (arg_boot) {
+                                if (arg_start_mode == START_BOOT) {
                                         /* Try to kill the init system on SIGINT or SIGTERM */
                                         sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, UINT32_TO_PTR(pid));
                                         sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, UINT32_TO_PTR(pid));
SOURCES/0516-journal-implicitly-flush-to-var-on-recovery-4028.patch
New file
@@ -0,0 +1,77 @@
From 80f0fa4a77bfceab3bae7cf67f44b8f899b22427 Mon Sep 17 00:00:00 2001
From: Vito Caputo <vcaputo@gnugeneration.com>
Date: Tue, 18 Jul 2017 18:00:37 +0200
Subject: [PATCH] journal: implicitly flush to var on recovery (#4028)
When the system journal becomes re-opened post-flush with the runtime
journal open, it implies we've recovered from something like an ENOSPC
situation where the system journal rotate had failed, leaving the system
journal closed, causing the runtime journal to be opened post-flush.
For the duration of the unavailable system journal, we log to the
runtime journal.  But when the system journal gets opened (space made
available, for example), we need to close the runtime journal before new
journal writes will go to the system journal.  Calling
server_flush_to_var() after opening the system journal with a runtime
journal present, post-flush, achieves this while preserving the runtime
journal's contents in the system journal.
The combination of the present flushed flag file and the runtime journal
being open is a state where we should be logging to the system journal,
so it's appropriate to resume doing so once we've successfully opened
the system journal.
(cherry picked from commit 929eeb5498e8ae87e05ae683c6d3014d4b59056d)
Related: #1364092
---
 src/journal/journald-server.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 2b7ecd09a..3e9412d57 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -923,6 +923,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
         char *fn;
         sd_id128_t machine;
         char ids[33];
+        bool flushed = false;
         r = sd_id128_get_machine(&machine);
         if (r < 0)
@@ -933,7 +934,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
         if (!s->system_journal &&
             (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
             (flush_requested
-             || access("/run/systemd/journal/flushed", F_OK) >= 0)) {
+             || (flushed = (access("/run/systemd/journal/flushed", F_OK) >= 0)))) {
                 /* If in auto mode: first try to create the machine
                  * path, but not the prefix.
@@ -958,6 +959,16 @@ static int system_journal_open(Server *s, bool flush_requested) {
                         r = 0;
                 }
+
+                /* If the runtime journal is open, and we're post-flush, we're
+                 * recovering from a failed system journal rotate (ENOSPC)
+                 * for which the runtime journal was reopened.
+                 *
+                 * Perform an implicit flush to var, leaving the runtime
+                 * journal closed, now that the system journal is back.
+                 */
+                if (s->runtime_journal && flushed)
+                        (void) server_flush_to_var(s);
         }
         if (!s->runtime_journal &&
@@ -1230,7 +1241,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
         log_info("Received request to flush runtime journal from PID %"PRIu32, si->ssi_pid);
-        server_flush_to_var(s);
+        (void) server_flush_to_var(s);
         server_sync(s);
         server_vacuum(s);
SOURCES/0517-journal-add-use-flushed_flag_is_set-helper-4041.patch
New file
@@ -0,0 +1,38 @@
From 0adc312bbf5ea8ea654a5a4740f78f37eda2e9d3 Mon Sep 17 00:00:00 2001
From: Vito Caputo <vcaputo@gnugeneration.com>
Date: Thu, 17 Aug 2017 09:45:38 +0200
Subject: [PATCH] journal: add/use flushed_flag_is_set() helper (#4041)
Minor cleanup suggested by Lennart.
(cherry-picked from commit 6431c7e216ceb9f3cfe073c94a47ac413b892e55)
Related: #1364092
---
 src/journal/journald-server.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 3e9412d57..96ffda4ec 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -917,6 +917,9 @@ finish:
         dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid);
 }
+static bool flushed_flag_is_set(void) {
+        return (access("/run/systemd/journal/flushed", F_OK) >= 0);
+}
 static int system_journal_open(Server *s, bool flush_requested) {
         int r;
@@ -933,8 +936,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
         if (!s->system_journal &&
             (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
-            (flush_requested
-             || (flushed = (access("/run/systemd/journal/flushed", F_OK) >= 0)))) {
+            (flush_requested || (flushed = flushed_flag_is_set()))) {
                 /* If in auto mode: first try to create the machine
                  * path, but not the prefix.
SOURCES/0518-journald-don-t-flush-to-var-log-journal-before-we-ge.patch
New file
@@ -0,0 +1,142 @@
From 6032a92b8fb27a7c65a1853e62a142fd9a062b73 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 17 Aug 2017 10:21:23 +0200
Subject: [PATCH] journald: don't flush to /var/log/journal before we get asked
 to
This changes journald to not write to /var/log/journal until it received
SIGUSR1 for the first time, thus having been requested to flush the runtime
journal to disk.
This makes the journal work nicer with systems which have the root file system
writable early, but still need to rearrange /var before journald should start
writing and creating files to it, for example because ACLs need to be applied
first, or because /var is to be mounted from another file system, NFS or tmpfs
(as is the case for systemd.volatile=state).
Before this change we required setupts with /var split out to mount the root
disk read-only early on, and ship an /etc/fstab that remounted it writable only
after having placed /var at the right place. But even that was racy for various
preparations as journald might end up accessing the file system before it was
entirely set up, as soon as it was writable.
With this change we make scheduling when to start writing to /var/log/journal
explicit. This means persistent mode now requires
systemd-journal-flush.service in the mix to work, as otherwise journald would
never write to the directory.
See: #1397
(cherry-picked from commit f78273c8dacf678cc8fd7387f678e6344a99405c)
Resolves: #1364092
---
 src/journal/journald-server.c | 21 +++++++++++----------
 src/journal/journald-server.h |  2 +-
 src/journal/journald.c        |  2 +-
 3 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 96ffda4ec..07426b41e 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -918,7 +918,7 @@ finish:
 }
 static bool flushed_flag_is_set(void) {
-        return (access("/run/systemd/journal/flushed", F_OK) >= 0);
+        return access("/run/systemd/journal/flushed", F_OK) >= 0;
 }
 static int system_journal_open(Server *s, bool flush_requested) {
@@ -926,7 +926,6 @@ static int system_journal_open(Server *s, bool flush_requested) {
         char *fn;
         sd_id128_t machine;
         char ids[33];
-        bool flushed = false;
         r = sd_id128_get_machine(&machine);
         if (r < 0)
@@ -935,8 +934,8 @@ static int system_journal_open(Server *s, bool flush_requested) {
         sd_id128_to_string(machine, ids);
         if (!s->system_journal &&
-            (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
-            (flush_requested || (flushed = flushed_flag_is_set()))) {
+            IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) &&
+            (flush_requested || flushed_flag_is_set())) {
                 /* If in auto mode: first try to create the machine
                  * path, but not the prefix.
@@ -969,8 +968,8 @@ static int system_journal_open(Server *s, bool flush_requested) {
                  * Perform an implicit flush to var, leaving the runtime
                  * journal closed, now that the system journal is back.
                  */
-                if (s->runtime_journal && flushed)
-                        (void) server_flush_to_var(s);
+                if (!flush_requested)
+                        (void) server_flush_to_var(s, true);
         }
         if (!s->runtime_journal &&
@@ -1021,7 +1020,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
         return r;
 }
-int server_flush_to_var(Server *s) {
+int server_flush_to_var(Server *s, bool require_flag_file) {
         sd_id128_t machine;
         sd_journal *j = NULL;
         char ts[FORMAT_TIMESPAN_MAX];
@@ -1031,13 +1030,15 @@ int server_flush_to_var(Server *s) {
         assert(s);
-        if (s->storage != STORAGE_AUTO &&
-            s->storage != STORAGE_PERSISTENT)
+        if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
                 return 0;
         if (!s->runtime_journal)
                 return 0;
+        if (require_flag_file && !flushed_flag_is_set())
+                return 0;
+
         system_journal_open(s, true);
         if (!s->system_journal)
@@ -1243,7 +1244,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
         log_info("Received request to flush runtime journal from PID %"PRIu32, si->ssi_pid);
-        (void) server_flush_to_var(s);
+        (void) server_flush_to_var(s, false);
         server_sync(s);
         server_vacuum(s);
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index b1263a758..7a456c2d5 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -173,6 +173,6 @@ void server_sync(Server *s);
 void server_vacuum(Server *s);
 void server_rotate(Server *s);
 int server_schedule_sync(Server *s, int priority);
-int server_flush_to_var(Server *s);
+int server_flush_to_var(Server *s, bool require_flag_file);
 void server_maybe_append_tags(Server *s);
 int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata);
diff --git a/src/journal/journald.c b/src/journal/journald.c
index 80f4634f6..15bbcbe3d 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -58,7 +58,7 @@ int main(int argc, char *argv[]) {
                 goto finish;
         server_vacuum(&server);
-        server_flush_to_var(&server);
+        server_flush_to_var(&server, true);
         server_flush_dev_kmsg(&server);
         log_debug("systemd-journald running as pid "PID_FMT, getpid());
SOURCES/0519-path-util-make-use-of-mnt_id-field-exported-in-proc-.patch
New file
@@ -0,0 +1,560 @@
From f63b66b6347a8d8e5e6930a939d1997bfd8e2e7c Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsynacek@redhat.com>
Date: Fri, 28 Jul 2017 15:31:50 +0200
Subject: [PATCH] path-util: make use of "mnt_id" field exported in
 /proc/self/fdinfo/<fd>
This commit is not a backport of a specific commit. It includes parts of
several upstream commits (3f72b427b44f39a1aec6806dad6f6b57103ae9ed,
5d409034017e9f9f8c4392157d95511fc2e05d87 and others).
The main goal was to bring path_is_mount_point() up to date, which meant
introducing fd_fdinfo_mnt_id() and fd_is_mount_point(). These were
needed mainly because we need to determine mount points based on
/proc/self/fdinfo/<fd> in containers. Also, there are more places in the
code where checks for mount points are performed, which would benefit from
this fix as well. Additionally, corresponding tests has been added.
Resolves: #1472439
---
 src/core/automount.c                        |   2 +-
 src/core/machine-id-setup.c                 |   2 +-
 src/core/mount-setup.c                      |   2 +-
 src/efi-boot-generator/efi-boot-generator.c |   2 +-
 src/gpt-auto-generator/gpt-auto-generator.c |   2 +-
 src/login/logind-user.c                     |   2 +-
 src/nspawn/nspawn.c                         |  10 +-
 src/shared/cgroup-util.c                    |   2 +-
 src/shared/condition.c                      |   2 +-
 src/shared/path-util.c                      | 209 ++++++++++++++++++++++------
 src/shared/path-util.h                      |   3 +-
 src/test/test-path-util.c                   |  66 ++++++++-
 12 files changed, 242 insertions(+), 62 deletions(-)
diff --git a/src/core/automount.c b/src/core/automount.c
index 4e066613d..eedd9b824 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -749,7 +749,7 @@ static int automount_start(Unit *u) {
         assert(a);
         assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
-        if (path_is_mount_point(a->where, false)) {
+        if (path_is_mount_point(a->where, 0)) {
                 log_unit_error(u->id,
                                "Path %s is already a mount point, refusing start for %s",
                                a->where, u->id);
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index d00a53246..1121d373f 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -203,7 +203,7 @@ int machine_id_commit(const char *root) {
                 etc_machine_id = path_kill_slashes(x);
         }
-        r = path_is_mount_point(etc_machine_id, false);
+        r = path_is_mount_point(etc_machine_id, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
         if (r == 0) {
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 521545e5c..2b8fbab1a 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -160,7 +160,7 @@ static int mount_one(const MountPoint *p, bool relabel) {
         if (relabel)
                 label_fix(p->where, true, true);
-        r = path_is_mount_point(p->where, true);
+        r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW);
         if (r < 0)
                 return r;
diff --git a/src/efi-boot-generator/efi-boot-generator.c b/src/efi-boot-generator/efi-boot-generator.c
index b3ff3a8b7..5492b1994 100644
--- a/src/efi-boot-generator/efi-boot-generator.c
+++ b/src/efi-boot-generator/efi-boot-generator.c
@@ -69,7 +69,7 @@ int main(int argc, char *argv[]) {
                 return EXIT_SUCCESS;
         }
-        if (path_is_mount_point("/boot", true) <= 0 &&
+        if (path_is_mount_point("/boot", AT_SYMLINK_FOLLOW) <= 0 &&
             dir_is_empty("/boot") <= 0) {
                 log_debug("/boot already populated, exiting.");
                 return EXIT_SUCCESS;
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 00a2141a5..d7b047118 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -299,7 +299,7 @@ static int probe_and_add_mount(
         assert(where);
         assert(description);
-        if (path_is_mount_point(where, true) <= 0 &&
+        if (path_is_mount_point(where, AT_SYMLINK_FOLLOW) <= 0 &&
             dir_is_empty(where) <= 0) {
                 log_debug("%s already populated, ignoring.", where);
                 return 0;
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 4298704ce..912c50ebd 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -320,7 +320,7 @@ static int user_mkdir_runtime_path(User *u) {
         } else
                 p = u->runtime_path;
-        if (path_is_mount_point(p, false) <= 0) {
+        if (path_is_mount_point(p, 0) <= 0) {
                 _cleanup_free_ char *t = NULL;
                 (void) mkdir(p, 0700);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index ea365b3f9..a90a3a5d7 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -863,7 +863,7 @@ static int mount_all(const char *dest) {
                 if (!where)
                         return log_oom();
-                t = path_is_mount_point(where, true);
+                t = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
                 if (t < 0) {
                         log_error_errno(t, "Failed to detect whether %s is a mount point: %m", where);
@@ -989,7 +989,7 @@ static int mount_cgroup_hierarchy(const char *dest, const char *controller, cons
         to = strjoina(dest, "/sys/fs/cgroup/", hierarchy);
-        r = path_is_mount_point(to, false);
+        r = path_is_mount_point(to, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
         if (r > 0)
@@ -1787,7 +1787,7 @@ static int setup_journal(const char *directory) {
         if (!p || !q)
                 return log_oom();
-        if (path_is_mount_point(p, false) > 0) {
+        if (path_is_mount_point(p, 0) > 0) {
                 if (arg_link_journal != LINK_AUTO) {
                         log_error("%s: already a mount point, refusing to use for journal", p);
                         return -EEXIST;
@@ -1796,7 +1796,7 @@ static int setup_journal(const char *directory) {
                 return 0;
         }
-        if (path_is_mount_point(q, false) > 0) {
+        if (path_is_mount_point(q, 0) > 0) {
                 if (arg_link_journal != LINK_AUTO) {
                         log_error("%s: already a mount point, refusing to use for journal", q);
                         return -EEXIST;
@@ -3665,7 +3665,7 @@ int main(int argc, char *argv[]) {
                          * the specified is not a mount point we
                          * create the new snapshot in the parent
                          * directory, just next to it. */
-                        r = path_is_mount_point(arg_directory, false);
+                        r = path_is_mount_point(arg_directory, 0);
                         if (r < 0) {
                                 log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory);
                                 goto finish;
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index c5d9e4bb5..cf085cb5f 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -488,7 +488,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
         if (_unlikely_(!good)) {
                 int r;
-                r = path_is_mount_point("/sys/fs/cgroup", false);
+                r = path_is_mount_point("/sys/fs/cgroup", 0);
                 if (r <= 0)
                         return r < 0 ? r : -ENOENT;
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 796cc520d..0d2cd2bc3 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -350,7 +350,7 @@ static int condition_test_path_is_mount_point(Condition *c) {
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
-        return path_is_mount_point(c->parameter, true) > 0;
+        return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
 }
 static int condition_test_path_is_read_write(Condition *c) {
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
index 1181ffb9d..0f252ec26 100644
--- a/src/shared/path-util.c
+++ b/src/shared/path-util.c
@@ -36,6 +36,7 @@
 #include "strv.h"
 #include "path-util.h"
 #include "missing.h"
+#include "fileio.h"
 bool path_is_absolute(const char *p) {
         return p[0] == '/';
@@ -473,87 +474,203 @@ char* path_join(const char *root, const char *path, const char *rest) {
                                NULL);
 }
-int path_is_mount_point(const char *t, bool allow_symlink) {
+static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
+        char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
+        _cleanup_free_ char *fdinfo = NULL;
+        _cleanup_close_ int subfd = -1;
+        char *p;
+        int r;
+
+        if ((flags & AT_EMPTY_PATH) && isempty(filename))
+                xsprintf(path, "/proc/self/fdinfo/%i", fd);
+        else {
+                subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
+                if (subfd < 0)
+                        return -errno;
+
+                xsprintf(path, "/proc/self/fdinfo/%i", subfd);
+        }
+
+        r = read_full_file(path, &fdinfo, NULL);
+        if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
+                return -EOPNOTSUPP;
+        if (r < 0)
+                return -errno;
+
+        p = startswith(fdinfo, "mnt_id:");
+        if (!p) {
+                p = strstr(fdinfo, "\nmnt_id:");
+                if (!p) /* The mnt_id field is a relatively new addition */
+                        return -EOPNOTSUPP;
+
+                p += 8;
+        }
-        union file_handle_union h = FILE_HANDLE_INIT;
+        p += strspn(p, WHITESPACE);
+        p[strcspn(p, WHITESPACE)] = 0;
+
+        return safe_atoi(p, mnt_id);
+}
+
+int fd_is_mount_point(int fd, const char *filename, int flags) {
+        union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
         int mount_id = -1, mount_id_parent = -1;
-        _cleanup_free_ char *parent = NULL;
+        bool nosupp = false, check_st_dev = true;
         struct stat a, b;
         int r;
-        bool nosupp = false;
-        /* We are not actually interested in the file handles, but
-         * name_to_handle_at() also passes us the mount ID, hence use
-         * it but throw the handle away */
+        assert(fd >= 0);
+        assert(filename);
-        if (path_equal(t, "/"))
-                return 1;
-
-        r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
+        /* First we will try the name_to_handle_at() syscall, which
+         * tells us the mount id and an opaque file "handle". It is
+         * not supported everywhere though (kernel compile-time
+         * option, not all file systems are hooked up). If it works
+         * the mount id is usually good enough to tell us whether
+         * something is a mount point.
+         *
+         * If that didn't work we will try to read the mount id from
+         * /proc/self/fdinfo/<fd>. This is almost as good as
+         * name_to_handle_at(), however, does not return the
+         * opaque file handle. The opaque file handle is pretty useful
+         * to detect the root directory, which we should always
+         * consider a mount point. Hence we use this only as
+         * fallback. Exporting the mnt_id in fdinfo is a pretty recent
+         * kernel addition.
+         *
+         * As last fallback we do traditional fstat() based st_dev
+         * comparisons. This is how things were traditionally done,
+         * but unionfs breaks breaks this since it exposes file
+         * systems with a variety of st_dev reported. Also, btrfs
+         * subvolumes have different st_dev, even though they aren't
+         * real mounts of their own. */
+
+        r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
         if (r < 0) {
                 if (errno == ENOSYS)
                         /* This kernel does not support name_to_handle_at()
-                         * fall back to the traditional stat() logic. */
-                        goto fallback;
+                         * fall back to simpler logic. */
+                        goto fallback_fdinfo;
                 else if (errno == EOPNOTSUPP)
                         /* This kernel or file system does not support
-                         * name_to_handle_at(), hence fallback to the
+                         * name_to_handle_at(), hence let's see if the
+                         * upper fs supports it (in which case it is a
+                         * mount point), otherwise fallback to the
                          * traditional stat() logic */
                         nosupp = true;
-                else if (errno == ENOENT)
-                        return 0;
                 else
                         return -errno;
         }
-        r = path_get_parent(t, &parent);
-        if (r < 0)
-                return r;
-
-        h.handle.handle_bytes = MAX_HANDLE_SZ;
-        r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, AT_SYMLINK_FOLLOW);
-        if (r < 0)
-                if (errno == EOPNOTSUPP)
+        r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
+        if (r < 0) {
+                if (errno == EOPNOTSUPP) {
                         if (nosupp)
                                 /* Neither parent nor child do name_to_handle_at()?
                                    We have no choice but to fall back. */
-                                goto fallback;
+                                goto fallback_fdinfo;
                         else
-                                /* The parent can't do name_to_handle_at() but
-                                 * the directory we are interested in can?
-                                 * Or the other way around?
+                                /* The parent can't do name_to_handle_at() but the
+                                 * directory we are interested in can?
                                  * If so, it must be a mount point. */
                                 return 1;
-                else
+                } else
                         return -errno;
-        else
-                return mount_id != mount_id_parent;
+        }
-fallback:
-        if (allow_symlink)
-                r = stat(t, &a);
-        else
-                r = lstat(t, &a);
+        /* The parent can do name_to_handle_at() but the
+         * directory we are interested in can't? If so, it
+         * must be a mount point. */
+        if (nosupp)
+                return 1;
-        if (r < 0) {
-                if (errno == ENOENT)
-                        return 0;
+        /* If the file handle for the directory we are
+         * interested in and its parent are identical, we
+         * assume this is the root directory, which is a mount
+         * point. */
-                return -errno;
-        }
+        if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
+            h.handle.handle_type == h_parent.handle.handle_type &&
+            memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
+                return 1;
-        free(parent);
-        parent = NULL;
+        return mount_id != mount_id_parent;
-        r = path_get_parent(t, &parent);
+fallback_fdinfo:
+        r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
+        if (r == -EOPNOTSUPP)
+                goto fallback_fstat;
         if (r < 0)
                 return r;
-        r = stat(parent, &b);
+        r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
         if (r < 0)
+                return r;
+
+        if (mount_id != mount_id_parent)
+                return 1;
+
+        /* Hmm, so, the mount ids are the same. This leaves one
+         * special case though for the root file system. For that,
+         * let's see if the parent directory has the same inode as we
+         * are interested in. Hence, let's also do fstat() checks now,
+         * too, but avoid the st_dev comparisons, since they aren't
+         * that useful on unionfs mounts. */
+        check_st_dev = false;
+
+fallback_fstat:
+        /* yay for fstatat() taking a different set of flags than the other
+         * _at() above */
+        if (flags & AT_SYMLINK_FOLLOW)
+                flags &= ~AT_SYMLINK_FOLLOW;
+        else
+                flags |= AT_SYMLINK_NOFOLLOW;
+        if (fstatat(fd, filename, &a, flags) < 0)
+                return -errno;
+
+        if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
+                return -errno;
+
+        /* A directory with same device and inode as its parent? Must
+         * be the root directory */
+        if (a.st_dev == b.st_dev &&
+            a.st_ino == b.st_ino)
+                return 1;
+
+        return check_st_dev && (a.st_dev != b.st_dev);
+}
+
+/* flags can be AT_SYMLINK_FOLLOW or 0 */
+int path_is_mount_point(const char *t, int flags) {
+        _cleanup_close_ int fd = -1;
+        _cleanup_free_ char *canonical = NULL, *parent = NULL;
+
+        assert(t);
+
+        if (path_equal(t, "/"))
+                return 1;
+
+        /* we need to resolve symlinks manually, we can't just rely on
+         * fd_is_mount_point() to do that for us; if we have a structure like
+         * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
+         * look at needs to be /usr, not /. */
+        if (flags & AT_SYMLINK_FOLLOW) {
+                canonical = canonicalize_file_name(t);
+                if (!canonical)
+                        return -errno;
+
+                t = canonical;
+        }
+
+        parent = dirname_malloc(t);
+        if (!parent)
+                return -ENOMEM;
+
+        fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
+        if (fd < 0)
                 return -errno;
-        return a.st_dev != b.st_dev;
+        return fd_is_mount_point(fd, basename(t), flags);
 }
 int path_is_read_only_fs(const char *path) {
diff --git a/src/shared/path-util.h b/src/shared/path-util.h
index 71bb740e9..e16484087 100644
--- a/src/shared/path-util.h
+++ b/src/shared/path-util.h
@@ -53,7 +53,8 @@ char** path_strv_make_absolute_cwd(char **l);
 char** path_strv_resolve(char **l, const char *prefix);
 char** path_strv_resolve_uniq(char **l, const char *prefix);
-int path_is_mount_point(const char *path, bool allow_symlink);
+int fd_is_mount_point(int fd, const char *filename, int flags);
+int path_is_mount_point(const char *path, int flags);
 int path_is_read_only_fs(const char *path);
 int path_is_os_tree(const char *path);
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 6396fcb39..8870f178a 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <unistd.h>
+#include <sys/mount.h>
 #include "path-util.h"
 #include "util.h"
@@ -85,8 +86,8 @@ static void test_path(void) {
         test_parent("/aa///file...", "/aa///");
         test_parent("file.../", NULL);
-        assert_se(path_is_mount_point("/", true));
-        assert_se(path_is_mount_point("/", false));
+        assert_se(path_is_mount_point("/", AT_SYMLINK_FOLLOW));
+        assert_se(path_is_mount_point("/", 0));
         {
                 char p1[] = "aaa/bbb////ccc";
@@ -99,6 +100,66 @@ static void test_path(void) {
         }
 }
+static void test_path_is_mount_point(void) {
+        int fd, rt, rf, rlt, rlf;
+        char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
+        _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
+
+        assert_se(path_is_mount_point("/", AT_SYMLINK_FOLLOW) > 0);
+        assert_se(path_is_mount_point("/", 0) > 0);
+
+        assert_se(path_is_mount_point("/proc", AT_SYMLINK_FOLLOW) > 0);
+        assert_se(path_is_mount_point("/proc", 0) > 0);
+
+        assert_se(path_is_mount_point("/proc/1", AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point("/proc/1", 0) == 0);
+
+        assert_se(path_is_mount_point("/sys", AT_SYMLINK_FOLLOW) > 0);
+        assert_se(path_is_mount_point("/sys", 0) > 0);
+
+        /* file mountpoints */
+        assert_se(mkdtemp(tmp_dir) != NULL);
+        file1 = path_join(NULL, tmp_dir, "file1");
+        assert_se(file1);
+        file2 = path_join(NULL, tmp_dir, "file2");
+        assert_se(file2);
+        fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+        assert_se(fd > 0);
+        close(fd);
+        fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+        assert_se(fd > 0);
+        close(fd);
+        link1 = path_join(NULL, tmp_dir, "link1");
+        assert_se(link1);
+        assert_se(symlink("file1", link1) == 0);
+        link2 = path_join(NULL, tmp_dir, "link2");
+        assert_se(link1);
+        assert_se(symlink("file2", link2) == 0);
+
+        assert_se(path_is_mount_point(file1, AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point(file1, 0) == 0);
+        assert_se(path_is_mount_point(link1, AT_SYMLINK_FOLLOW) == 0);
+        assert_se(path_is_mount_point(link1, 0) == 0);
+
+        /* this test will only work as root */
+        if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
+                rf = path_is_mount_point(file2, 0);
+                rt = path_is_mount_point(file2, AT_SYMLINK_FOLLOW);
+                rlf = path_is_mount_point(link2, 0);
+                rlt = path_is_mount_point(link2, AT_SYMLINK_FOLLOW);
+
+                assert_se(umount(file2) == 0);
+
+                assert_se(rf == 1);
+                assert_se(rt == 1);
+                assert_se(rlf == 0);
+                assert_se(rlt == 1);
+        } else
+                printf("Skipping bind mount file test: %m\n");
+
+        assert_se(rm_rf(tmp_dir, false, true, false) == 0);
+}
+
 static void test_find_binary(const char *self, bool local) {
         char *p;
@@ -288,6 +349,7 @@ int main(int argc, char **argv) {
         test_make_relative();
         test_strv_resolve();
         test_path_startswith();
+        test_path_is_mount_point();
         return 0;
 }
SOURCES/0520-Revert-Revert-journald-allow-restarting-journald-wit.patch
New file
@@ -0,0 +1,546 @@
From e0a3cd2cb02c465c13dcc4e2c092c9e14883ad59 Mon Sep 17 00:00:00 2001
From: Lukas Nykryn <lnykryn@redhat.com>
Date: Wed, 3 Feb 2016 10:37:48 +0100
Subject: [PATCH] Revert "Revert "journald: allow restarting journald without
 losing stream connections""
This reverts commit 91cb89c1b79ef3c475d91319edb0c052cb9f2724.
Resolves: #1359939
---
 src/journal/journald-server.c |  26 ++-
 src/journal/journald-stream.c | 371 ++++++++++++++++++++++++++++++++++++------
 src/journal/journald-stream.h |   3 +-
 3 files changed, 340 insertions(+), 60 deletions(-)
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 07426b41e..c1358e1e9 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -1477,6 +1477,7 @@ static int server_open_hostname(Server *s) {
 }
 int server_init(Server *s) {
+        _cleanup_fdset_free_ FDSet *fds = NULL;
         int n, r, fd;
         assert(s);
@@ -1573,26 +1574,33 @@ int server_init(Server *s) {
                         s->audit_fd = fd;
                 } else {
-                        log_warning("Unknown socket passed as file descriptor %d, ignoring.", fd);
-                        /* Let's close the fd, better be safe than
-                           sorry. The fd might reference some resource
-                           that we really want to release if we don't
-                           make use of it. */
+                        if (!fds) {
+                                fds = fdset_new();
+                                if (!fds)
+                                        return log_oom();
+                        }
-                        safe_close(fd);
+                        r = fdset_put(fds, fd);
+                        if (r < 0)
+                                return log_oom();
                 }
         }
-        r = server_open_syslog_socket(s);
+        r = server_open_stdout_socket(s, fds);
         if (r < 0)
                 return r;
-        r = server_open_native_socket(s);
+        if (fdset_size(fds) > 0) {
+                log_warning("%u unknown file descriptors passed, closing.", fdset_size(fds));
+                fds = fdset_free(fds);
+        }
+
+        r = server_open_syslog_socket(s);
         if (r < 0)
                 return r;
-        r = server_open_stdout_socket(s);
+        r = server_open_native_socket(s);
         if (r < 0)
                 return r;
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index b8607144b..15c9110c0 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -28,8 +28,11 @@
 #endif
 #include "sd-event.h"
+#include "sd-daemon.h"
 #include "socket-util.h"
 #include "selinux-util.h"
+#include "mkdir.h"
+#include "fileio.h"
 #include "journald-server.h"
 #include "journald-stream.h"
 #include "journald-syslog.h"
@@ -66,14 +69,148 @@ struct StdoutStream {
         bool forward_to_kmsg:1;
         bool forward_to_console:1;
+        bool fdstore:1;
+
         char buffer[LINE_MAX+1];
         size_t length;
         sd_event_source *event_source;
+        char *state_file;
+
         LIST_FIELDS(StdoutStream, stdout_stream);
 };
+void stdout_stream_free(StdoutStream *s) {
+        if (!s)
+                return;
+
+        if (s->server) {
+                assert(s->server->n_stdout_streams > 0);
+                s->server->n_stdout_streams --;
+                LIST_REMOVE(stdout_stream, s->server->stdout_streams, s);
+        }
+
+        if (s->event_source) {
+                sd_event_source_set_enabled(s->event_source, SD_EVENT_OFF);
+                s->event_source = sd_event_source_unref(s->event_source);
+        }
+
+        safe_close(s->fd);
+        free(s->label);
+        free(s->identifier);
+        free(s->unit_id);
+        free(s->state_file);
+
+        free(s);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(StdoutStream*, stdout_stream_free);
+
+static void stdout_stream_destroy(StdoutStream *s) {
+        if (!s)
+                return;
+
+        if (s->state_file)
+                unlink(s->state_file);
+
+        stdout_stream_free(s);
+}
+
+static int stdout_stream_save(StdoutStream *s) {
+        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
+
+        assert(s);
+
+        if (s->state != STDOUT_STREAM_RUNNING)
+                return 0;
+
+        if (!s->state_file) {
+                struct stat st;
+
+                r = fstat(s->fd, &st);
+                if (r < 0)
+                        return log_warning_errno(errno, "Failed to stat connected stream: %m");
+
+                /* We use device and inode numbers as identifier for the stream */
+                if (asprintf(&s->state_file, "/run/systemd/journal/streams/%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0)
+                        return log_oom();
+        }
+
+        mkdir_p("/run/systemd/journal/streams", 0755);
+
+        r = fopen_temporary(s->state_file, &f, &temp_path);
+        if (r < 0)
+                goto finish;
+
+        fprintf(f,
+                "# This is private data. Do not parse\n"
+                "PRIORITY=%i\n"
+                "LEVEL_PREFIX=%i\n"
+                "FORWARD_TO_SYSLOG=%i\n"
+                "FORWARD_TO_KMSG=%i\n"
+                "FORWARD_TO_CONSOLE=%i\n",
+                s->priority,
+                s->level_prefix,
+                s->forward_to_syslog,
+                s->forward_to_kmsg,
+                s->forward_to_console);
+
+        if (!isempty(s->identifier)) {
+                _cleanup_free_ char *escaped;
+
+                escaped = cescape(s->identifier);
+                if (!escaped) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                fprintf(f, "IDENTIFIER=%s\n", escaped);
+        }
+
+        if (!isempty(s->unit_id)) {
+                _cleanup_free_ char *escaped;
+
+                escaped = cescape(s->unit_id);
+                if (!escaped) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                fprintf(f, "UNIT=%s\n", escaped);
+        }
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                goto finish;
+
+        if (rename(temp_path, s->state_file) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        free(temp_path);
+        temp_path = NULL;
+
+        /* Store the connection fd in PID 1, so that we get it passed
+         * in again on next start */
+        if (!s->fdstore) {
+                sd_pid_notify_with_fds(0, false, "FDSTORE=1", &s->fd, 1);
+                s->fdstore = true;
+        }
+
+finish:
+        if (temp_path)
+                unlink(temp_path);
+
+        if (r < 0)
+                log_error_errno(r, "Failed to save stream data %s: %m", s->state_file);
+
+        return r;
+}
+
 static int stdout_stream_log(StdoutStream *s, const char *p) {
         struct iovec iovec[N_IOVEC_META_FIELDS + 5];
         int priority;
@@ -219,6 +356,9 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
                 s->forward_to_console = !!r;
                 s->state = STDOUT_STREAM_RUNNING;
+
+                /* Try to save the stream, so that journald can be restarted and we can recover */
+                (void) stdout_stream_save(s);
                 return 0;
         case STDOUT_STREAM_RUNNING:
@@ -313,36 +453,62 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
         return 1;
 terminate:
-        stdout_stream_free(s);
+        stdout_stream_destroy(s);
         return 0;
 }
-void stdout_stream_free(StdoutStream *s) {
+static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
+        _cleanup_(stdout_stream_freep) StdoutStream *stream = NULL;
+        int r;
+
         assert(s);
+        assert(fd >= 0);
-        if (s->server) {
-                assert(s->server->n_stdout_streams > 0);
-                s->server->n_stdout_streams --;
-                LIST_REMOVE(stdout_stream, s->server->stdout_streams, s);
-        }
+        stream = new0(StdoutStream, 1);
+        if (!stream)
+                return log_oom();
-        if (s->event_source) {
-                sd_event_source_set_enabled(s->event_source, SD_EVENT_OFF);
-                s->event_source = sd_event_source_unref(s->event_source);
+        stream->fd = -1;
+        stream->priority = LOG_INFO;
+
+        r = getpeercred(fd, &stream->ucred);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine peer credentials: %m");
+
+        if (mac_selinux_use()) {
+                r = getpeersec(fd, &stream->label);
+                if (r < 0 && r != -EOPNOTSUPP)
+                        (void) log_warning_errno(r, "Failed to determine peer security context: %m");
         }
-        safe_close(s->fd);
+        (void) shutdown(fd, SHUT_WR);
-        free(s->label);
-        free(s->identifier);
-        free(s->unit_id);
-        free(s);
+        r = sd_event_add_io(s->event, &stream->event_source, fd, EPOLLIN, stdout_stream_process, stream);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add stream to event loop: %m");
+
+        r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5);
+        if (r < 0)
+                return log_error_errno(r, "Failed to adjust stdout event source priority: %m");
+
+        stream->fd = fd;
+
+        stream->server = s;
+        LIST_PREPEND(stdout_stream, s->stdout_streams, stream);
+        s->n_stdout_streams ++;
+
+        if (ret)
+                *ret = stream;
+
+        stream = NULL;
+
+        return 0;
 }
 static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revents, void *userdata) {
+        _cleanup_close_ int fd = -1;
         Server *s = userdata;
-        StdoutStream *stream;
-        int fd, r;
+        int r;
         assert(s);
@@ -362,61 +528,163 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
         if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
                 log_warning("Too many stdout streams, refusing connection.");
-                safe_close(fd);
                 return 0;
         }
-        stream = new0(StdoutStream, 1);
-        if (!stream) {
-                safe_close(fd);
-                return log_oom();
+        r = stdout_stream_install(s, fd, NULL);
+        if (r < 0)
+                return r;
+
+        fd = -1;
+        return 0;
+}
+
+static int stdout_stream_load(StdoutStream *stream, const char *fname) {
+        _cleanup_free_ char
+                *priority = NULL,
+                *level_prefix = NULL,
+                *forward_to_syslog = NULL,
+                *forward_to_kmsg = NULL,
+                *forward_to_console = NULL;
+        int r;
+
+        assert(stream);
+        assert(fname);
+
+        if (!stream->state_file) {
+                stream->state_file = strappend("/run/systemd/journal/streams/", fname);
+                if (!stream->state_file)
+                        return log_oom();
         }
-        stream->fd = fd;
+        r = parse_env_file(stream->state_file, NEWLINE,
+                           "PRIORITY", &priority,
+                           "LEVEL_PREFIX", &level_prefix,
+                           "FORWARD_TO_SYSLOG", &forward_to_syslog,
+                           "FORWARD_TO_KMSG", &forward_to_kmsg,
+                           "FORWARD_TO_CONSOLE", &forward_to_console,
+                           "IDENTIFIER", &stream->identifier,
+                           "UNIT", &stream->unit_id,
+                           NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read: %s", stream->state_file);
-        r = getpeercred(fd, &stream->ucred);
-        if (r < 0) {
-                log_error_errno(errno, "Failed to determine peer credentials: %m");
-                goto fail;
+        if (priority) {
+                int p;
+
+                p = log_level_from_string(priority);
+                if (p >= 0)
+                        stream->priority = p;
         }
-#ifdef HAVE_SELINUX
-        if (mac_selinux_use()) {
-                r = getpeersec(fd, &stream->label);
-                if (r < 0 && r != -EOPNOTSUPP)
-                        (void) log_warning_errno(r, "Failed to determine peer security context: %m");
+        if (level_prefix) {
+                r = parse_boolean(level_prefix);
+                if (r >= 0)
+                        stream->level_prefix = r;
         }
-#endif
-        if (shutdown(fd, SHUT_WR) < 0) {
-                log_error_errno(errno, "Failed to shutdown writing side of socket: %m");
-                goto fail;
+        if (forward_to_syslog) {
+                r = parse_boolean(forward_to_syslog);
+                if (r >= 0)
+                        stream->forward_to_syslog = r;
         }
-        r = sd_event_add_io(s->event, &stream->event_source, fd, EPOLLIN, stdout_stream_process, stream);
-        if (r < 0) {
-                log_error_errno(r, "Failed to add stream to event loop: %m");
-                goto fail;
+        if (forward_to_kmsg) {
+                r = parse_boolean(forward_to_kmsg);
+                if (r >= 0)
+                        stream->forward_to_kmsg = r;
         }
-        r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5);
-        if (r < 0) {
-                log_error_errno(r, "Failed to adjust stdout event source priority: %m");
-                goto fail;
+        if (forward_to_console) {
+                r = parse_boolean(forward_to_console);
+                if (r >= 0)
+                        stream->forward_to_console = r;
         }
-        stream->server = s;
-        LIST_PREPEND(stdout_stream, s->stdout_streams, stream);
-        s->n_stdout_streams ++;
+        return 0;
+}
+
+static int stdout_stream_restore(Server *s, const char *fname, int fd) {
+        StdoutStream *stream;
+        int r;
+
+        assert(s);
+        assert(fname);
+        assert(fd >= 0);
+
+        if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
+                log_warning("Too many stdout streams, refusing restoring of stream.");
+                return -ENOBUFS;
+        }
+
+        r = stdout_stream_install(s, fd, &stream);
+        if (r < 0)
+                return r;
+
+        stream->state = STDOUT_STREAM_RUNNING;
+        stream->fdstore = true;
+
+        /* Ignore all parsing errors */
+        (void) stdout_stream_load(stream, fname);
         return 0;
+}
+
+static int server_restore_streams(Server *s, FDSet *fds) {
+        _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
+        int r;
+
+        d = opendir("/run/systemd/journal/streams");
+        if (!d) {
+                if (errno == ENOENT)
+                        return 0;
+
+                return log_warning_errno(errno, "Failed to enumerate /run/systemd/journal/streams: %m");
+        }
+
+        FOREACH_DIRENT(de, d, goto fail) {
+                unsigned long st_dev, st_ino;
+                bool found = false;
+                Iterator i;
+                int fd;
+
+                if (sscanf(de->d_name, "%lu:%lu", &st_dev, &st_ino) != 2)
+                        continue;
+
+                FDSET_FOREACH(fd, fds, i) {
+                        struct stat st;
+
+                        if (fstat(fd, &st) < 0)
+                                return log_error_errno(errno, "Failed to stat %s: %m", de->d_name);
+
+                        if (S_ISSOCK(st.st_mode) && st.st_dev == st_dev && st.st_ino == st_ino) {
+                                found = true;
+                                break;
+                        }
+                }
+
+                if (!found) {
+                        /* No file descriptor? Then let's delete the state file */
+                        log_debug("Cannot restore stream file %s", de->d_name);
+                        unlinkat(dirfd(d), de->d_name, 0);
+                        continue;
+                }
+
+                fdset_remove(fds, fd);
+
+                r = stdout_stream_restore(s, de->d_name, fd);
+                if (r < 0)
+                        safe_close(fd);
+        }
-fail:
-        stdout_stream_free(stream);
         return 0;
+
+fail:
+        return log_error_errno(errno, "Failed to read streams directory: %m");
 }
-int server_open_stdout_socket(Server *s) {
+int server_open_stdout_socket(Server *s, FDSet *fds) {
         int r;
         assert(s);
@@ -452,5 +720,8 @@ int server_open_stdout_socket(Server *s) {
         if (r < 0)
                 return log_error_errno(r, "Failed to adjust priority of stdout server event source: %m");
+        /* Try to restore streams, but don't bother if this fails */
+        (void) server_restore_streams(s, fds);
+
         return 0;
 }
diff --git a/src/journal/journald-stream.h b/src/journal/journald-stream.h
index 8cad01296..94bf955d7 100644
--- a/src/journal/journald-stream.h
+++ b/src/journal/journald-stream.h
@@ -21,8 +21,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
+#include "fdset.h"
 #include "journald-server.h"
-int server_open_stdout_socket(Server *s);
+int server_open_stdout_socket(Server *s, FDSet *fds);
 void stdout_stream_free(StdoutStream *s);
SOURCES/0521-journald-make-sure-we-retain-all-stream-fds-across-r.patch
New file
@@ -0,0 +1,34 @@
From ad2d5449dc86ac37460ac9c16e0d5d088befbd0b Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekletar@users.noreply.github.com>
Date: Mon, 17 Jul 2017 10:04:37 +0200
Subject: [PATCH] journald: make sure we retain all stream fds across restarts
 (#6348)
Currently we set 4096 as maximum for number of stream connections that
we accept. However maximum number of file descriptors that systemd is
willing to accept from us is just 1024. This means we can't retain all
stream connections that we accepted. Hence bump the limit of fds in a
unit file so that systemd holds open all stream fds while we are
restarted.
New limit is set to 4224 (4096 + 128).
(cherry picked from commit 3c978aca69e0e43d4dd453437ec9c498ea788795)
Related: #1359939
---
 units/systemd-journald.service.in | 1 +
 1 file changed, 1 insertion(+)
diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in
index c85c34932..0d1ea61fe 100644
--- a/units/systemd-journald.service.in
+++ b/units/systemd-journald.service.in
@@ -20,6 +20,7 @@ ExecStart=@rootlibexecdir@/systemd-journald
 Restart=always
 RestartSec=0
 StandardOutput=null
+FileDescriptorStoreMax=4224
 CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE
 WatchdogSec=3min
SOURCES/0522-Allow-systemd-tmpfiles-to-set-the-file-directory-att.patch
New file
@@ -0,0 +1,218 @@
From 037b80886a6c3acad294aee139d28d1f574d82cc Mon Sep 17 00:00:00 2001
From: Goffredo Baroncelli <kreijack@inwind.it>
Date: Mon, 16 Mar 2015 20:33:50 +0100
Subject: [PATCH] Allow systemd-tmpfiles to set the file/directory attributes
Allow systemd-tmpfiles to set the file/directory attributes, like
chattr(1) does. Two more commands are added: 'H' and 'h' to set the
attributes, recursively and not.
(cherry picked from commit 22c3a6cadbc99ad623501db9a928f52f6f84c0c3)
Related: #1299714
---
 src/tmpfiles/tmpfiles.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 150 insertions(+)
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index ed35b8cf0..c8c56c722 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -40,6 +40,7 @@
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/xattr.h>
+#include <linux/fs.h>
 #include "log.h"
 #include "util.h"
@@ -91,6 +92,8 @@ typedef enum ItemType {
         RELABEL_PATH = 'z',
         RECURSIVE_RELABEL_PATH = 'Z',
         ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
+        SET_ATTRIB = 'h',
+        RECURSIVE_SET_ATTRIB = 'H',
 } ItemType;
 typedef struct Item {
@@ -109,12 +112,15 @@ typedef struct Item {
         usec_t age;
         dev_t major_minor;
+        unsigned long attrib_value;
+        unsigned long attrib_mask;
         bool uid_set:1;
         bool gid_set:1;
         bool mode_set:1;
         bool age_set:1;
         bool mask_perms:1;
+        bool attrib_set:1;
         bool keep_first_level:1;
@@ -817,6 +823,127 @@ static int path_set_acls(Item *item, const char *path) {
         return r;
 }
+#define ALL_ATTRIBS          \
+        FS_NOATIME_FL      | \
+        FS_SYNC_FL         | \
+        FS_DIRSYNC_FL      | \
+        FS_APPEND_FL       | \
+        FS_COMPR_FL        | \
+        FS_NODUMP_FL       | \
+        FS_EXTENT_FL       | \
+        FS_IMMUTABLE_FL    | \
+        FS_JOURNAL_DATA_FL | \
+        FS_SECRM_FL        | \
+        FS_UNRM_FL         | \
+        FS_NOTAIL_FL       | \
+        FS_TOPDIR_FL       | \
+        FS_NOCOW_FL
+
+static int get_attrib_from_arg(Item *item) {
+        static const unsigned attributes[] = {
+                [(uint8_t)'A'] = FS_NOATIME_FL,      /* do not update atime */
+                [(uint8_t)'S'] = FS_SYNC_FL,         /* Synchronous updates */
+                [(uint8_t)'D'] = FS_DIRSYNC_FL,      /* dirsync behaviour (directories only) */
+                [(uint8_t)'a'] = FS_APPEND_FL,       /* writes to file may only append */
+                [(uint8_t)'c'] = FS_COMPR_FL,        /* Compress file */
+                [(uint8_t)'d'] = FS_NODUMP_FL,       /* do not dump file */
+                [(uint8_t)'e'] = FS_EXTENT_FL,       /* Top of directory hierarchies*/
+                [(uint8_t)'i'] = FS_IMMUTABLE_FL,    /* Immutable file */
+                [(uint8_t)'j'] = FS_JOURNAL_DATA_FL, /* Reserved for ext3 */
+                [(uint8_t)'s'] = FS_SECRM_FL,        /* Secure deletion */
+                [(uint8_t)'u'] = FS_UNRM_FL,         /* Undelete */
+                [(uint8_t)'t'] = FS_NOTAIL_FL,       /* file tail should not be merged */
+                [(uint8_t)'T'] = FS_TOPDIR_FL,       /* Top of directory hierarchies*/
+                [(uint8_t)'C'] = FS_NOCOW_FL,        /* Do not cow file */
+        };
+        char *p = item->argument;
+        enum {
+                MODE_ADD,
+                MODE_DEL,
+                MODE_SET
+        } mode = MODE_ADD;
+        unsigned long value = 0, mask = 0;
+
+        if (!p) {
+                log_error("\"%s\": setting ATTR need an argument", item->path);
+                return -EINVAL;
+        }
+
+        if (*p == '+') {
+                mode = MODE_ADD;
+                p++;
+        } else if (*p == '-') {
+                mode = MODE_DEL;
+                p++;
+        } else  if (*p == '=') {
+                mode = MODE_SET;
+                p++;
+        }
+
+        if (!*p && mode != MODE_SET) {
+                log_error("\"%s\": setting ATTR: argument is empty", item->path);
+                return -EINVAL;
+        }
+        for (; *p ; p++) {
+                if ((uint8_t)*p > ELEMENTSOF(attributes) || attributes[(uint8_t)*p] == 0) {
+                        log_error("\"%s\": setting ATTR: unknown attr '%c'", item->path, *p);
+                        return -EINVAL;
+                }
+                if (mode == MODE_ADD || mode == MODE_SET)
+                        value |= attributes[(uint8_t)*p];
+                else
+                        value &= ~attributes[(uint8_t)*p];
+                mask |= attributes[(uint8_t)*p];
+        }
+
+        if (mode == MODE_SET)
+                mask |= ALL_ATTRIBS;
+
+        assert(mask);
+
+        item->attrib_mask = mask;
+        item->attrib_value = value;
+        item->attrib_set = true;
+
+        return 0;
+
+}
+
+static int path_set_attrib(Item *item, const char *path) {
+        _cleanup_close_ int fd = -1;
+        int r;
+        unsigned f;
+        struct stat st;
+
+        /* do nothing */
+        if (item->attrib_mask == 0 || !item->attrib_set)
+                return 0;
+        /*
+         * It is OK to ignore an lstat() error, because the error
+         * will be catch by the open() below anyway
+         */
+        if (lstat(path, &st) == 0 &&
+            !S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
+                return 0;
+        }
+
+        fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
+
+        if (fd < 0)
+                return log_error_errno(errno, "Cannot open \"%s\": %m", path);
+
+        f = item->attrib_value & item->attrib_mask;
+        if (!S_ISDIR(st.st_mode))
+                f &= ~FS_DIRSYNC_FL;
+        r = change_attr_fd(fd, f, item->attrib_mask);
+        if (r < 0)
+                return log_error_errno(errno,
+                        "Cannot set attrib for \"%s\", value=0x%08lx, mask=0x%08lx: %m",
+                        path, item->attrib_value, item->attrib_mask);
+
+        return 0;
+}
+
 static int write_one_file(Item *i, const char *path) {
         _cleanup_close_ int fd = -1;
         int flags, r = 0;
@@ -1266,6 +1393,18 @@ static int create_item(Item *i) {
                 if (r < 0)
                         return r;
                 break;
+
+        case SET_ATTRIB:
+                r = glob_item(i, path_set_attrib, false);
+                if (r < 0)
+                        return r;
+                break;
+
+        case RECURSIVE_SET_ATTRIB:
+                r = glob_item(i, path_set_attrib, true);
+                if (r < 0)
+                        return r;
+                break;
         }
         return 0;
@@ -1712,6 +1851,17 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                         return r;
                 break;
+        case SET_ATTRIB:
+        case RECURSIVE_SET_ATTRIB:
+                if (!i.argument) {
+                        log_error("[%s:%u] Set attrib requires argument.", fname, line);
+                        return -EBADMSG;
+                }
+                r = get_attrib_from_arg(&i);
+                if (r < 0)
+                        return r;
+                break;
+
         default:
                 log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
                 return -EBADMSG;
SOURCES/0523-tmpfiles-rework-file-attribute-code.patch
New file
@@ -0,0 +1,326 @@
From 3a68810cd6ac23f7107491ab6e1fbd565ed52bf0 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 8 Apr 2015 22:35:52 +0200
Subject: [PATCH] tmpfiles: rework file attribute code
- Stick to one type for the flags field: unsigned. This appears to be
  what the kernel uses, and there's no point in using something else.
- compress the flags array by avoiding sparse entries
- extend some error messages to not use abbreviated words
- avoid TTOCTTOU issues by invoking fstat() after open() when applying
  file flags
- add explanation why we need to check the file type with fstat().
- don't needlessly abbreviate "attribute" as "attrib", in particually as
  "chattr" abbreviates it as "attr" rather than "attrib".
(cherry picked from commit 88ec4dfa289cd97496dbb9670365a3d4be13d41c)
Conflicts:
    src/tmpfiles/tmpfiles.c
Related: #1299714
---
 src/tmpfiles/tmpfiles.c | 207 ++++++++++++++++++++++++++----------------------
 1 file changed, 114 insertions(+), 93 deletions(-)
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index c8c56c722..800e620bc 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -92,8 +92,8 @@ typedef enum ItemType {
         RELABEL_PATH = 'z',
         RECURSIVE_RELABEL_PATH = 'Z',
         ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
-        SET_ATTRIB = 'h',
-        RECURSIVE_SET_ATTRIB = 'H',
+        SET_ATTRIBUTE = 'h',
+        RECURSIVE_SET_ATTRIBUTE = 'H',
 } ItemType;
 typedef struct Item {
@@ -112,15 +112,15 @@ typedef struct Item {
         usec_t age;
         dev_t major_minor;
-        unsigned long attrib_value;
-        unsigned long attrib_mask;
+        unsigned attribute_value;
+        unsigned attribute_mask;
         bool uid_set:1;
         bool gid_set:1;
         bool mode_set:1;
         bool age_set:1;
         bool mask_perms:1;
-        bool attrib_set:1;
+        bool attribute_set:1;
         bool keep_first_level:1;
@@ -823,123 +823,144 @@ static int path_set_acls(Item *item, const char *path) {
         return r;
 }
-#define ALL_ATTRIBS          \
-        FS_NOATIME_FL      | \
-        FS_SYNC_FL         | \
-        FS_DIRSYNC_FL      | \
-        FS_APPEND_FL       | \
-        FS_COMPR_FL        | \
-        FS_NODUMP_FL       | \
-        FS_EXTENT_FL       | \
-        FS_IMMUTABLE_FL    | \
-        FS_JOURNAL_DATA_FL | \
-        FS_SECRM_FL        | \
-        FS_UNRM_FL         | \
-        FS_NOTAIL_FL       | \
-        FS_TOPDIR_FL       | \
-        FS_NOCOW_FL
-
-static int get_attrib_from_arg(Item *item) {
-        static const unsigned attributes[] = {
-                [(uint8_t)'A'] = FS_NOATIME_FL,      /* do not update atime */
-                [(uint8_t)'S'] = FS_SYNC_FL,         /* Synchronous updates */
-                [(uint8_t)'D'] = FS_DIRSYNC_FL,      /* dirsync behaviour (directories only) */
-                [(uint8_t)'a'] = FS_APPEND_FL,       /* writes to file may only append */
-                [(uint8_t)'c'] = FS_COMPR_FL,        /* Compress file */
-                [(uint8_t)'d'] = FS_NODUMP_FL,       /* do not dump file */
-                [(uint8_t)'e'] = FS_EXTENT_FL,       /* Top of directory hierarchies*/
-                [(uint8_t)'i'] = FS_IMMUTABLE_FL,    /* Immutable file */
-                [(uint8_t)'j'] = FS_JOURNAL_DATA_FL, /* Reserved for ext3 */
-                [(uint8_t)'s'] = FS_SECRM_FL,        /* Secure deletion */
-                [(uint8_t)'u'] = FS_UNRM_FL,         /* Undelete */
-                [(uint8_t)'t'] = FS_NOTAIL_FL,       /* file tail should not be merged */
-                [(uint8_t)'T'] = FS_TOPDIR_FL,       /* Top of directory hierarchies*/
-                [(uint8_t)'C'] = FS_NOCOW_FL,        /* Do not cow file */
+#define ATTRIBUTES_ALL                          \
+        (FS_NOATIME_FL      |                   \
+         FS_SYNC_FL         |                   \
+         FS_DIRSYNC_FL      |                   \
+         FS_APPEND_FL       |                   \
+         FS_COMPR_FL        |                   \
+         FS_NODUMP_FL       |                   \
+         FS_EXTENT_FL       |                   \
+         FS_IMMUTABLE_FL    |                   \
+         FS_JOURNAL_DATA_FL |                   \
+         FS_SECRM_FL        |                   \
+         FS_UNRM_FL         |                   \
+         FS_NOTAIL_FL       |                   \
+         FS_TOPDIR_FL       |                   \
+         FS_NOCOW_FL)
+
+static int get_attribute_from_arg(Item *item) {
+
+        static const struct {
+                char character;
+                unsigned value;
+        } attributes[] = {
+                { 'A', FS_NOATIME_FL },      /* do not update atime */
+                { 'S', FS_SYNC_FL },         /* Synchronous updates */
+                { 'D', FS_DIRSYNC_FL },      /* dirsync behaviour (directories only) */
+                { 'a', FS_APPEND_FL },       /* writes to file may only append */
+                { 'c', FS_COMPR_FL },        /* Compress file */
+                { 'd', FS_NODUMP_FL },       /* do not dump file */
+                { 'e', FS_EXTENT_FL },       /* Top of directory hierarchies*/
+                { 'i', FS_IMMUTABLE_FL },    /* Immutable file */
+                { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */
+                { 's', FS_SECRM_FL },        /* Secure deletion */
+                { 'u', FS_UNRM_FL },         /* Undelete */
+                { 't', FS_NOTAIL_FL },       /* file tail should not be merged */
+                { 'T', FS_TOPDIR_FL },       /* Top of directory hierarchies*/
+                { 'C', FS_NOCOW_FL },        /* Do not cow file */
         };
-        char *p = item->argument;
+
         enum {
                 MODE_ADD,
                 MODE_DEL,
                 MODE_SET
         } mode = MODE_ADD;
-        unsigned long value = 0, mask = 0;
-        if (!p) {
-                log_error("\"%s\": setting ATTR need an argument", item->path);
-                return -EINVAL;
-        }
+        unsigned value = 0, mask = 0;
+        const char *p;
-        if (*p == '+') {
-                mode = MODE_ADD;
-                p++;
-        } else if (*p == '-') {
-                mode = MODE_DEL;
-                p++;
-        } else  if (*p == '=') {
-                mode = MODE_SET;
-                p++;
+        assert(item);
+
+        p = item->argument;
+        if (p) {
+                if (*p == '+') {
+                        mode = MODE_ADD;
+                        p++;
+                } else if (*p == '-') {
+                        mode = MODE_DEL;
+                        p++;
+                } else  if (*p == '=') {
+                        mode = MODE_SET;
+                        p++;
+                }
         }
-        if (!*p && mode != MODE_SET) {
-                log_error("\"%s\": setting ATTR: argument is empty", item->path);
+        if (isempty(p) && mode != MODE_SET) {
+                log_error("Setting file attribute on '%s' needs an attribute specification.", item->path);
                 return -EINVAL;
         }
-        for (; *p ; p++) {
-                if ((uint8_t)*p > ELEMENTSOF(attributes) || attributes[(uint8_t)*p] == 0) {
-                        log_error("\"%s\": setting ATTR: unknown attr '%c'", item->path, *p);
+
+        for (; p && *p ; p++) {
+                unsigned i, v;
+
+                for (i = 0; i < ELEMENTSOF(attributes); i++)
+                        if (*p == attributes[i].character)
+                                break;
+
+                if (i >= ELEMENTSOF(attributes)) {
+                        log_error("Unknown file attribute '%c' on '%s'.", *p, item->path);
                         return -EINVAL;
                 }
+
+                v = attributes[i].value;
+
                 if (mode == MODE_ADD || mode == MODE_SET)
-                        value |= attributes[(uint8_t)*p];
+                        value |= v;
                 else
-                        value &= ~attributes[(uint8_t)*p];
-                mask |= attributes[(uint8_t)*p];
+                        value &= ~v;
+
+                mask |= v;
         }
         if (mode == MODE_SET)
-                mask |= ALL_ATTRIBS;
+                mask |= ATTRIBUTES_ALL;
-        assert(mask);
+        assert(mask != 0);
-        item->attrib_mask = mask;
-        item->attrib_value = value;
-        item->attrib_set = true;
+        item->attribute_mask = mask;
+        item->attribute_value = value;
+        item->attribute_set = true;
         return 0;
-
 }
-static int path_set_attrib(Item *item, const char *path) {
+static int path_set_attribute(Item *item, const char *path) {
         _cleanup_close_ int fd = -1;
-        int r;
-        unsigned f;
         struct stat st;
+        unsigned f;
+        int r;
-        /* do nothing */
-        if (item->attrib_mask == 0 || !item->attrib_set)
-                return 0;
-        /*
-         * It is OK to ignore an lstat() error, because the error
-         * will be catch by the open() below anyway
-         */
-        if (lstat(path, &st) == 0 &&
-            !S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
+        if (!item->attribute_set || item->attribute_mask == 0)
                 return 0;
-        }
         fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
         if (fd < 0)
-                return log_error_errno(errno, "Cannot open \"%s\": %m", path);
+                return log_error_errno(errno, "Cannot open '%s': %m", path);
-        f = item->attrib_value & item->attrib_mask;
+        if (fstat(fd, &st) < 0)
+                return log_error_errno(errno, "Cannot stat '%s': %m", path);
+
+        /* Issuing the file attribute ioctls on device nodes is not
+         * safe, as that will be delivered to the drivers, not the
+         * file system containing the device node. */
+        if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
+                log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path);
+                return -EINVAL;
+        }
+
+        f = item->attribute_value & item->attribute_mask;
+
+        /* Mask away directory-specific flags */
         if (!S_ISDIR(st.st_mode))
                 f &= ~FS_DIRSYNC_FL;
-        r = change_attr_fd(fd, f, item->attrib_mask);
+
+        r = chattr_fd(fd, f, item->attribute_mask);
         if (r < 0)
-                return log_error_errno(errno,
-                        "Cannot set attrib for \"%s\", value=0x%08lx, mask=0x%08lx: %m",
-                        path, item->attrib_value, item->attrib_mask);
+                return log_error_errno(r,
+                        "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
+                        path, item->attribute_value, item->attribute_mask);
         return 0;
 }
@@ -1394,14 +1415,14 @@ static int create_item(Item *i) {
                         return r;
                 break;
-        case SET_ATTRIB:
-                r = glob_item(i, path_set_attrib, false);
+        case SET_ATTRIBUTE:
+                r = glob_item(i, path_set_attribute, false);
                 if (r < 0)
                         return r;
                 break;
-        case RECURSIVE_SET_ATTRIB:
-                r = glob_item(i, path_set_attrib, true);
+        case RECURSIVE_SET_ATTRIBUTE:
+                r = glob_item(i, path_set_attribute, true);
                 if (r < 0)
                         return r;
                 break;
@@ -1851,13 +1872,13 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                         return r;
                 break;
-        case SET_ATTRIB:
-        case RECURSIVE_SET_ATTRIB:
+        case SET_ATTRIBUTE:
+        case RECURSIVE_SET_ATTRIBUTE:
                 if (!i.argument) {
-                        log_error("[%s:%u] Set attrib requires argument.", fname, line);
+                        log_error("[%s:%u] Set file attribute requires argument.", fname, line);
                         return -EBADMSG;
                 }
-                r = get_attrib_from_arg(&i);
+                r = get_attribute_from_arg(&i);
                 if (r < 0)
                         return r;
                 break;
SOURCES/0524-tmpfiles-warn-if-we-get-an-argument-on-lines-that-do.patch
New file
@@ -0,0 +1,40 @@
From 95ee3c8f1ef9408543c962af5f21e01ccef544e1 Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Thu, 7 Sep 2017 15:46:24 +0200
Subject: [PATCH] tmpfiles: warn if we get an argument on lines that don't take
 any
(cherry picked from commit c82500c6fb37a25bc3c4b1e0be11a90a395619d9)
Related: #1299714
---
 src/tmpfiles/tmpfiles.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 800e620bc..70e0cc2fa 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -1788,8 +1788,6 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         switch (i.type) {
-        case CREATE_FILE:
-        case TRUNCATE_FILE:
         case CREATE_DIRECTORY:
         case CREATE_SUBVOLUME:
         case EMPTY_DIRECTORY:
@@ -1802,6 +1800,13 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         case ADJUST_MODE:
         case RELABEL_PATH:
         case RECURSIVE_RELABEL_PATH:
+                if (i.argument)
+                        log_warning("[%s:%u] %c lines don't take argument field, ignoring.", fname, line, i.type);
+
+                break;
+
+        case CREATE_FILE:
+        case TRUNCATE_FILE:
                 break;
         case CREATE_SYMLINK:
SOURCES/0525-tmpfiles-substitute-specifiers-in-arguments-for-writ.patch
New file
@@ -0,0 +1,207 @@
From d23386f61d810dab77e9d9d9130adbd826ea823f Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Thu, 7 Sep 2017 15:49:08 +0200
Subject: [PATCH] tmpfiles: substitute % specifiers in arguments for writing
 files and xattrs
(cherry-picked from commit bd550f78eb261c757cbff85acdb55563c56521f2)
Related: #1299714
---
 src/tmpfiles/tmpfiles.c | 79 ++++++++++++++++++++++++++-----------------------
 1 file changed, 42 insertions(+), 37 deletions(-)
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 70e0cc2fa..ddb274fce 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -151,6 +151,14 @@ static const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles");
 static Hashmap *items = NULL, *globs = NULL;
 static Set *unix_sockets = NULL;
+static const Specifier specifier_table[] = {
+        { 'm', specifier_machine_id, NULL },
+        { 'b', specifier_boot_id, NULL },
+        { 'H', specifier_host_name, NULL },
+        { 'v', specifier_kernel_release, NULL },
+        {}
+};
+
 static bool needs_glob(ItemType t) {
         return IN_SET(t,
                       WRITE_FILE,
@@ -657,8 +665,7 @@ static int path_set_perms(Item *i, const char *path) {
         return label_fix(path, false, false);
 }
-static int get_xattrs_from_arg(Item *i) {
-        char *xattr;
+static int parse_xattrs_from_arg(Item *i) {
         const char *p;
         int r;
@@ -667,35 +674,37 @@ static int get_xattrs_from_arg(Item *i) {
         p = i->argument;
-        while ((r = unquote_first_word(&p, &xattr, false)) > 0) {
-                _cleanup_free_ char *tmp = NULL, *name = NULL,
-                        *value = NULL, *value2 = NULL, *_xattr = xattr;
+        for (;;) {
+                _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL;
+
+                r = unquote_first_word(&p, &xattr, false);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
+                if (r <= 0)
+                        break;
+
+                r = specifier_printf(xattr, specifier_table, NULL, &xattr_replaced);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to replace specifiers in extended attribute '%s': %m", xattr);
-                r = split_pair(xattr, "=", &name, &value);
+                r = split_pair(xattr_replaced, "=", &name, &value);
                 if (r < 0) {
                         log_warning("Illegal xattr found: \"%s\" - ignoring.", xattr);
                         continue;
                 }
-                if (strempty(name) || strempty(value)) {
-                        log_warning("Malformed xattr found: \"%s\" - ignoring.", xattr);
+                if (isempty(name) || isempty(value)) {
+                        log_warning("Malformed extended attribute found, ignoring: %s", xattr);
                         continue;
                 }
-                tmp = unquote(value, "\"");
-                if (!tmp)
-                        return log_oom();
-
-                value2 = cunescape(tmp);
-                if (!value2)
+                if (strv_push_pair(&i->xattrs, name, value) < 0)
                         return log_oom();
-                if (strv_push_pair(&i->xattrs, name, value2) < 0)
-                        return log_oom();
-                name = value2 = NULL;
+                name = value = NULL;
         }
-        return r;
+        return 0;
 }
 static int path_set_xattrs(Item *i, const char *path) {
@@ -708,17 +717,16 @@ static int path_set_xattrs(Item *i, const char *path) {
                 int n;
                 n = strlen(*value);
-                log_debug("\"%s\": setting xattr \"%s=%s\"", path, *name, *value);
+                log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
                 if (lsetxattr(path, *name, *value, n, 0) < 0) {
-                        log_error("Setting extended attribute %s=%s on %s failed: %m",
-                                  *name, *value, path);
+                        log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path);
                         return -errno;
                 }
         }
         return 0;
 }
-static int get_acls_from_arg(Item *item) {
+static int parse_acls_from_arg(Item *item) {
 #ifdef HAVE_ACL
         int r;
@@ -726,6 +734,7 @@ static int get_acls_from_arg(Item *item) {
         /* If force (= modify) is set, we will not modify the acl
          * afterwards, so the mask can be added now if necessary. */
+
         r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
         if (r < 0)
                 log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring",
@@ -839,7 +848,7 @@ static int path_set_acls(Item *item, const char *path) {
          FS_TOPDIR_FL       |                   \
          FS_NOCOW_FL)
-static int get_attribute_from_arg(Item *item) {
+static int parse_attribute_from_arg(Item *item) {
         static const struct {
                 char character;
@@ -993,7 +1002,7 @@ static int write_one_file(Item *i, const char *path) {
         }
         if (i->argument) {
-                _cleanup_free_ char *unescaped;
+                _cleanup_free_ char *unescaped = NULL, *replaced = NULL;
                 log_debug("%s to \"%s\".",
                           i->type == CREATE_FILE ? "Appending" : "Writing", path);
@@ -1002,7 +1011,11 @@ static int write_one_file(Item *i, const char *path) {
                 if (!unescaped)
                         return log_oom();
-                r = loop_write(fd, unescaped, strlen(unescaped), false);
+                r = specifier_printf(unescaped, specifier_table, NULL, &replaced);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to replace specifiers in parameter to write '%s': %m", unescaped);
+
+                r = loop_write(fd, replaced, strlen(replaced), false);
                 if (r < 0)
                         return log_error_errno(r, "Failed to write file \"%s\": %m", path);
         } else
@@ -1712,14 +1725,6 @@ static bool should_include_path(const char *path) {
 static int parse_line(const char *fname, unsigned line, const char *buffer) {
-        static const Specifier specifier_table[] = {
-                { 'm', specifier_machine_id, NULL },
-                { 'b', specifier_boot_id, NULL },
-                { 'H', specifier_host_name, NULL },
-                { 'v', specifier_kernel_release, NULL },
-                {}
-        };
-
         _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
         _cleanup_(item_free_contents) Item i = {};
         ItemArray *existing;
@@ -1801,7 +1806,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         case RELABEL_PATH:
         case RECURSIVE_RELABEL_PATH:
                 if (i.argument)
-                        log_warning("[%s:%u] %c lines don't take argument field, ignoring.", fname, line, i.type);
+                        log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type);
                 break;
@@ -1861,7 +1866,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                         log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
                         return -EBADMSG;
                 }
-                r = get_xattrs_from_arg(&i);
+                r = parse_xattrs_from_arg(&i);
                 if (r < 0)
                         return r;
                 break;
@@ -1872,7 +1877,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                         log_error("[%s:%u] Set ACLs requires argument.", fname, line);
                         return -EBADMSG;
                 }
-                r = get_acls_from_arg(&i);
+                r = parse_acls_from_arg(&i);
                 if (r < 0)
                         return r;
                 break;
@@ -1883,7 +1888,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                         log_error("[%s:%u] Set file attribute requires argument.", fname, line);
                         return -EBADMSG;
                 }
-                r = get_attribute_from_arg(&i);
+                r = parse_attribute_from_arg(&i);
                 if (r < 0)
                         return r;
                 break;
SOURCES/0526-btrfs-util-introduce-btrfs_is_filesystem-and-make-us.patch
New file
@@ -0,0 +1,105 @@
From 81f0a57bed6e03eeaa24443d16555c7f5d20ee1a Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 22 Apr 2015 13:08:19 +0200
Subject: [PATCH] btrfs-util: introduce btrfs_is_filesystem() and make use of
 it where appropriate
Let's unify the code that checks whether an fd is on btrfs a bit.
(Also, rename btrfs_is_snapshot() to btrfs_is_subvol(), since that's
usually how this is referred to in our code)
(cherry picked from commit 21222ea5cdec65fa30a75bd5a78475459075b946)
Related: #1299714
---
 src/shared/btrfs-util.c    | 23 ++++++++++++++++-------
 src/shared/btrfs-util.h    |  3 ++-
 src/shared/machine-image.c |  9 ++++-----
 3 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
index b34ac8b15..52a214349 100644
--- a/src/shared/btrfs-util.c
+++ b/src/shared/btrfs-util.c
@@ -83,10 +83,22 @@ static int extract_subvolume_name(const char *path, const char **subvolume) {
         return 0;
 }
-int btrfs_is_snapshot(int fd) {
-        struct stat st;
+int btrfs_is_filesystem(int fd) {
         struct statfs sfs;
+        assert(fd >= 0);
+
+        if (fstatfs(fd, &sfs) < 0)
+                return -errno;
+
+        return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
+}
+
+int btrfs_is_subvol(int fd) {
+        struct stat st;
+
+        assert(fd >= 0);
+
         /* On btrfs subvolumes always have the inode 256 */
         if (fstat(fd, &st) < 0)
@@ -95,10 +107,7 @@ int btrfs_is_snapshot(int fd) {
         if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
                 return 0;
-        if (fstatfs(fd, &sfs) < 0)
-                return -errno;
-
-        return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
+        return btrfs_is_filesystem(fd);
 }
 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
@@ -115,7 +124,7 @@ int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_
         if (old_fd < 0)
                 return -errno;
-        r = btrfs_is_snapshot(old_fd);
+        r = btrfs_is_subvol(old_fd);
         if (r < 0)
                 return r;
         if (r == 0) {
diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h
index 1b9c142e5..1315def87 100644
--- a/src/shared/btrfs-util.h
+++ b/src/shared/btrfs-util.h
@@ -43,7 +43,8 @@ typedef struct BtrfsQuotaInfo {
         uint64_t exclusive_max;
 } BtrfsQuotaInfo;
-int btrfs_is_snapshot(int fd);
+int btrfs_is_filesystem(int fd);
+int btrfs_is_subvol(int fd);
 int btrfs_subvol_make(const char *path);
 int btrfs_subvol_make_label(const char *path);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index c02ee814c..256622928 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -136,12 +136,11 @@ static int image_make(
                 /* btrfs subvolumes have inode 256 */
                 if (st.st_ino == 256) {
-                        struct statfs sfs;
-                        if (fstatfs(fd, &sfs) < 0)
-                                return -errno;
-
-                        if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
+                        r = btrfs_is_filesystem(fd);
+                        if (r < 0)
+                                return r;
+                        if (r) {
                                 BtrfsSubvolInfo info;
                                 BtrfsQuotaInfo quota;
SOURCES/0527-journal-don-t-force-FS_NOCOW_FL-on-new-journal-files.patch
New file
@@ -0,0 +1,86 @@
From 245ad27530ae9e99242ebfa1631bd7fc8f66a59c Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 22 Apr 2015 13:20:49 +0200
Subject: [PATCH] journal: don't force FS_NOCOW_FL on new journal files, but
 warn if it is missing
This way users have the freedom to set or unset the FS_NOCOW_FL flag on
their journal files by setting it on the journal directory. Since our
default tmpfiles configuration now sets this flag on the directory the
flag is set by default on new files, however people can opt-out of this
by masking the tmpfiles file for it.
(cherry picked from commit fc68c92973e5437ee0489c1bc80d80f0a7b6ca0b)
Conflicts:
    src/journal/journal-file.c
Resolves: #1299714
---
 src/journal/journal-file.c | 46 ++++++++++++++++++++++++++++++++++++----------
 1 file changed, 36 insertions(+), 10 deletions(-)
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 8034b771d..0fd59ec07 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -2543,6 +2543,41 @@ void journal_file_print_header(JournalFile *f) {
                 printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (off_t) st.st_blocks * 512ULL));
 }
+static int journal_file_warn_btrfs(JournalFile *f) {