Rsync – Troubleshooting `–link-dest` Not Working with Symlinks

hard linkrsyncsymlink

I'm using rsync to backup some of my files:

rsync -aEN --delete --link-dest="$CURR/" "$SOURCE/" "$NEW/"

The --link-dest option works fine with most files, but not with symlinks.
When I was writing a clean-up script for old backups, I noticed that unchanged symlinks are not hard-linked, but rather copied.

Now I'm wondering:
Is there a way to make rsync hard-link unchanged symlinks as well?
And if not: Is it intentional or a bug in rsync?

I'm using rsync version 3.1.1 on Mac OS 10.11.

Edit:

It seems to be a problem in Mac OS X. For some reason HFS+ seems not to support hard-links to symlinks.

Best Answer

The filesystem on macOS (HFS+) does not support hard links to symbolic links:

$ touch file
$ ls -l file
-rw-r--r-- 1 kk staff 0 Jun 17 18:35 file

$ ln -s file slink
$ ls -l file slink
-rw-r--r-- 1 kk staff 0 Jun 17 18:35 file
lrwxr-xr-x 1 kk staff 4 Jun 17 18:36 slink -> file

The following would ordinarily create a hard link to a symbolic link, and is even documented in the ln manual on macOS to do so (EDIT: no it isn't, unless you have GNU coreutils installed and read the wrong manual, doh!):

$ ln -P slink hlink
$ ls -l file slink hlink
-rw-r--r-- 1 kk staff 0 Jun 17 18:35 file
lrwxr-xr-x 1 kk staff 4 Jun 17 18:38 hlink -> file
lrwxr-xr-x 1 kk staff 4 Jun 17 18:36 slink -> file

You can see by the ref count (1) that no new name was created for slink (would have been 2 for both slink and hlink if it had worked). Also, stat tells us that hlink is a symbolic link with 1 inode link (not 2):

$ stat hlink
  File: 'hlink' -> 'file'
  Size: 4               Blocks: 8          IO Block: 4096   symbolic link
Device: 1000004h/16777220d      Inode: 83828644    Links: 1
Access: (0755/lrwxr-xr-x)  Uid: (  501/      kk)   Gid: (   20/   staff)
Access: 2016-06-17 18:38:18.000000000 +0200
Modify: 2016-06-17 18:38:18.000000000 +0200
Change: 2016-06-17 18:38:18.000000000 +0200
 Birth: 2016-06-17 18:38:18.000000000 +0200

EDIT: Since I was caught using GNU coreutils, here's the tests again with /bin/ln on macOS:

$ touch file
$ /bin/ln -s file slink
$ /bin/ln slink hlink   # there is no option corresponding to GNU's -P
$ ls -l file slink hlink
-rw-r--r--  2 kk  staff  0 Jun 17 18:59 file
-rw-r--r--  2 kk  staff  0 Jun 17 18:59 hlink
lrwxr-xr-x  1 kk  staff  4 Jun 17 18:59 slink -> file

The hard link is pointing to file rather than to slink.

On e.g. Linux and OpenBSD (the other OSes I use), it is possible to do this, which results in

$ ls -l file slink hlink
-rw-rw-r-- 1 kk kk 0 Jun 17 18:35 file
lrwxrwxrwx 2 kk kk 4 Jun 17 18:43 hlink -> file
lrwxrwxrwx 2 kk kk 4 Jun 17 18:43 slink -> file

(notice "2")

Related Question