Below set of commands are executed on SuSE Linux Enterprise Server 11 SP 3 operating system,
There can be many scenarios of copying symbolic links between different directories, let me try to explain some of the cases with examples to help you understand
I have created few sample files and symbolic links to demonstrate this example
# mkdir /tmp/src_file # cd /tmp/src_files
# ls -l total 0 -rw-r--r-- 1 root root 0 Sep 11 17:53 a -rw-r--r-- 1 root root 0 Sep 11 17:53 b -rw-r--r-- 1 root root 0 Sep 11 17:53 c lrwxrwxrwx 1 root root 13 Sep 11 17:53 cron_allow -> ../cron.allow lrwxrwxrwx 1 root root 12 Sep 11 17:53 cron_deny -> ../cron.deny
Copy symbolic links as files
Above I have two smylinks cron_allow
and cron_deny
, Let us copy them to /tmp/dst_files
as normal files instead of symlinks
# cp -LRv /tmp/src_files/* /tmp/dst_files/ `/tmp/src_files/a' -> `/tmp/dst_files/a' `/tmp/src_files/b' -> `/tmp/dst_files/b' `/tmp/src_files/c' -> `/tmp/dst_files/c' `/tmp/src_files/cron_allow' -> `/tmp/dst_files/cron_allow' `/tmp/src_files/cron_deny' -> `/tmp/dst_files/cron_deny'
Verify the symlinks
# ll /tmp/dst_files/ total 8 -rw-r--r-- 1 root root 0 Sep 11 18:03 a -rw-r--r-- 1 root root 0 Sep 11 18:03 b -rw-r--r-- 1 root root 0 Sep 11 18:03 c -rw-r--r-- 1 root root 28 Sep 11 18:03 cron_allow -rw-r--r-- 1 root root 15 Sep 11 18:03 cron_deny
As you see both symlinks are converted into normal file. Let us match the content of these with the original file
# sdiff cron_allow /tmp/cron.allow root root deepak deepak amit amit rahul rahul
From the 'man' page
‘-L’ ,‘--dereference’ - Follow symbolic links when copying from them. With this option, cp cannot create a symbolic link. For example, a symlink (to regular file) in the source tree will be copied to a regular file in the destination tree.
Copy symlinks as symlinks (not a file)
# cp -Prv /tmp/src_files/* /tmp/dst_files/ `/tmp/src_files/a' -> `/tmp/dst_files/a' `/tmp/src_files/b' -> `/tmp/dst_files/b' `/tmp/src_files/c' -> `/tmp/dst_files/c' `/tmp/src_files/cron_allow' -> `/tmp/dst_files/cron_allow' `/tmp/src_files/cron_deny' -> `/tmp/dst_files/cron_deny'
Verify the symlinks
# ll /tmp/dst_files/ total 0 -rw-r--r-- 1 root root 0 Sep 11 18:06 a -rw-r--r-- 1 root root 0 Sep 11 18:06 b -rw-r--r-- 1 root root 0 Sep 11 18:06 c lrwxrwxrwx 1 root root 13 Sep 11 18:06 cron_allow -> ../cron.allow lrwxrwxrwx 1 root root 12 Sep 11 18:06 cron_deny -> ../cron.deny
This can also be achieved with some more arguments like below
# cp -dv /tmp/src_files/* /tmp/dst_files/
`/tmp/src_files/a' -> `/tmp/dst_files/a'
`/tmp/src_files/b' -> `/tmp/dst_files/b'
`/tmp/src_files/c' -> `/tmp/dst_files/c'
`/tmp/src_files/cron_allow' -> `/tmp/dst_files/cron_allow'
`/tmp/src_files/cron_deny' -> `/tmp/dst_files/cron_deny'
Copy symlinks with same permissions and ownership as from source directory
Normally if you copy a file the permission of the target location is changed to the user which was used to copy the file (considering that user as the new owner of the target location)
Let us change the permission and owner of one of the file from our source directory
# ls -l total 0 -rwxrwxrwx 1 oamsys root 0 Sep 11 17:53 a -rwxrwxrwx 1 oamsys root 0 Sep 11 17:53 b -rw-r--r-- 1 root root 0 Sep 11 17:53 c lrwxrwxrwx 1 root root 13 Sep 11 17:53 cron_allow -> ../cron.allow lrwxrwxrwx 1 root root 12 Sep 11 17:53 cron_deny -> ../cron.deny
After copying
# cp -dv /tmp/src_files/* /tmp/dst_files/ `/tmp/src_files/a' -> `/tmp/dst_files/a' `/tmp/src_files/b' -> `/tmp/dst_files/b' `/tmp/src_files/c' -> `/tmp/dst_files/c' `/tmp/src_files/cron_allow' -> `/tmp/dst_files/cron_allow' `/tmp/src_files/cron_deny' -> `/tmp/dst_files/cron_deny'
Verify
# ll /tmp/dst_files/ total 0 -rwxr-xr-x 1 root root 0 Sep 11 18:12 a -rwxr-xr-x 1 root root 0 Sep 11 18:12 b -rw-r--r-- 1 root root 0 Sep 11 18:12 c lrwxrwxrwx 1 root root 13 Sep 11 18:12 cron_allow -> ../cron.allow lrwxrwxrwx 1 root root 12 Sep 11 18:12 cron_deny -> ../cron.deny
So considering my umask value i.e. 0022 the file permission were updated to 755 and ownership has been changed to 'root'' as the files were copied using 'root' user.
So how do I preserve these permissions along with symlinks
# cp -dav /tmp/src_files/* /tmp/dst_files/ `/tmp/src_files/a' -> `/tmp/dst_files/a' `/tmp/src_files/b' -> `/tmp/dst_files/b' `/tmp/src_files/c' -> `/tmp/dst_files/c' `/tmp/src_files/cron_allow' -> `/tmp/dst_files/cron_allow' `/tmp/src_files/cron_deny' -> `/tmp/dst_files/cron_deny'
Verify
# ll /tmp/dst_files/ total 0 -rwxrwxrwx 1 oamsys root 0 Sep 11 17:53 a -rwxrwxrwx 1 oamsys root 0 Sep 11 17:53 b -rw-r--r-- 1 root root 0 Sep 11 17:53 c lrwxrwxrwx 1 root root 13 Sep 11 17:53 cron_allow -> ../cron.allow lrwxrwxrwx 1 root root 12 Sep 11 17:53 cron_deny -> ../cron.deny
So everything looks on track now.
From 'man' page
-a, --archive same as -dR --preserve=all
Copy symlinks with relative path
One big question here, for me the copying of symbolic link is working fine because we are copying the symlink in the same parent directory and the target file is always accessible by the symlink.
Let me show you some more examples
# ll total 0 -rwxrwxrwx 1 oamsys root 0 Sep 11 17:53 a -rwxrwxrwx 1 oamsys root 0 Sep 11 17:53 b -rw-r--r-- 1 root root 0 Sep 11 17:53 c lrwxrwxrwx 1 root root 13 Sep 11 17:53 cron_allow -> ../cron.allow lrwxrwxrwx 1 root root 12 Sep 11 17:53 cron_deny -> ../cron.deny
Here above the symlink cron_allow is pointing to /tmp/cron.allow
, but what I copy these files to a completely different location where relative path ../cron.allow
is inaccessible
# cp -Prv /tmp/src_files/* /tmp/test/new_dst/ `/tmp/src_files/a' -> `/tmp/test/new_dst/a' `/tmp/src_files/b' -> `/tmp/test/new_dst/b' `/tmp/src_files/c' -> `/tmp/test/new_dst/c' `/tmp/src_files/cron_allow' -> `/tmp/test/new_dst/cron_allow' `/tmp/src_files/cron_deny' -> `/tmp/test/new_dst/cron_deny'
Verify
# ll /tmp/test/new_dst/ total 0 -rwxr-xr-x 1 root root 0 Sep 11 18:19 a -rwxr-xr-x 1 root root 0 Sep 11 18:19 b -rw-r--r-- 1 root root 0 Sep 11 18:19 c lrwxrwxrwx 1 root root 13 Sep 11 18:19 cron_allow -> ../cron.allow lrwxrwxrwx 1 root root 12 Sep 11 18:19 cron_deny -> ../cron.deny
# cd /tmp/test/new_dst/
# less cron_allow
cron_allow: No such file or directory
So ../cron.allow
is not accessible. To avoid such scenarios it is always recommended to use full path of the target file for a symlink.
Currently I am now aware of a single command which can fix this, if someone knows than please put in your comment which can be used to overcome such scenarios
As of now I use scripting to fix these issues (a small sample script)
#!/bin/sh src="/tmp/src_files" dst="/tmp/test/new_dst" #clean destination if [ -d $dst ]; then echo "Cleaning target directory $dst" rm -f $dst/* fi #copy files for i in `ls $src`; do path="" if [ -L $i ]; then path=`readlink -f $i` ln -s $path $dst/$i else cp -vP $i $dst fi done # Listing target directory files ls -al $dst
I hope the article was useful.