Files
acp/hash_policy.go
T
Samuel Cui 730fb0efb4 docs: correct the statements the release review falsified
Each of these described behaviour the code does not have:

- The nil-item note on Submit and the comment on its test claimed a rejected submission ends the
  run, while the feed keeps accepting items; the test now pins that follow-up submissions are still
  accepted and complete.
- The buildOption comment claimed every repeated option is last-wins, although the job and device
  options accumulate.
- The usesCache comment claimed stored hashes are always read, while HashRead never looks one up;
  the function decides whether a run needs a cache at all.
- TESTING.md claimed the non-linear pre-allocation on /dev/full reports ErrTargetIO. Measured on
  Linux, fallocate there returns ENODEV, which matches none of the classified errors (ENOSPC,
  EROFS, EIO), so the target carries the device's own error.
- TESTING.md's "every signature cache test" filter missed TestRunHoldsTargetCacheUntilTheHashIsComplete
  and TestManagedSignatureIsNotOrdinaryCopiedXattr.
- The Windows vet diagnostic that make check does not run is documented where the cross-platform
  commands live, with the reason it is accepted instead of worked around.
2026-09-19 16:58:32 +08:00

108 lines
3.6 KiB
Go

package acp
import "fmt"
// HashPolicy selects how an item's content hash is produced and how the stored hash cache is
// used. Hashing and the cache are one policy, because refreshing the cache requires a
// computed hash: a miss under a refresh-enabled value must read.
//
// A targetless item may reuse a stored hash, because it records content facts without writing
// the content anywhere. A transfer always reads its source and produces a computed hash, so
// the two reuse-only values are rejected for an item that requests targets.
type HashPolicy uint8
const (
// HashOff produces no hash and leaves the cache untouched.
HashOff HashPolicy = iota
// HashCachedOnly reuses a stored hash for a targetless item; a miss leaves the item
// without a hash. Rejected for an item with targets.
HashCachedOnly
// HashCachedOrRead reuses a stored hash for a targetless item; a miss reads and hashes
// without refreshing. Rejected for an item with targets, because a copy always reads and
// a reused hash would then describe content the transfer never computed.
HashCachedOrRead
// HashCachedOrReadRefresh reuses a stored hash for a targetless item; a miss reads,
// hashes and refreshes. A transfer always reads, hashes and refreshes.
HashCachedOrReadRefresh
// HashRead always reads and hashes; the cache is not refreshed. A targetless item
// ignores its stored hash, and a transfer hashes the content it copies.
HashRead
// HashReadRefresh always reads and hashes, and refreshes the cache.
HashReadRefresh
)
func (p HashPolicy) String() string {
switch p {
case HashOff:
return "off"
case HashCachedOnly:
return "cached-only"
case HashCachedOrRead:
return "cached-or-read"
case HashCachedOrReadRefresh:
return "cached-or-read-refresh"
case HashRead:
return "read"
case HashReadRefresh:
return "read-refresh"
}
return fmt.Sprintf("unknown(%d)", uint8(p))
}
// valid reports whether the value is one of the defined policies.
func (p HashPolicy) valid() bool {
return p <= HashReadRefresh
}
// appliesToTransfer reports whether the policy can describe an item that requests a target. A
// transfer always reads its source and produces a computed hash, so a value that promises a
// stored hash where a transfer cannot use one is rejected instead of silently reading.
func (p HashPolicy) appliesToTransfer() bool {
switch p {
case HashCachedOnly, HashCachedOrRead:
return false
}
return true
}
// readsContent reports whether the policy needs the source content.
func (p HashPolicy) readsContent() bool {
switch p {
case HashCachedOrRead, HashCachedOrReadRefresh, HashRead, HashReadRefresh:
return true
}
return false
}
// producesHash reports whether a read item receives a content hash.
func (p HashPolicy) producesHash() bool {
return p != HashOff
}
// usesCache reports whether the policy touches the stored cache at all: it either reuses a stored
// hash, refreshes one with a computed hash, or both. It is what decides whether a run needs a
// signature cache, and it is true for HashRead, which hashes content and writes nothing:
// reusesCache reports the lookup, refreshesCache reports the write.
func (p HashPolicy) usesCache() bool {
return p != HashOff
}
// reusesCache reports whether a stored hash may replace the hash of an item that does not
// write its source.
func (p HashPolicy) reusesCache() bool {
switch p {
case HashCachedOnly, HashCachedOrRead, HashCachedOrReadRefresh:
return true
}
return false
}
// refreshesCache reports whether a computed hash is written back to the cache.
func (p HashPolicy) refreshesCache() bool {
switch p {
case HashCachedOrReadRefresh, HashReadRefresh:
return true
}
return false
}