in my case, a USB drive flaked out in the middle of a file move. the result was
that a bunch of files were created with 0 size, e.g. ls -l showed 0 bytes.
BUT, du showed the correct sizes.

Somehow, xfs managed to allocate blocks for the file, and (probably) even wrote
out the data -- it did everything but update the file's size afterwards. so the
data is there, but marked "allocated but not used yet".

How to access it:

get the inode of the file in question:
stat filename
xfs_check -i <inode number> /dev/filesystem

a long list of blocks is printed in a format something like this:
setting inode to 15656849 for block 3/43231
setting inode to 15656849 for block 3/43232

those 3's are the allocation group number, and the 43231 is the block within
the allocation group. now, xfs_db has a 'convert' command, which you can use
thusly to turn those into raw byte offsets on the underlying device:
$ xfs_db /dev/filesystem
xfs_db> convert agnumber 3 agblock 42878 byte
0x2df93e000 (12340944896)

That number is where you want to go and copy (blocksize of your filesystem,
usually 4K) bytes from. do it in order and append, and you should be able to
copy the whole file out.