* Copyright (c) 2025 Bocloud Technologies Co., Ltd.
* installer is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain n copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
******************************************************************/
package utils
import (
"archive/tar"
"compress/gzip"
"fmt"
goutils "go-frpc/utils"
"io"
"os"
"path/filepath"
"gopkg.openfuyao.cn/bkeadm/utils/log"
)
func Tar(src, dst string, absolute bool) error {
fw, err := createTarFile(dst)
if err != nil {
return err
}
defer fw.Close()
gw, tw, err := createCompressedWriter(fw)
if err != nil {
return err
}
defer gw.Close()
defer tw.Close()
return packDirectory(src, absolute, tw)
}
func createTarFile(dst string) (*os.File, error) {
fw, err := os.Create(dst)
if err != nil {
return nil, fmt.Errorf("failed to create tar file %s: %w", dst, err)
}
if err := fw.Chmod(DefaultFilePermission); err != nil {
log.Warnf("failed to set file permission for %s: %v", dst, err)
}
return fw, nil
}
func createCompressedWriter(fw *os.File) (*gzip.Writer, *tar.Writer, error) {
gw := gzip.NewWriter(fw)
tw := tar.NewWriter(gw)
return gw, tw, nil
}
func packDirectory(src string, absolute bool, tw *tar.Writer) error {
return walkWithHandler(src,
func(fileName string, fi os.FileInfo) error {
return packFile(src, fileName, fi, absolute, tw)
},
defaultErrorHandler,
)
}
func walkWithHandler(root string, fn func(string, os.FileInfo) error, errFn func(error) error) error {
if errFn == nil {
errFn = func(err error) error { return err }
}
return filepath.Walk(root, func(fileName string, fi os.FileInfo, err error) error {
if err != nil {
return errFn(err)
}
return fn(fileName, fi)
})
}
func defaultErrorHandler(err error) error {
return err
}
func packFile(src, fileName string, fi os.FileInfo, absolute bool, tw *tar.Writer) error {
hdr, err := createTarHeader(src, fileName, fi, absolute)
if err != nil {
return err
}
if err := tw.WriteHeader(hdr); err != nil {
return fmt.Errorf("failed to write tar header for %s: %w", fileName, err)
}
if fi.Mode().IsRegular() {
if err := writeFileContent(fileName, tw); err != nil {
return err
}
}
log.Debugf("Packaged %s successfully\n", fileName)
return nil
}
func createTarHeader(src, fileName string, fi os.FileInfo, absolute bool) (*tar.Header, error) {
hdr, err := tar.FileInfoHeader(fi, "")
if err != nil {
return nil, fmt.Errorf("failed to create tar header for %s: %w", fileName, err)
}
if absolute {
hdr.Name = filepath.ToSlash(fileName)
} else {
relPath, err := filepath.Rel(src, fileName)
if err != nil {
return nil, fmt.Errorf("failed to get relative path for %s: %w", fileName, err)
}
hdr.Name = filepath.ToSlash(relPath)
}
return hdr, nil
}
func writeFileContent(fileName string, tw *tar.Writer) error {
fr, err := os.Open(fileName)
if err != nil {
return fmt.Errorf("failed to open file %s: %w", fileName, err)
}
defer fr.Close()
if _, err := io.Copy(tw, fr); err != nil {
return fmt.Errorf("failed to copy file %s to tar: %w", fileName, err)
}
return nil
}
func UnTar(src, dst string) error {
return goutils.UnTar(src, dst, false)
}