Documentation
¶
Overview ¶
Package transfer implements verified, resumable file-transfer primitives.
Index ¶
- Constants
- func SendFile(ctx context.Context, client *http.Client, baseURL string, root int, ...) (string, error)
- func SendFileWithProgress(ctx context.Context, client *http.Client, baseURL string, root int, ...) (string, error)
- func SendTreeWithProgress(ctx context.Context, client *http.Client, baseURL string, root int, ...) error
- func SourceName(path string) string
- func ValidateRelative(p string) error
- type Job
- type JobSpec
- type Jobs
- type Manifest
- type Store
- type Tree
- type TreeFile
- type TreeIntent
- type TreeIntentFile
- type TreeStore
- func (s *TreeStore) Abort(id string) error
- func (s *TreeStore) Commit(id string) error
- func (s *TreeStore) Create(destination string, tree Tree) (string, error)
- func (s *TreeStore) ListIntents() (map[string]TreeIntent, error)
- func (s *TreeStore) ResolveStagingPath(id, relative string) (string, error)
- func (s *TreeStore) StagingRoot(id string) string
- func (s *TreeStore) Status(id string) (TreeIntent, map[string]bool, error)
- func (s *TreeStore) Sweep(ttl time.Duration, now time.Time) (int, error)
- func (s *TreeStore) Touch(id string) error
Constants ¶
const DefaultChunkSize int64 = 4 << 20
Variables ¶
This section is empty.
Functions ¶
func SendFile ¶
func SendFile(ctx context.Context, client *http.Client, baseURL string, root int, destination, source string) (string, error)
SendFile transfers source directly to an Eta peer, resuming only its missing verified chunks. The coordinator need not proxy file bytes.
func SendFileWithProgress ¶
func SendFileWithProgress(ctx context.Context, client *http.Client, baseURL string, root int, destination, source string, progress func(completed, total int)) (string, error)
SendFileWithProgress reports each peer-acknowledged chunk. A nil callback leaves the protocol behavior identical to SendFile.
func SendTreeWithProgress ¶
func SendTreeWithProgress(ctx context.Context, client *http.Client, baseURL string, root int, destination, source string, tree Tree, progress func(completed, total int)) error
SendTreeWithProgress delivers a source directory tree to the peer via the atomic tree-transfer protocol when the peer supports it (/api/transfer-trees). Files are still sent through the existing per-file /api/transfers flow against staging paths so chunk-level verification and per-file resume reuse is unchanged. On commit the receiver performs a single os.Rename of the staging tree to the destination, giving POSIX-atomic destination materialization.
The sender is robust against legacy peers: a 404 from /api/transfer-trees falls back to the prior per-directory create + piecemeal finalize flow so mixed-version fleets degrade gracefully.
func SourceName ¶
func ValidateRelative ¶
ValidateRelative rejects empty / dot / dot-dot / absolute / escape paths. Walks the components manually so that "a/../b" (which filepath.Clean collapses to "b") is still caught. Also rejects any segment that resolves to the empty string after cleaning. Exported so HTTP handlers can reuse the same rule for user-supplied paths.
Types ¶
type Job ¶
type Job struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Completed int `json:"completed"`
Total int `json:"total"`
Error string `json:"error,omitempty"`
Done bool `json:"done"`
Started time.Time `json:"started"`
SourcePeer string `json:"sourcePeer,omitempty"`
SourceRoot int `json:"sourceRoot,omitempty"`
SourcePath string `json:"sourcePath,omitempty"`
DestinationPeer string `json:"destinationPeer,omitempty"`
DestinationRoot int `json:"destinationRoot,omitempty"`
DestinationPath string `json:"destinationPath,omitempty"`
}
Job is a durable snapshot of an asynchronous outbound transfer. SourcePeer / SourcePath and DestinationPeer / DestinationPath carry the routing needed for the server to auto-resume the transfer after a process restart. Empty peer means a local endpoint. Jobs whose fields are populated and are not Done are left in that state on restart so the resume goroutine can pick them up; jobs without routing info are too old to resume and are marked interrupted.
type JobSpec ¶
type JobSpec struct {
Name string
Total int
SourcePeer string
SourceRoot int
SourcePath string
DestinationPeer string
DestinationRoot int
DestinationPath string
}
JobSpec bundles the routing and size hints required to start a Job. It is intentionally separate from Job's runtime fields so that StartWith has no overlap with the polled-progress mutation path.
type Jobs ¶
type Jobs struct {
// contains filtered or unexported fields
}
Jobs keeps small control-plane records. The optional path persists snapshots atomically; a process restart marks in-flight work interrupted rather than pretending the sender goroutine can resume by itself.
func NewPersistentJobs ¶
type Manifest ¶
type Tree ¶
Tree is a deterministic, symlink-free description of a source directory. Paths are slash-separated and relative to the source directory.
type TreeIntent ¶
type TreeIntent struct {
Version int `json:"version"`
ID string `json:"id"`
Destination string `json:"destination"`
Directories []string `json:"directories"`
Files []TreeIntentFile `json:"files"`
Created time.Time `json:"created"`
LastProgress time.Time `json:"lastProgress"`
}
TreeIntent is the durable per-session record. Persisted atomically alongside the staging directory under {root}/.eta/intents/{id}.json.
type TreeIntentFile ¶
TreeIntentFile is the per-file record kept in the intent. Size is the pre-transfer byte length so commit can verify presence without keeping full chunk manifests in the intent record (which would balloon for big trees).
type TreeStore ¶
type TreeStore struct {
// contains filtered or unexported fields
}
TreeStore manages a per-root tree-transfer session lifecycle. A session reserves a destination, stages every file under that root's hidden staging directory, and atomically promotes the staging tree to the destination via a single os.Rename. The promotion is POSIX-atomic on any single filesystem; no half-copied destination tree can ever be observed.
The contract this store enforces:
- destination must NOT exist before Create (single-rename semantics)
- files & directories inside the staging tree must be valid relative paths
- the staging root (".eta") is reserved and never user-creatable
- commit either succeeds completely or leaves the destination absent
Staging lives inside the destination root on purpose: it removes the EXDEV cross-filesystem rename hazard that per-file staging (under the user cache directory) currently has against NAS-mounted roots.
func NewTreeStore ¶
NewTreeStore binds a tree session manager to a single filesystem root.
func (*TreeStore) Commit ¶
Commit verifies that every file in the intent is fully written under the staging tree, then performs a single os.Rename of the staging root to the destination. Either the entire destination tree appears or the rename fails and the staging tree remains for retry/abort.
func (*TreeStore) Create ¶
Create reserves a destination and creates the staging tree. The supplied Tree describes the directories and files that will be sent. On success returns the new intent ID and persists the intent record. On any error the partially-built staging directory is removed.
func (*TreeStore) ListIntents ¶
func (s *TreeStore) ListIntents() (map[string]TreeIntent, error)
ListIntents returns every persisted tree-intent record on this root, keyed by id. Used by the receiver to discover in-flight sessions during per-file finalize (so it can refresh LastProgress on the matching tree). Returns an empty map when no intents exist.
func (*TreeStore) ResolveStagingPath ¶
ResolveStagingPath returns the absolute staging path a sender must target for a given file (relative to the session's destination tree).
func (*TreeStore) StagingRoot ¶
StagingRoot returns the per-session staging directory for the given ID. Useful for tests; the HTTP API resolves this internally.
func (*TreeStore) Status ¶
Status returns the current intent plus a per-file "complete" map derived from the filesystem: each file is complete iff a regular file exists at its staging path with the expected size. Cheap (no chunk manifest in intent) and survives crashes.