diff --git a/rootfs.go b/rootfs.go index 03f1efd..39bedc2 100644 --- a/rootfs.go +++ b/rootfs.go @@ -1,7 +1,13 @@ package main import ( + "archive/tar" + "encoding/json" "errors" + "fmt" + "io" + "os" + "strings" goflags "github.com/jessevdk/go-flags" ) @@ -13,9 +19,87 @@ type cmdRootFS struct { } `positional-args:"yes" required:"yes"` } +const ( + _manifestJSON = "manifest.json" +) + func (r *cmdRootFS) Execute(args []string) error { if len(args) != 0 { return errors.New("too many args") } - return nil + + in, err := os.Open(string(r.PositionalArgs.Infile)) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(string(r.PositionalArgs.Outfile)) + if err != nil { + return err + } + defer out.Close() + return r.rootfs(in, out) +} + +type dockerManifestJSON []struct { + Config string `json:"Config"` + Layers []string `json:"Layers"` +} + +// rootfs accepts a docker layer tarball and writes it to outfile. +// 1. create map[string]io.ReadSeeker for each layer. +// 2. parse manifest.json and get the layer order. +// 3. go through each layer in order and write: +// a) to an ordered slice: the file name. +// b) to an FS map: where does the file come from? +// I) layer name +// II) offset (0 being the first file in the layer) +// 4. go through +func (r *cmdRootFS) rootfs(in io.ReadSeeker, out io.Writer) error { + tr := tar.NewReader(in) + layerOffsets := map[string]int64{} + var manifest dockerManifestJSON + + // phase 1: get layer offsets and manifest.json + for { + hdrIn, err := tr.Next() + if err == io.EOF { + break + } + + if hdrIn.Typeflag != tar.TypeReg { + continue + } + + switch { + case hdrIn.Name == _manifestJSON: + dec := json.NewDecoder(tr) + if err := dec.Decode(&manifest); err != nil { + return fmt.Errorf("decode manifest.json: %w", err) + } + case strings.HasSuffix(hdrIn.Name, "/layer.tar"): + here, err := in.Seek(0, io.SeekCurrent) + if err != nil { + return fmt.Errorf("seek: %w", err) + } + layers[hdrIn.Name] = here + } + } + + // phase 1.5: enumerate layers + layers := make([]int64, len(layers)) + for i, name := range manifest.Layers { + layers[i] = layerOffsets[name] + } + + // phase 2: iterate through all layers and extract filenames + // to files and ordered + files := map[string]struct{ + layer string + + fmt.Printf("layers: %+v\n", layers) + + return nil + //return tw.Close() }