diff options
Diffstat (limited to 'archive.go')
-rw-r--r-- | archive.go | 124 |
1 files changed, 88 insertions, 36 deletions
@@ -12,7 +12,7 @@ import ( "golang.org/x/sys/unix" ) -type ArchiveReader interface { +type TarReader interface { Next() (*tar.Header, error) Read([]byte) (int, error) } @@ -107,15 +107,78 @@ func tarCreateSymlink(root string, header *tar.Header) error { return err } -type InstallArchiveCfg struct { +type TarFilter interface { + FilterTar(header *tar.Header, r io.Reader) error +} + +type TarFilterFunc func( + header *tar.Header, + r io.Reader, +) error + +func (f TarFilterFunc) FilterTar(h *tar.Header, r io.Reader) error { + return f(h, r) +} + +type TarCfg struct { Root string Chown bool Chmod bool } -func InstallArchive( - r ArchiveReader, - cfg *InstallArchiveCfg, +type TarExtracter struct { + cfg *TarCfg +} + +func NewTarExtractor(cfg *TarCfg) *TarExtracter { + return &TarExtracter{cfg} +} + +func (s *TarExtracter) FilterTar( + header *tar.Header, + r io.Reader, +) error { + target := path.Join(s.cfg.Root, header.Name) + + var err error + switch header.Typeflag { + case tar.TypeBlock: + err = tarCreateBlockDev(target, header) + case tar.TypeChar: + err = tarCreateCharDev(target, header) + case tar.TypeFifo: + err = tarCreateFifo(target, header) + case tar.TypeDir: + err = tarCreateDir(target) + case tar.TypeReg: + err = tarCreateReg(target, r, header) + case tar.TypeLink: + err = tarCreateLink(s.cfg.Root, header) + case tar.TypeSymlink: + err = tarCreateSymlink(s.cfg.Root, header) + default: + err = errors.Errorf("unhandled file type '%c'", header.Typeflag) + } + if err != nil { + return errors.Wrap(err, "unpacking tar archive") + } + + if s.cfg.Chown { + if err = os.Chown(target, header.Uid, header.Gid); err != nil { + fmt.Fprintln(os.Stderr, err) + } + } + if s.cfg.Chmod { + if err = os.Chmod(target, header.FileInfo().Mode()); err != nil { + fmt.Fprintln(os.Stderr, err) + } + } + return nil +} + +func FilterTar( + r TarReader, + filters ...TarFilter, ) error { for { header, err := r.Next() @@ -127,40 +190,29 @@ func InstallArchive( if header == nil { continue } - target := path.Join(cfg.Root, header.Name) - - switch header.Typeflag { - case tar.TypeBlock: - err = tarCreateBlockDev(target, header) - case tar.TypeChar: - err = tarCreateCharDev(target, header) - case tar.TypeFifo: - err = tarCreateFifo(target, header) - case tar.TypeDir: - err = tarCreateDir(target) - case tar.TypeReg: - err = tarCreateReg(target, r, header) - case tar.TypeLink: - err = tarCreateLink(cfg.Root, header) - case tar.TypeSymlink: - err = tarCreateSymlink(cfg.Root, header) - default: - err = errors.Errorf("unhandled file type '%c'", header.Typeflag) - } - if err != nil { - return errors.Wrap(err, "unpacking tar archive") - } - if cfg.Chown { - if err = os.Chown(target, header.Uid, header.Gid); err != nil { - fmt.Fprintln(os.Stderr, err) - } - } - if cfg.Chmod { - if err = os.Chmod(target, header.FileInfo().Mode()); err != nil { - fmt.Fprintln(os.Stderr, err) + + for _, f := range filters { + // TODO: how do we dup r? + err := f.FilterTar(header, r) + if err != nil { + return err } } } return nil } + +func ExtractTar( + cfg *TarCfg, + fp string, +) error { + f, err := os.Open(fp) + if err != nil { + return err + } + defer f.Close() + r := tar.NewReader(f) + err = FilterTar(r, NewTarExtractor(cfg)) + return errors.Wrap(err, "extracting tar file") +} |