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.