crfs/stargz/stargzify: add tool to convert a tar.gz to stargz
And in testing converting the Debian base layer I found a hard link,
so add enough hardlink support (mostly in TODO form) for the tool to
run for now. Proper hardlink support later.
Size stats:
-rw-r--r-- 1 bradfitz bradfitz 51354364 Mar 3 03:32 debian.tar.gz
-rw-r--r-- 1 bradfitz bradfitz 55061714 Mar 21 20:37 debian.stargz
About 7.6% bigger. (Acceptable)
Updates golang/go#30829
Change-Id: I4d76850be68d32ea6e8c2bd81c4233df1b5fc7af
Reviewed-on: https://go-review.googlesource.com/c/build/+/168737
Reviewed-by: Jon Johnson <jonjohnson@google.com>
diff --git a/crfs/stargz/stargz.go b/crfs/stargz/stargz.go
index d2983cf..1dd1863 100644
--- a/crfs/stargz/stargz.go
+++ b/crfs/stargz/stargz.go
@@ -107,7 +107,7 @@
// stored in the tar file, not just the base name.
Name string `json:"name"`
- // Type is one of "dir", "reg", "symlink", or "chunk".
+ // Type is one of "dir", "reg", "symlink", "hardlink", or "chunk".
// The "chunk" type is used for regular file data chunks past the first
// TOCEntry; the 2nd chunk and on have only Type ("chunk"), Offset,
// ChunkOffset, and ChunkSize populated.
@@ -122,7 +122,7 @@
ModTime3339 string `json:"modtime,omitempty"`
modTime time.Time
- // LinkName, for symlinks, is the link target.
+ // LinkName, for symlinks and hardlinks, is the link target.
LinkName string `json:"linkName,omitempty"`
// Mode is the permission and mode bits.
@@ -239,6 +239,9 @@
if r == nil {
return
}
+ // TODO: decide at which stage to handle hard links. Probably
+ // here? And it probably needs a link count field stored in
+ // the TOCEntry.
e, ok = r.m[path]
return
}
@@ -488,7 +491,8 @@
}
switch h.Typeflag {
case tar.TypeLink:
- return fmt.Errorf("TODO: unsupported hardlink %q => %q", h.Name, h.Linkname)
+ ent.Type = "hardlink"
+ ent.LinkName = h.Linkname
case tar.TypeSymlink:
ent.Type = "symlink"
ent.LinkName = h.Linkname
diff --git a/crfs/stargz/stargzify/stargzify.go b/crfs/stargz/stargzify/stargzify.go
new file mode 100644
index 0000000..115bb9b
--- /dev/null
+++ b/crfs/stargz/stargzify/stargzify.go
@@ -0,0 +1,70 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The stargzify command converts a tarball into a seekable stargz
+// tarball. The output is still a valid tarball, but has new gzip
+// streams throughout the file and and an Table of Contents (TOC)
+// index at the end pointing into those streams.
+package main
+
+import (
+ "flag"
+ "log"
+ "os"
+ "strings"
+
+ "golang.org/x/build/crfs/stargz"
+)
+
+var (
+ in = flag.String("in", "", "input file in tar or tar.gz format. Use \"-\" for stdin.")
+ out = flag.String("out", "", "output file. If empty, it's the input base + \".stargz\", or stdout if the input is stdin. Use \"-\" for stdout.")
+)
+
+func main() {
+ flag.Parse()
+ var f, fo *os.File // file in, file out
+ var err error
+ switch *in {
+ case "":
+ log.Fatal("missing required --in flag")
+ case "-":
+ f = os.Stdin
+ default:
+ f, err = os.Open(*in)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ defer f.Close()
+
+ if *out == "" {
+ if *in == "-" {
+ *out = "-"
+ } else {
+ base := strings.TrimSuffix(*in, ".gz")
+ base = strings.TrimSuffix(base, ".tgz")
+ base = strings.TrimSuffix(base, ".tar")
+ *out = base + ".stargz"
+ }
+ }
+ if *out == "-" {
+ fo = os.Stdout
+ } else {
+ fo, err = os.Create(*out)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ w := stargz.NewWriter(fo)
+ if err := w.AppendTar(f); err != nil {
+ log.Fatal(err)
+ }
+ if err := w.Close(); err != nil {
+ log.Fatal(err)
+ }
+ if err := fo.Close(); err != nil {
+ log.Fatal(err)
+ }
+}