package archiver import ( "archive/tar" "compress/gzip" "fmt" "io" "os" "path/filepath" "strings" ) // TarGz creates a .tar.gz file at targzPath containing // the contents of files listed in filePaths. File paths // can be those of regular files or directories. Regular // files are stored at the 'root' of the archive, and // directories are recursively added. func TarGz(targzPath string, filePaths []string) error { out, err := os.Create(targzPath) if err != nil { return fmt.Errorf("error creating %s: %v", targzPath, err) } defer out.Close() gzWriter := gzip.NewWriter(out) defer gzWriter.Close() tarWriter := tar.NewWriter(gzWriter) defer tarWriter.Close() for _, fpath := range filePaths { err := tarGzFile(tarWriter, fpath) if err != nil { return err } } return nil } func tarGzFile(tarWriter *tar.Writer, source string) error { sourceInfo, err := os.Stat(source) if err != nil { return fmt.Errorf("%s: stat: %v", source, err) } var baseDir string if sourceInfo.IsDir() { baseDir = filepath.Base(source) } return filepath.Walk(source, func(path string, info os.FileInfo, err error) error { if err != nil { return fmt.Errorf("error walking to %s: %v", path, err) } header, err := tar.FileInfoHeader(info, path) if err != nil { return fmt.Errorf("%s: making header: %v", path, err) } if baseDir != "" { header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source)) } if info.IsDir() { header.Name += "/" } err = tarWriter.WriteHeader(header) if err != nil { return fmt.Errorf("%s: writing header: %v", path, err) } if info.IsDir() { return nil } if header.Typeflag == tar.TypeReg { file, err := os.Open(path) if err != nil { return fmt.Errorf("%s: open: %v", path, err) } defer file.Close() _, err = io.Copy(tarWriter, file) if err != nil { return fmt.Errorf("%s: copying contents: %v", path, err) } } return nil }) } // UntarGz untars source and decompresses the contents into destination. func UntarGz(source, destination string) error { f, err := os.Open(source) if err != nil { return fmt.Errorf("%s: failed to open archive: %v", source, err) } defer f.Close() gzf, err := gzip.NewReader(f) if err != nil { return fmt.Errorf("%s: create new gzip reader: %v", source, err) } defer gzf.Close() tr := tar.NewReader(gzf) for { header, err := tr.Next() if err == io.EOF { break } else if err != nil { return err } if err := untarGzFile(tr, header, destination); err != nil { return err } } return nil } func untarGzFile(tr *tar.Reader, header *tar.Header, destination string) error { switch header.Typeflag { case tar.TypeDir: return mkdir(filepath.Join(destination, header.Name)) case tar.TypeReg: return writeNewFile(filepath.Join(destination, header.Name), tr, header.FileInfo().Mode()) case tar.TypeSymlink: return writeNewSymbolicLink(filepath.Join(destination, header.Name), header.Linkname) default: return fmt.Errorf("%s: unknown type flag: %c", header.Name, header.Typeflag) } }