shared/install: when creating symlinks, keep existing relative symlinks
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sat, 13 Aug 2016 05:27:21 +0000 (01:27 -0400)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 19 Aug 2016 13:55:54 +0000 (09:55 -0400)
Running preset-all on a system installed from rpms or even created
using make install would remove and recreate a lot of symlinks, changing
relative to absolute symlinks. In general relative symlinks are nicer,
so there is no reason to change them, and those spurious changes were
obscuring more interesting stuff.

$ make install DESTDIR=/var/tmp/inst1

$ systemctl preset-all --root=/var/tmp/inst1
(before)
Removed /var/tmp/inst1/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service.
Created symlink /var/tmp/inst1/etc/systemd/system/ctrl-alt-del.target → /usr/lib/systemd/system/exit.target.
Removed /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/remote-fs.target.
Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/remote-fs.target → /usr/lib/systemd/system/remote-fs.target.
Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/machines.target → /usr/lib/systemd/system/machines.target.
Created symlink /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-journal-remote.socket → /usr/lib/systemd/system/systemd-journal-remote.socket.
Removed /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-networkd.socket.
Created symlink /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-networkd.socket → /usr/lib/systemd/system/systemd-networkd.socket.
Removed /var/tmp/inst1/etc/systemd/system/getty.target.wants/getty@tty1.service.
Created symlink /var/tmp/inst1/etc/systemd/system/getty.target.wants/getty@tty1.service → /usr/lib/systemd/system/getty@.service.
Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-journal-upload.service → /usr/lib/systemd/system/systemd-journal-upload.service.
Removed /var/tmp/inst1/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service.
Created symlink /var/tmp/inst1/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service → /usr/lib/systemd/system/systemd-timesyncd.service.
Removed /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-resolved.service.
Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-resolved.service → /usr/lib/systemd/system/systemd-resolved.service.
Removed /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-networkd.service.
Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-networkd.service → /usr/lib/systemd/system/systemd-networkd.service.

(after)
Removed /var/tmp/inst1/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service.
Created symlink /var/tmp/inst1/etc/systemd/system/ctrl-alt-del.target → /usr/lib/systemd/system/exit.target.
Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/machines.target → /usr/lib/systemd/system/machines.target.
Created symlink /var/tmp/inst1/etc/systemd/system/sockets.target.wants/systemd-journal-remote.socket → /usr/lib/systemd/system/systemd-journal-remote.socket.
Created symlink /var/tmp/inst1/etc/systemd/system/multi-user.target.wants/systemd-journal-upload.service → /usr/lib/systemd/system/systemd-journal-upload.service.

src/shared/install.c

index ad2e619..6c7eb9b 100644 (file)
@@ -393,6 +393,21 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang
                 log_error_errno(r, "Failed to %s: %m.", verb);
 }
 
+/**
+ * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem.
+ * wc should be the full path in the host file system.
+ */
+static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) {
+        assert(path_is_absolute(wd));
+
+        /* This will give incorrect results if the paths are relative and go outside
+         * of the chroot. False negatives are possible. */
+
+        a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
+        b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
+        return path_equal_or_files_same(a, b);
+}
+
 static int create_symlink(
                 const LookupPaths *paths,
                 const char *old_path,
@@ -401,7 +416,7 @@ static int create_symlink(
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        _cleanup_free_ char *dest = NULL;
+        _cleanup_free_ char *dest = NULL, *dirname = NULL;
         const char *rp;
         int r;
 
@@ -442,7 +457,11 @@ static int create_symlink(
                 return r;
         }
 
-        if (path_equal(dest, old_path))
+        dirname = dirname_malloc(new_path);
+        if (!dirname)
+                return -ENOMEM;
+
+        if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path))
                 return 1;
 
         if (!force) {