package compression import ( "archive/tar" "compress/gzip" "fmt" "io" "os" "path/filepath" "strings" log "github.com/s00500/env_logger" ) func isSymlink(fi os.FileInfo) bool { return fi.Mode()&os.ModeSymlink != 0 } func Compress(inputPath, outputPath string) error { var file *os.File var err error var writer *gzip.Writer if file, err = os.OpenFile(outputPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644); err != nil { return err } defer file.Close() if writer, err = gzip.NewWriterLevel(file, gzip.BestCompression); err != nil { return err } defer writer.Close() tw := tar.NewWriter(writer) defer tw.Close() err = filepath.Walk(inputPath, func(file string, fi os.FileInfo, err error) error { log.Should(err) if inputPath == file { return nil } // Skip some strange systemfiles if strings.Contains(file, ".DS_Store") || strings.Contains(file, ".Trash") || strings.Contains(file, "._") { return nil } log.Trace("Compressing ", inputPath, " : Got: ", file) // generate tar header header, err := tar.FileInfoHeader(fi, file) // set to numeric only if err != nil { return err } if isSymlink(fi) { linkTarget, err := os.Readlink(file) if err != nil { return fmt.Errorf("%s: readlink: %v", fi.Name(), err) } header, err = tar.FileInfoHeader(fi, filepath.ToSlash(linkTarget)) if err != nil { return fmt.Errorf("%s: making header: %v", fi.Name(), err) } } header.Name = "./" + strings.TrimPrefix(filepath.ToSlash(file), filepath.ToSlash(inputPath)+"/") header.Uname = "" header.Gname = "" // write header if err := tw.WriteHeader(header); err != nil { return err } // if not a dir, write file content if !fi.IsDir() && !isSymlink(fi) { data, err := os.Open(file) if err != nil { return err } if _, err := io.Copy(tw, data); err != nil { return err } } return nil }) return err } func ExtractTarGz(gzipStream io.Reader, extractDir string) error { uncompressedStream, err := gzip.NewReader(gzipStream) if err != nil { return fmt.Errorf("extractTarGz: NewReader failed: %w", err) } tarReader := tar.NewReader(uncompressedStream) for { header, err := tarReader.Next() if err == io.EOF { break } if err != nil { return fmt.Errorf("extractTarGz: Next() failed: %w", err) } switch header.Typeflag { case tar.TypeDir: //log.Info("DIr!", header.Name) if err := os.Mkdir(filepath.Join(extractDir, header.Name), 0755); err != nil { return fmt.Errorf("extractTarGz: Mkdir() failed: %w", err) } case tar.TypeReg: //log.Info("File!", header.Name) if err := os.MkdirAll(filepath.Join(extractDir, filepath.Dir(header.Name)), 0755); err != nil { return fmt.Errorf("extractTarGz: File Mkdir() failed: %w", err) } outFile, err := os.OpenFile(filepath.Join(extractDir, header.Name), os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) if err != nil { return fmt.Errorf("extractTarGz: OpenFile() failed: %w", err) } if _, err := io.Copy(outFile, tarReader); err != nil { return fmt.Errorf("extractTarGz: Copy() failed: %w", err) } outFile.Close() case tar.TypeSymlink: if err := os.MkdirAll(filepath.Join(extractDir, filepath.Dir(header.Name)), 0755); err != nil { return fmt.Errorf("extractTarGz: File Mkdir() failed: %w", err) } if err := os.Symlink(header.Linkname, filepath.Join(extractDir, header.Name)); err != nil { log.Errorf("failed writing symbolic link: %s", err) //return fmt.Errorf("failed writing symbolic link: %s", err) } //log.Infof("Skip symlink in: %b in %s", // header.Typeflag, // header.Name) default: return fmt.Errorf("extractTarGz: uknown type: %d in %s", header.Typeflag, header.Name) } } return nil } func Exists(name string) bool { _, err := os.Stat(name) return !os.IsNotExist(err) }