diff options
Diffstat (limited to 'internal/pkg/tar.go')
| -rw-r--r-- | internal/pkg/tar.go | 263 |
1 files changed, 116 insertions, 147 deletions
diff --git a/internal/pkg/tar.go b/internal/pkg/tar.go index 1483246c..2b9aacdf 100644 --- a/internal/pkg/tar.go +++ b/internal/pkg/tar.go @@ -11,7 +11,6 @@ import ( "io/fs" "net/http" "os" - "sync" "hakurei.app/container/check" ) @@ -25,59 +24,44 @@ const ( // A tarArtifact is an [Artifact] unpacking a tarball backed by a [File]. type tarArtifact struct { - // Computed ahead of time from the checksum of the identifier of f appended - // with parameters of tarArtifact. - id ID - // Caller-supplied backing tarball. f File // Compression on top of the tarball. compression uint64 - - // Populated when submitting to or loading from [Cache]. - pathname *check.Absolute - // Checksum of cured directory. Valid if pathname is not nil. - checksum Checksum - - // Instance of [Cache] to submit the cured artifact to. - c *Cache - // Protects the Pathname critical section. - mu sync.Mutex } // NewTar returns a new [Artifact] backed by the supplied [File] and // compression method. -func (c *Cache) NewTar(f File, compression uint64) Artifact { - return &tarArtifact{id: KindTar.Ident( - binary.LittleEndian.AppendUint64(nil, compression), f, - ), f: f, compression: compression, c: c} +func NewTar(f File, compression uint64) Artifact { + return &tarArtifact{f: f, compression: compression} } // NewHTTPGetTar is abbreviation for NewHTTPGet passed to NewTar. -func (c *Cache) NewHTTPGetTar( +func NewHTTPGetTar( ctx context.Context, hc *http.Client, url string, checksum Checksum, compression uint64, ) (Artifact, error) { - f, err := c.NewHTTPGet(ctx, hc, url, checksum) + f, err := NewHTTPGet(ctx, hc, url, checksum) if err != nil { return nil, err } - return c.NewTar(f, compression), nil + return NewTar(f, compression), nil } // Kind returns the hardcoded [Kind] constant. func (a *tarArtifact) Kind() Kind { return KindTar } -// ID returns the identifier prepared ahead of time. -func (a *tarArtifact) ID() ID { return a.id } +// Params returns compression encoded in little endian. +func (a *tarArtifact) Params() []byte { + return binary.LittleEndian.AppendUint64(nil, a.compression) +} -// Hash cures the [Artifact] and returns its hash. -func (a *tarArtifact) Hash() (Checksum, error) { - _, err := a.Pathname() - return a.checksum, err +// Dependencies returns a slice containing the backing file. +func (a *tarArtifact) Dependencies() []Artifact { + return []Artifact{a.f} } // A DisallowedTypeflagError describes a disallowed typeflag encountered while @@ -88,146 +72,131 @@ func (e DisallowedTypeflagError) Error() string { return "disallowed typeflag '" + string(e) + "'" } -// Pathname cures the [Artifact] and returns its pathname in the [Cache]. -func (a *tarArtifact) Pathname() (*check.Absolute, error) { - a.mu.Lock() - defer a.mu.Unlock() +// Cure cures the [Artifact], producing a directory located at work. +func (a *tarArtifact) Cure(work *check.Absolute, loadData CacheDataFunc) (err error) { + var tr io.ReadCloser - if a.pathname != nil { - return a.pathname, nil + { + var data []byte + data, err = loadData(a.f) + if err != nil { + return + } + tr = io.NopCloser(bytes.NewReader(data)) } - pathname, _, err := a.c.Store(a.id, func(work *check.Absolute) (err error) { - var tr io.ReadCloser - - { - var data []byte - data, err = a.f.Data() - if err != nil { - return - } - tr = io.NopCloser(bytes.NewReader(data)) + defer func() { + closeErr := tr.Close() + if err == nil { + err = closeErr } + }() - defer func() { - closeErr := tr.Close() - if err == nil { - err = closeErr - } - }() - - switch a.compression { - case TarUncompressed: - break - - case TarGzip: - if tr, err = gzip.NewReader(tr); err != nil { - return - } - break - - default: - return os.ErrInvalid - } + switch a.compression { + case TarUncompressed: + break - type dirTargetPerm struct { - path *check.Absolute - mode fs.FileMode + case TarGzip: + if tr, err = gzip.NewReader(tr); err != nil { + return } - var madeDirectories []dirTargetPerm + break - var header *tar.Header - r := tar.NewReader(tr) - for header, err = r.Next(); err == nil; header, err = r.Next() { - typeflag := header.Typeflag - for { - switch typeflag { - case 0: - if len(header.Name) > 0 && header.Name[len(header.Name)-1] == '/' { - typeflag = tar.TypeDir - } else { - typeflag = tar.TypeReg - } - continue + default: + return os.ErrInvalid + } - case tar.TypeReg: - var f *os.File - if f, err = os.OpenFile( - work.Append(header.Name).String(), - os.O_CREATE|os.O_EXCL|os.O_WRONLY, - header.FileInfo().Mode()&0400, - ); err != nil { - return - } - if _, err = io.Copy(f, r); err != nil { - _ = f.Close() - return - } else if err = f.Close(); err != nil { - return - } - break + type dirTargetPerm struct { + path *check.Absolute + mode fs.FileMode + } + var madeDirectories []dirTargetPerm - case tar.TypeLink: - if err = os.Link( - header.Linkname, - work.Append(header.Name).String(), - ); err != nil { - return - } - break + var header *tar.Header + r := tar.NewReader(tr) + for header, err = r.Next(); err == nil; header, err = r.Next() { + typeflag := header.Typeflag + for { + switch typeflag { + case 0: + if len(header.Name) > 0 && header.Name[len(header.Name)-1] == '/' { + typeflag = tar.TypeDir + } else { + typeflag = tar.TypeReg + } + continue - case tar.TypeSymlink: - if err = os.Symlink( - header.Linkname, - work.Append(header.Name).String(), - ); err != nil { - return - } - break + case tar.TypeReg: + var f *os.File + if f, err = os.OpenFile( + work.Append(header.Name).String(), + os.O_CREATE|os.O_EXCL|os.O_WRONLY, + header.FileInfo().Mode()&0400, + ); err != nil { + return + } + if _, err = io.Copy(f, r); err != nil { + _ = f.Close() + return + } else if err = f.Close(); err != nil { + return + } + break - case tar.TypeDir: - pathname := work.Append(header.Name) - madeDirectories = append(madeDirectories, dirTargetPerm{ - path: pathname, - mode: header.FileInfo().Mode(), - }) - if err = os.MkdirAll( - pathname.String(), - 0700, - ); err != nil { - return - } - break + case tar.TypeLink: + if err = os.Link( + header.Linkname, + work.Append(header.Name).String(), + ); err != nil { + return + } + break - case tar.TypeXGlobalHeader: - // ignore - break + case tar.TypeSymlink: + if err = os.Symlink( + header.Linkname, + work.Append(header.Name).String(), + ); err != nil { + return + } + break - default: - return DisallowedTypeflagError(typeflag) + case tar.TypeDir: + pathname := work.Append(header.Name) + madeDirectories = append(madeDirectories, dirTargetPerm{ + path: pathname, + mode: header.FileInfo().Mode(), + }) + if err = os.MkdirAll( + pathname.String(), + 0700, + ); err != nil { + return } + break + case tar.TypeXGlobalHeader: + // ignore break + + default: + return DisallowedTypeflagError(typeflag) } + + break } - if errors.Is(err, io.EOF) { - err = nil - } + } + if errors.Is(err, io.EOF) { + err = nil + } - if err == nil { - for _, e := range madeDirectories { - if err = os.Chmod(e.path.String(), e.mode&0500); err != nil { - return - } + if err == nil { + for _, e := range madeDirectories { + if err = os.Chmod(e.path.String(), e.mode&0500); err != nil { + return } - err = os.Chmod(work.String(), 0500) } - return - }, &a.checksum, false) - if err != nil { - return nil, err + err = os.Chmod(work.String(), 0500) } - - a.pathname = pathname - return pathname, nil + return } |
