apkovl-cli/compression/compression.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)
}