Re: Hardlink support feasible?

Greg Troxel <[email protected]> Sat, 17 Jan 2026 10:16:59 -0500
Newsgroups gmane.network.unison.general
Message-ID <[email protected]>
Alan Savage <[email protected]> writes:

> Thanks for getting back to me quickly.
>
>> it may be instructive to understand rsync's edge cases.
>
> Agreed. `rsync -H` only preserves source hard links, not destination hard
> links, so it is not symmetrical. If the destination does not support
> hard-links the files are silently duplicated. If you then reverse source
> and destination, the hard links are destroyed.
>
> The `rsync -H` implementation builds an in-memory hard-link table keyed by
> (device, inode). I think we'd want something similar, but that's getting
> into implementation.
>
> For a second pass, I'd try and preserve any asymmetry. E.g. if on Side A 2
> files are hardlinked and on Side B they are not, then we keep them that
> way. If one of the files on Side B is touched then the question would be
> whether to

Beware that I am not thinking deeply, so take this with grain of salt
design thought (not as well thought out unison doctrine):

  Probably we need to think about NFS and CIFS mounts.  NFS supports
  hardlinks (I just made one, NetBSD 9 client, NetBSD 10 server).  CIFS
  almost certainly does not (but I avoid it, so idk).

  First define hardlink-capable as 1) OS supports hardlinks 2) fs that
  contains the replica supports hardlinks and 2) version of unison
  running on both sides advertise hardlink support.  There's also a
  crazy edge case of a replica with a mount inside it.

  If neither side is hardlink-capable, we are where we are now.  Perhaps
  there is a user-facing pref to disable or enable hardlinks.  Maybe we
  start off with it opt in and later flip to opt out.  Perhaps we can
  avoid that.

  If both sides are hardlink-capable, then I would see AhB (hardlinked)
  and AcB (separate files) as a conflict.  The point is to replicate the
  same state.  I am leery of just changing AcB to AhB and vice versa
  because while the bits are the same on read, writing has different
  semantics.

  If one side is hardlink-capable and the other isn't, then I can see
  newly appearing AhB on the capable side leading to AcB on the
  non-capable side, with AcB on the non-capable side leading to AcB on
  the capable side.  And, AhB(capable) and AcB(non-capable) counting as
  not a difference.

  Perhaps we have a mechanism to coalesce AcB into AhB, when requested,
  and perhaps this has nothing to do with unison and it's just a utility
  we point to (or is written for this), to help people get from here to
  unison-hardlinks.

  This is beyond user spec, but we have archive files that describe the
  local state to detect changes.  Those will likely have to change.  I
  have always wanted a tool to dump archive files to nerd-readable; it
  would be cool to have unison-archive2json and unison-json2archive and
  similarly for fingerprint (or the tool works on pairs).

>> if hardlinks are specified by POSIX and if real implementations follow
>> POSIX

> POSIX specifies hardlink behavior for `linkat`, `unlinkat` and the
> `fstatat` calls, so we can rely on the POSIX where it's available. I'm not
> sure about Windows.
>
> Some of the relevant rsync code seems to rely on the POSIX API:

sounds good. 

>> what happens on filesystems without this facility
>
> What happens right now? I would start with preserving that behavior or
> leaving a big caveat that the behavior is undefined and logging a warning
> with future updates having better conflict handling. What do you think?

I would really like to avoid documenting UB.  People sync from POSIX to
windows all the time.  (I don't have any experience doing that.)

> In general, I would think we can treat POSIX errors the same way we
> currently treat file creation/editing/deletion errors, but I'd have to dig
> in to see if that's really sufficient.

I think that's a good starting position.  Unison generally takes the
position that system calls are supposed to work reliably and that errors
are errors.  We don't have a "then back off and try plan B" mentality.

>> The second hard part is extending the sync protocol to represent
>> hardlinks and decomposing the rules for what happens into rules for the
>> two sides.
>
> How can we proceed with this? Is there a good example to follow?

Probably not but you can start reading the sources.  I really have no
idea how hard that part is.  There is an extension mechanism for the two
sides to know what the other is capable of.   The idea would be that
each side says "I support the hardlink feature" and then the
describe-replica protocol  is somehow changed/extended to say that the
2..N files that are links in the fs are links to the original file,
instead of inlining the size/checksum.

The other thing you could do, in parallel, is to prepare a (marked draft
please) PR to update the documentation to describe your proposed
hardlink syncing rules.  We'd need that, but writing that first would
let people think about whether the details are right, and if it's
complete.  This would take the place of a specification in software
engineering terms (not that we have rules like that; it's just how I
think).

I'm pretty sure you're aware, but I want to caution you that this is a
fairly big project, and I'd estimate closer to 100 hours than 10 or 1000
of work time.   I dare not estimate to more than integer log10 values :-)

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].