Add cruntar - untar for ChromeOS.
This commit is contained in:
parent
1df0350fc7
commit
f44bbc9eca
|
@ -19,6 +19,8 @@ Contents:
|
||||||
the time to expiry and checking for revocations.
|
the time to expiry and checking for revocations.
|
||||||
clustersh/ Run commands or transfer files across multiple
|
clustersh/ Run commands or transfer files across multiple
|
||||||
servers via SSH.
|
servers via SSH.
|
||||||
|
cruntar/ Untar an archive with hard links, copying instead of
|
||||||
|
linking.
|
||||||
csrpubdump/ Dump the public key from an X.509 certificate request.
|
csrpubdump/ Dump the public key from an X.509 certificate request.
|
||||||
fragment/ Print a fragment of a file.
|
fragment/ Print a fragment of a file.
|
||||||
jlp/ JSON linter/prettifier.
|
jlp/ JSON linter/prettifier.
|
||||||
|
@ -47,7 +49,7 @@ Contents:
|
||||||
testio/ Various I/O utilities useful during testing.
|
testio/ Various I/O utilities useful during testing.
|
||||||
testutil/ Various utility functions useful during testing.
|
testutil/ Various utility functions useful during testing.
|
||||||
|
|
||||||
|
|
||||||
Each program should have a small README in the directory with more
|
Each program should have a small README in the directory with more
|
||||||
information.
|
information.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
ChromeOS untar
|
||||||
|
|
||||||
|
This is a tool that is intended to support untarring on SquashFS file
|
||||||
|
systems. In particular, every time it encounters a hard link, it
|
||||||
|
will just create a copy of the file.
|
||||||
|
|
||||||
|
Usage: cruntar [-jmvpz] archive [dest]
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-a Shortcut for -m -p: preserve owners and file mode.
|
||||||
|
-j The archive is compressed with bzip2.
|
||||||
|
-m Preserve file modes.
|
||||||
|
-p Preserve ownership.
|
||||||
|
-v Print the name of each file as it is being processed.
|
||||||
|
-z The archive is compressed with gzip.
|
||||||
|
|
||||||
|
I wrote this after running into problems with untarring the
|
||||||
|
gcc-arm-eabi-none toolchain. The shared storage in Termux under
|
||||||
|
ChromeOS doesn't support hard links, so I opted to just make a copy
|
||||||
|
rather than dealing with links and whatnot.
|
|
@ -0,0 +1,265 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"compress/bzip2"
|
||||||
|
"compress/gzip"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/kisom/goutils/die"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
preserveOwners bool
|
||||||
|
preserveMode bool
|
||||||
|
verbose bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupFile(hdr *tar.Header, file *os.File) error {
|
||||||
|
if preserveMode {
|
||||||
|
if verbose {
|
||||||
|
fmt.Printf("\tchmod %0#o\n", hdr.Mode)
|
||||||
|
}
|
||||||
|
err := file.Chmod(os.FileMode(hdr.Mode))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if preserveOwners {
|
||||||
|
fmt.Printf("\tchown %d:%d\n", hdr.Uid, hdr.Gid)
|
||||||
|
err := file.Chown(hdr.Uid, hdr.Gid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkTarget(target, top string) string {
|
||||||
|
if filepath.IsAbs(target) {
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Clean(filepath.Join(target, top))
|
||||||
|
}
|
||||||
|
|
||||||
|
func processFile(tfr *tar.Reader, hdr *tar.Header, top string) error {
|
||||||
|
if verbose {
|
||||||
|
fmt.Println(hdr.Name)
|
||||||
|
}
|
||||||
|
filePath := filepath.Clean(filepath.Join(top, hdr.Name))
|
||||||
|
switch hdr.Typeflag {
|
||||||
|
case tar.TypeReg, tar.TypeRegA:
|
||||||
|
file, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(file, tfr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupFile(hdr, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case tar.TypeLink:
|
||||||
|
file, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
source, err := os.Open(hdr.Linkname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(file, source)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupFile(hdr, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case tar.TypeSymlink:
|
||||||
|
err := os.Symlink(linkTarget(hdr.Linkname, top), filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case tar.TypeDir:
|
||||||
|
err := os.MkdirAll(filePath, os.FileMode(hdr.Mode))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var compression = map[string]bool{
|
||||||
|
"gzip": false,
|
||||||
|
"bzip2": false,
|
||||||
|
}
|
||||||
|
|
||||||
|
type bzipCloser struct {
|
||||||
|
r io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (brc *bzipCloser) Read(p []byte) (int, error) {
|
||||||
|
return brc.r.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (brc *bzipCloser) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBzipCloser(r io.ReadCloser) (io.ReadCloser, error) {
|
||||||
|
br := bzip2.NewReader(r)
|
||||||
|
return &bzipCloser{r: br}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var compressFuncs = map[string]func(io.ReadCloser) (io.ReadCloser, error){
|
||||||
|
"gzip": func(r io.ReadCloser) (io.ReadCloser, error) { return gzip.NewReader(r) },
|
||||||
|
"bzip2": newBzipCloser,
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyCompression() bool {
|
||||||
|
var compressed bool
|
||||||
|
for _, v := range compression {
|
||||||
|
if compressed && v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
compressed = compressed || v
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getReader(r io.ReadCloser) (io.ReadCloser, error) {
|
||||||
|
for c, v := range compression {
|
||||||
|
if v {
|
||||||
|
return compressFuncs[c](r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func openArchive(path string) (io.ReadCloser, error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := getReader(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var compressFlags struct {
|
||||||
|
z bool
|
||||||
|
j bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCompressFlags() error {
|
||||||
|
if compressFlags.z {
|
||||||
|
compression["gzip"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if compressFlags.j {
|
||||||
|
compression["bzip2"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !verifyCompression() {
|
||||||
|
return errors.New("multiple compression formats specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func usage(w io.Writer) {
|
||||||
|
fmt.Fprintf(w, `ChromeOS untar
|
||||||
|
|
||||||
|
This is a tool that is intended to support untarring on SquashFS file
|
||||||
|
systems. In particular, every time it encounters a hard link, it
|
||||||
|
will just create a copy of the file.
|
||||||
|
|
||||||
|
Usage: cruntar [-jmvpz] archive [dest]
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-a Shortcut for -m -p: preserve owners and file mode.
|
||||||
|
-j The archive is compressed with bzip2.
|
||||||
|
-m Preserve file modes.
|
||||||
|
-p Preserve ownership.
|
||||||
|
-v Print the name of each file as it is being processed.
|
||||||
|
-z The archive is compressed with gzip.
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.Usage = func() { usage(os.Stderr) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var archive, help bool
|
||||||
|
flag.BoolVar(&archive, "a", false, "Shortcut for -m -p: preserve owners and file mode.")
|
||||||
|
flag.BoolVar(&help, "h", false, "print a help message")
|
||||||
|
flag.BoolVar(&compressFlags.j, "j", false, "bzip2 compression")
|
||||||
|
flag.BoolVar(&preserveMode, "m", false, "preserve file modes")
|
||||||
|
flag.BoolVar(&preserveOwners, "p", false, "preserve ownership")
|
||||||
|
flag.BoolVar(&verbose, "v", false, "verbose mode")
|
||||||
|
flag.BoolVar(&compressFlags.z, "z", false, "gzip compression")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if help {
|
||||||
|
usage(os.Stdout)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if archive {
|
||||||
|
preserveMode = true
|
||||||
|
preserveOwners = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err := parseCompressFlags()
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
if flag.NArg() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
top := "./"
|
||||||
|
if flag.NArg() > 1 {
|
||||||
|
top = flag.Arg(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := openArchive(flag.Arg(0))
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
tfr := tar.NewReader(r)
|
||||||
|
for {
|
||||||
|
hdr, err := tfr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
err = processFile(tfr, hdr, top)
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Close()
|
||||||
|
}
|
Loading…
Reference in New Issue