Describe a change by what the run saw on both sides - #221
Merged
Conversation
A Change carried a kind and a path, and the kind was the only thing it knew about either side. A caller reviewing a deferred run then had to re-read the workdir and the upper to learn what changed, and could not tell a chmod from a rewrite or pair a rename. The change now carries an Entry for each side (kind, mode, size, digest for files, target for links) and derives Added, Modified, or Deleted from which sides exist. Helpers cover the questions a reviewer asks: content unchanged, type changed, and a digest join that pairs renames. Both sides are still read from the live trees; the digests stay empty until the branch records what it saw at first touch. Signed-off-by: Cong Wang <cwang@multikernel.io>
The change set can only say what the run replaced if something remembered the workdir entry before the branch touched it. This map holds that, keyed by relative path, first record wins, nothing on disk: losing it degrades a report from Modified to Added and nothing else, which does not justify an fsync in the syscall path the way a lost whiteout does. Signed-off-by: Cong Wang <cwang@multikernel.io>
The change set labelled a path by looking at the live workdir when the report was read, so a file that appeared underneath after the run turned a creation into an overwrite, and a report could never carry the old digest. Every handler that mutates a path now records the workdir entry at first touch, and the report is computed from that record and the upper alone. A copied-up file's digest is taken inside the copy stream, off the COW lock, and handed back once the copy lands: hashing under the lock would reintroduce the stall the two-phase copy exists to avoid. A file unlinked without a copy-up is hashed at capture from the workdir, which the commit has not touched yet, guarded by its recorded size. A whiteout over an entry the confined lstat could not reach keeps being reported, with an unknown before side, so an obstructed deletion still shows as outstanding after a failed merge. Signed-off-by: Cong Wang <cwang@multikernel.io>
A kept or preserved branch outlives the process that ran it, and with it the origin map, so recovery tooling could only report additions and bare deletions. The marker already lists the outstanding deletions and is written once, atomically, off the syscall path; it now also holds one line per origin record, and a preserved branch reports the same change set a live run does through the same computation. Nothing is written during the run. A supervisor killed before it preserves loses the origins and the report degrades to unknown before sides, which is the trade for a run that pays no fsync per touch. Signed-off-by: Cong Wang <cwang@multikernel.io>
The C ABI only carried a change's kind and path, so a binding could not show what a run replaced. One accessor fills a pointer-free struct with either side (kind, mode, size, digest) so every binding can hold it on the stack, and a second returns a symlink target as a string. The existing kind and path accessors keep their signatures. Signed-off-by: Cong Wang <cwang@multikernel.io>
Change now carries the workdir entry the run first saw and the branch entry it left, each as an Entry with kind, mode, size, digest, and link target. kind is derived from which sides exist, so the README examples keep printing "A out.txt", and renames() pairs moved files by digest. Signed-off-by: Cong Wang <cwang@multikernel.io>
Change now carries the Before and After entries (kind, mode, size, digest, link target) and derives its kind from which sides exist; Renames pairs moved files by digest. The ChangeKind constants are unchanged. Signed-off-by: Cong Wang <cwang@multikernel.io>
Signed-off-by: Cong Wang <cwang@multikernel.io>
lstat_entry_in_root mapped char and block devices to "absent", the same answer as ENOENT. A device node in the upper was then reported as a deletion of a path that still exists, and one the run removed from the workdir lost its before side. Other exists for entries that carry no bytes, so devices belong there alongside fifos and sockets. Signed-off-by: Cong Wang <cwang@multikernel.io>
The entry mode is st_mode & 0o7777, but os.FileMode keeps setuid, setgid, and sticky in its own high bits. Casting the raw value put those three bits where FileMode never looks, so Perm() silently dropped a setuid file to 0755. Signed-off-by: Cong Wang <cwang@multikernel.io>
Every other discriminant the header exposes is a named enum, but the entry kind and the side selector were bare integers documented only in a comment, so each binding mirrored them by position. sandlock_entry_kind and SANDLOCK_CHANGE_BEFORE/AFTER give C callers and cbindgen consumers the names, and widen kind to the uint32 the sibling enums use. Signed-off-by: Cong Wang <cwang@multikernel.io>
The kind of a change is now derived from which sides are present, and both bindings read the sides directly, so the character accessor had no callers left. C callers get the same answer from sandlock_result_change_entry's return value on each side. Signed-off-by: Cong Wang <cwang@multikernel.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Result.changesused to carry a kind and a path, and the kind was derived by looking at the live workdir when the report was read. A caller reviewing a deferred run had to re-read both trees to learn what changed, could not tell a chmod from a rewrite, could not pair a rename, and saw a file that appeared underneath after the run turn a creation into an overwrite.A
Changenow carries anEntryfor each side, the workdir entry the run first saw and the branch entry it left, with kind, mode, size, a SHA-256 digest for files, and the target for symlinks. Added, Modified, and Deleted are derived from which sides exist.content_unchanged,type_changed, and arenamesdigest join answer the questions a reviewer asks.How the before side is captured
API
Entry,EntryKind, two-sidedChangewithkind()as a method,renames.Change.kindstops being a field, no shim.sandlock_entry_tplussandlock_result_change_entryandsandlock_result_change_target. Existing accessors keep their signatures. Header regenerated.Entry, the newChange, andrenames/Renames.kindstays a string property in Python so existing examples keep printingA out.txt.Ring, already compiled through the rustls features, becomes a direct dependency of core for SHA-256.
Testing