You are listing edge cases that exist, but the relevant question is whether they meaningfully apply to Firefox profile migration on typical systems.
Same-disk detection can be done through stat() on both paths and comparing st_dev, which is trivial. But more importantly, why does this matter for migration? If it is cross-filesystem, copy and move works fine. If you are concerned about atomicity, that is a different problem, but Firefox profiles are not typically manipulated concurrently during a migration that happens once at startup.
Partial copy cleanup is reasonable, but again, context matters. For a one-time migration triggered at browser start with exclusive access to the profile, you verify checksums or sizes post-copy, and if verification fails, you do not delete the source. User gets an error, tries again later. Not complex.
As for overwrites: do not overwrite if target exists. Check once before starting. If the XDG path already has data, skip migration entirely or prompt. This is not a continuous sync operation.
FWIW "cp -a" preserves access rights on Unix. On Windows, ACLs can be trickier but for user-owned profiles it is usually a non-issue.
The real complexity in robust file operations show up with network filesystems (SMB, NFS), concurrent access patterns, or where atomicity guarantees are critical (and a move operation is indeed atomic, assuming typical systems). For a single-user profile migration that happens once with exclusive lock? The corner cases you mentioned are either straightforward to handle or do not apply.