diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-06-04 19:43:50 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-06-04 19:46:36 +0900 |
| commit | 8e8410ce3850a34d8b79431eb89ce4a7b9f89a60 (patch) | |
| tree | 7de96628652c65d149c4e1d09306145a298844ad /internal/pkg/archive.go | |
| parent | 8fb6fdaa80e793928f80c45d058aa5aebf79e78b (diff) | |
internal/pkg: archive unpack artifact
This unpacks an internal/pkg archive stream used in the upcoming mirror service.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pkg/archive.go')
| -rw-r--r-- | internal/pkg/archive.go | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/internal/pkg/archive.go b/internal/pkg/archive.go index 8b5398fa..902d2c31 100644 --- a/internal/pkg/archive.go +++ b/internal/pkg/archive.go @@ -277,3 +277,117 @@ func SumFS(buf *Checksum, fsys fs.FS, root string) error { func SumDir(buf *Checksum, pathname *check.Absolute) error { return SumFS(buf, os.DirFS(pathname.String()), ".") } + +// archiveArtifact is an [Artifact] unpacking an archive supported by [Reader] +// backed by a [FileArtifact]. +type archiveArtifact struct { + // Caller-supplied backing archive. + f Artifact +} + +// NewArchive returns a new [Artifact] backed by the supplied [Artifact]. The +// source [Artifact] must be a [FileArtifact] and produce a stream compatible +// with [Reader]. +func NewArchive(a Artifact) Artifact { + return archiveArtifact{a} +} + +// Kind returns the hardcoded [Kind] constant. +func (archiveArtifact) Kind() Kind { return KindArchive } + +// Params is a noop. +func (archiveArtifact) Params(*IContext) {} + +func init() { + register(KindArchive, func(r *IRReader) Artifact { + a := NewArchive(r.Next()) + if _, ok := r.Finalise(); ok { + panic(ErrUnexpectedChecksum) + } + return a + }) +} + +// Dependencies returns a slice containing the backing file. +func (a archiveArtifact) Dependencies() []Artifact { + return []Artifact{a.f} +} + +// IsExclusive returns false: [Reader] is fully sequential. +func (archiveArtifact) IsExclusive() bool { return false } + +// Cure cures the [Artifact], producing a directory located at work. +func (a archiveArtifact) Cure(t *TContext) (err error) { + var r io.ReadCloser + if r, err = t.Open(a.f); err != nil { + return + } + + defer func() { + closeErr := r.Close() + if err == nil { + err = closeErr + } + }() + + if err = os.MkdirAll(t.GetWorkDir().String(), 0700); err != nil { + return + } + var root *os.Root + if root, err = os.OpenRoot(t.GetWorkDir().String()); err != nil { + return + } + defer func() { + closeErr := root.Close() + if err == nil { + err = closeErr + } + }() + + var header *ArchiveHeader + ar := NewReader(r) + for header, err = ar.Next(); err == nil; header, err = ar.Next() { + if header.Mode.IsRegular() { + var f *os.File + if f, err = root.OpenFile( + header.Path, + os.O_CREATE|os.O_EXCL|os.O_WRONLY, + header.Mode.Perm(), + ); err != nil { + return + } + if _, err = io.Copy(f, ar); err != nil { + _ = f.Close() + return + } else if err = f.Close(); err != nil { + return + } + } else if header.Mode&fs.ModeSymlink != 0 { + var p []byte + if p, err = io.ReadAll(ar); err != nil { + return + } + + if err = root.Symlink( + unsafe.String(unsafe.SliceData(p), len(p)), + header.Path, + ); err != nil { + return + } + } else if header.Mode.IsDir() { + if header.Path == "." { + continue + } + + if err = root.Mkdir(header.Path, header.Mode.Perm()); err != nil { + return + } + } else { + return InvalidFileModeError(header.Mode) + } + } + if errors.Is(err, io.EOF) { + err = nil + } + return +} |
