]> git.ipfire.org Git - thirdparty/util-linux.git/commit
libmount: support bind symlink over symlink
authorKarel Zak <kzak@redhat.com>
Thu, 26 Sep 2024 12:44:36 +0000 (14:44 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 26 Sep 2024 12:44:36 +0000 (14:44 +0200)
commit22cda2f71cdf2c75ac01cdbedf3a57b146186812
tree3aea85c3219d5d86031ec0c40220dff240890a37
parenta904aefe307359700dc64e3ad524baa6ee19e82a
libmount: support bind symlink over symlink

The new mount API allows for the use of AT_SYMLINK_NOFOLLOW when
opening a mount tree (aka the "mount source" for libmount).
As a result, you can now replace one symlink with another by using a
bind mount.

By default, the mount(8) command follows symlinks and canonicalizes
all paths. However, with the X-mount.nocanonicalize=source option, it
is possible to open the symlink itself. Similarly, with the
X-mount.nocanonicalize=target option, the path of the mount point can
be kept as the original symlink. (Using X-mount.nocanonicalize without
any argument works for both the "source" and "target".)

Example:

 # file /mnt/test/symlinkA /mnt/test/symlinkB
 /mnt/test/symlinkA: symbolic link to /mnt/test/fileA
 /mnt/test/symlinkB: symbolic link to /mnt/test/fileB

 # strace -e open_tree,move_mount \
   ./mount --bind -o X-mount.nocanonicalize /mnt/test/symlinkA /mnt/test/symlinkB
 ...
 open_tree(AT_FDCWD, "/mnt/test/symlinkA", OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW) = 3
 move_mount(3, "", AT_FDCWD, "/mnt/test/symlinkB", MOVE_MOUNT_F_EMPTY_PATH) = 0

 # ls -la  /mnt/test/symlinkB
 lrwxrwxrwx 1 root root 15 Sep 26 13:41 /mnt/test/symlinkB -> /mnt/test/fileA

The result is that 'symlinkB' is still a symlink, but it now points to
a different file.

This commit also modifies umount(8) because it does not work with
symlinks by default. The solution is to call umount2(UMOUNT_NOFOLLOW)
for symlinks after a failed regular umount(). For example:

 # strace -e umount,umount2 \
   ./umount /mnt/test/symlinkB
 ...
 umount2("/mnt/test/symlinkB", 0)        = -1 EINVAL (Invalid argument)
 umount2("/mnt/test/symlinkB", UMOUNT_NOFOLLOW) = 0

Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/context.c
libmount/src/context_umount.c