154 lines
3.8 KiB
Go
154 lines
3.8 KiB
Go
|
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)
|
||
|
}
|