When I use rsync with option --copy-unsafe-links
it does not work when a symlink is relative. If a simlink is absolute it works as expected: copies content of the target instead of creating a broken symlink.
I have a dir structure in /tmp
:
rsync-problem
├── dst
└── src
├── a
│ ├── a-file
│ ├── b-symlink -> ../b
│ └── c-symlink -> /tmp/rsync-problem/src/c
├── b
│ └── b-file
└── c
└── c-file
after running rsync:
rsync \
--copy-unsafe-links \
--links \
--recursive \
/tmp/rsync-problem/src/a \
/tmp/rsync-problem/dst
I get a broken relative symlink: b-symlink -> ../b
in the rsync-problem/dst/a
dir and a properly converted absolute symlink into a file rsync-problem/dst/a/c-symlink/c-file
rsync-problem
├── dst
│ └── a
│ ├── a-file
│ ├── b-symlink -> ../b
│ └── c-symlink
│ └── c-file
└── src
├── a
│ ├── a-file
│ ├── b-symlink -> ../b
│ └── c-symlink -> /tmp/rsync-problem/src/c
├── b
│ └── b-file
└── c
└── c-file
man of rsync for --copy-unsafe-links:
This tells rsync to copy the referent of symbolic links that point outside the copied tree. Absolute symlinks are also treated like ordinary files, and so are any symlinks in the source path itself when --relative is used. This option has no additional effect if --copy-links was also specified.
Does not state that this option has no effect when a relative symlink is encountered.
and if I understand correctly
This tells rsync to copy the referent of symbolic links that point outside the copied tree.
/tmp/rsync-problem/src/b
is outside the copied src
tree:
/tmp/rsync-problem/src/a
what am I doing wrong and how to make rsync convert relative symlinks into files as it does with the absolute ones?
Script to replicate the issue:
#!/bin/sh
set -ue
mkdir -p /tmp/rsync-problem/dst
mkdir -p /tmp/rsync-problem/src/a
touch /tmp/rsync-problem/src/a/a-file
mkdir -p /tmp/rsync-problem/src/b
touch /tmp/rsync-problem/src/b/b-file
mkdir -p /tmp/rsync-problem/src/c
touch /tmp/rsync-problem/src/c/c-file
cd /tmp/rsync-problem/src/a
ln -sr ../b b-symlink
ln -s /tmp/rsync-problem/src/c /tmp/rsync-problem/src/a/c-symlink
rsync \
--copy-unsafe-links \
--links \
--recursive \
/tmp/rsync-problem/src/a \
/tmp/rsync-problem/dst
rsync
treats differently paths with the /
at the end and without it. This applies for both: source and destination paths. The problem is that the source path in the question /tmp/rsync-problem/src/a
does not end with the /
.
As per manual for the --copy-unsafe-links
option:
Note that the cut-off point is the top of the transfer, which is the part of the path that rsync isn't mentioning in the verbose output. If you copy "/src/subdir" to "/dest/" then the "subdir" directory is a name inside the transfer tree, not the top of the transfer (which is /src) so it is legal for created relative symlinks to refer to other names inside the /src and /dest directories. If you instead copy "/src/subdir/" (with a trailing slash) to "/dest/subdir" that would not allow symlinks to any files outside of "subdir".
Using source path with a trailing slash: /tmp/rsync-problem/src/a/
this way:
rsync \
--copy-unsafe-links \
--links \
--recursive \
/tmp/rsync-problem/src/a/ \
/tmp/rsync-problem/dst
generates proper structure at the /tmp/rsync-problem/dst
without the broken b-symlink -> ../b
rsync-problem/
├── dst
│ ├── a-file
│ ├── b-symlink
│ │ └── b-file
│ └── c-symlink
│ └── c-file
└── src
├── a
│ ├── a-file
│ ├── b-symlink -> ../b
│ └── c-symlink -> /tmp/rsync-problem/src/c
├── b
│ └── b-file
└── c
└── c-file
but with the /tmp/rsync-problem/dst/b-symlink/b-file
at the destination. Yet again in compliance with the manual statement referring to the --copy-unsafe-links
:
This tells rsync to copy the referent of symbolic links that point outside the copied tree. Absolute symlinks are also treated like ordinary files, and so are any symlinks in the source path itself when --relative is used.