XCOPY – File Verification Fails on Symbolic Link but Diff Shows No Difference

backupcommand linewindowsxcopy

I'm performing an xcopy backup of a disk on Windows using the following command line:

xcopy "d:\" "r:\" /v /h /k /e /b /d

I know that the verify switch doesn't do much but a file size comparison is better than nothing for the initial copy.

The problem I'm having is that xcopy is throwing a file verification error and I don't know why. The file in question is a symbolic link and the file it points to already exists.

I have Cygwin present and I ran both a diff, diff --no-dereference, and cmp on both the links as well as the actual file that it points to on both disks with no difference.

Both files are exactly 73 bytes.

I cannot reproduce this by creating a test folder with valid or invalid links while using xcopy with the same options to perform a copy on the same or different disks.

Why would xcopy fail in this way?

Best Answer

Xcopy doesn't record a file size when copying a symbolic link, but the verification pass is unaware of symlinks and tries to compare the size of the target file against the (unset) record anyway.

This can be observed with Sysinternals Process Monitor. It's most enlightening to copy only a single file at once and only begin capturing file IO events from the xcopy.exe process after Xcopy has asked you whether the destination is a directory but before you answer—that way, you skip all the process startup IO. When /B is passed, Xcopy sets the "Open Reparse Point" option in the CreateFile operation that opens the source file, telling Windows that if the file turns out to be a symlink (reparse point) it wants to open the symlink itself rather than the target file. Later, after creating the destination file, setting it as a symlink, and shuffling some file attributes around, if /V is passed, Xcopy issues another CreateFile to open the destination file again, but without the "Open Reparse Point" option. If that's a symlink, the result is REPARSE and Windows actually opens the target file for Xcopy in the immediately following CreateFile operation. Soon, Xcopy performs a QueryStandardInformationFile operation, getting the file size of what it thought was the destination. But looking back up the log, we remind ourselves that no QueryStandardInformationFile operation on/through the source file occurred previously to compare the file size with. In contrast, if you monitor an Xcopy of a normal file (even with /B), you will see a QueryStandardInformationFile operation on the source file very shortly after it's opened with CreateFile.

In further support of this hypothesis, Xcopy can copy and successfully verify a symbolic link to a zero-byte file. Apparently the file size record is zero-initialized, so even though it's not set when copying the symlink, when the verification pass blindly opens the target of the copied symlink to get its size, the answer of zero matches the default zero.

Related Question