2

Problem:

Trying to copy the entire content of one directory to another which includes hidden files/directories and symlinks.

This is fairly straightforward cp -a ${src} ${dst}

It works as expected on my local VM but it doesn't work properly on my VPS (hostgator). It fails to copy the symlinks if the link target comes later in the directory listing.

Considering that the original link targets work, are device local and I'm copying on the same device, what is the best way to make sure that the entire directory structure is copied exactly?

It took me quite a while but I figured out that all I actually need to do is run the copy command twice and the second pass adds the missing links. Is there a better way than this?

Working but Hack-ish Solution:

src=".default/.";
dst="tld.domain/";
cp -a ${src} ${dst};
cp -a ${src} ${dst};

I shouldn't need to double up the command to ensure that everything copied correctly.

I tried tar as well, same issue. I didn't try rsync but both seem like an overkill solution for something that should be a pretty trivial thing.

Scenario:

For additional perspective in case it matters or I haven't be clear.

I have created a template directory structure for website domains. After creating a new sub-domain on my web-host, I copy the template over to the sub-domain's document root.

This is my subdomain and template (.default/) directory structure:

sites/
    .default/
        .htaccess
        .www -> production/
        production/
    tld.domain.x/
    tld.domain.y/
    tld.domain.z/

So I create a new subdomain and then populate it's document root with the contents of the template directory. The issue here is that .www/ is a symlink to production/ but cp et al create everything in sorted order so it tries to create .www/ before creating production/ and consequently fails.

My web-host is a HostGator VPS running CentOS6

I can drill the issue down to some restriction on creating symlinks. My guess is that the VPS doesn't allow symlinks to external sources which would normally behave the same as an invalid link target. IE unknown source.

Where neither names exist, calling on my VPS:

ln -s foo bar

Outputs:

ln: creating symbolic link `bar': Permission denied

If I create foo/ first then link, it works as expected.

df -T . shows that the filesystem type is virtfs.

  • 1
    Symlink targets don't have to exist (try it!), so that part of the diagnosis is wrong. The correct way to do what you want is cp -a ${src} ${dst}, which works fine for me with your layout. There is some material detail missing from your description, although I don't know what it might be. – Michael Homer Mar 7 '15 at 2:04
  • Thanks, I tried in my local CentOS VM and it works as expected so it has to do with my VPS. I don't have root and some permissions are probably a little restricted. That didn't occur to me. I'll update my question to clarify the environment. – Fuzzy Logic Mar 7 '15 at 2:25
  • I'm pretty sure you want cp -HR . /path/to/target. The spec says: If source_file is a file of type symbolic link, and the options require the symbolic link itself to be acted upon, the pathname contained in dest_file shall be the same as the pathname contained in source_file. – mikeserv Mar 7 '15 at 3:50
  • @mikeserv I gave that a try just now but it still fails the same way. – Fuzzy Logic Mar 7 '15 at 12:14
  • Sorry then. That's a bummer. – mikeserv Mar 7 '15 at 12:31
0

Normally, a symbolic link is inert: it can store any sequence of bytes (except null bytes, and only up to a certain length). When creating a symbolic link, it is irrelevant whether the content happens to point to an existing file. It's only when accessing the symbolic link that the existence of the target matters. So what you're seeing on your VPS is abnormal (and unusual, to boot).

Virtfs is a filesystem designed for KVM/QEMU virtual machines where both the guest and the host is running Linux. This filesystem presents a portion of the host's filesystem in the guest. It allows sharing files between the guest and the host in an easy and fast way (faster than a network filesystem). It is useful on a VPS, for example, where all guests are running the same operating system, so the OS files are shared (read-only, one hopes) between the host and all the guests. It can also be used for guest-specific files (which are not shared with any other guest).

Virtfs offers two storage modes: mapped and passthrough. Passthrough mode, as the name suggests, simply passes file manipulation commands from the guest through to the host; for example creating a symlink in the guest creates a symlink on the host. Mapped mode uses extended attributes in the host to store files' metadata, and makes all files owned by the user running QEMU. Mapped mode is appropriate to store per-guest files, because they are then owned by the one user running QEMU, no matter what user owns them in the guest. Your site is presumably stored on a virtfs instance in mapped mode.

In mapped mode, symbolic links are stored as a regular file, with an extended attribute indicating that they are a symbolic link. This, per se, doesn't explain your problem. However, it opens a window of opportunity for something to go wrong. It seems that something is checking for the existence of symlink targets and denying their creation if the target doesn't exist.

I lack experience with virtfs, so I can't tell you whether this is due to some configuration option. I did find an issue that's similar to yours. The user affected was also on a VPS and also got “permission denied” errors when attempting to create a dangling symbolic link. His conclusion was

FYI, the problem is not RedHat 4.4 - the ln -s command is broken on purpose by the system provider to prevent users from installing things like RVM.

You may want to contact your VPS provider to request an explanation and hopefully a fix.

If you can't obtain a fix, a workaround would be to first copy directories and regular files, then copy symbolic links. I don't think any of the usual copy programs (GNU cp, rsync, cpio, tar, pax) have any way to copy everything but leave symlinks alone. A simple workaround is to perform the copy twice (with rsync, so that already-copied files are not copied again), ignoring errors in the first round:

rsync -a source/ destination/ 2>/dev/null
rsync -a source/ destination/
  • Here is the solution I came up with today that solves the issue and does not invoke an error code: pushd ${src}/ >/dev/null; { find . -depth ! -type l; find . -depth -type l; } | cpio -pamd --quiet ${dst}/; popd > /dev/null – Fuzzy Logic Mar 8 '15 at 1:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.