aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/tar.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/pkg/tar.go')
-rw-r--r--internal/pkg/tar.go263
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
}