Files
SamWaf/utils/archiveutil.go
T
2025-05-01 21:45:01 +08:00

150 lines
3.7 KiB
Go

package utils
import (
"archive/tar"
"archive/zip"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
// Unzip 解压缩zip文件到指定目录
// zipFile: zip文件路径
// destDir: 解压目标目录
func Unzip(zipFile, destDir string) error {
// 打开zip文件
reader, err := zip.OpenReader(zipFile)
if err != nil {
return fmt.Errorf("打开zip文件失败: %v", err)
}
defer reader.Close()
// 确保目标目录存在
if err := os.MkdirAll(destDir, 0755); err != nil {
return fmt.Errorf("创建目标目录失败: %v", err)
}
// 遍历zip文件中的所有文件和目录
for _, file := range reader.File {
// 构建目标路径
path := filepath.Join(destDir, file.Name)
// 检查路径是否在目标目录内(防止zip slip漏洞)
if !strings.HasPrefix(path, filepath.Clean(destDir)+string(os.PathSeparator)) {
return fmt.Errorf("非法的文件路径: %s", file.Name)
}
// 如果是目录,创建它
if file.FileInfo().IsDir() {
if err := os.MkdirAll(path, file.Mode()); err != nil {
return fmt.Errorf("创建目录失败: %v", err)
}
continue
}
// 确保父目录存在
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return fmt.Errorf("创建父目录失败: %v", err)
}
// 创建目标文件
destFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
return fmt.Errorf("创建目标文件失败: %v", err)
}
// 打开源文件
srcFile, err := file.Open()
if err != nil {
destFile.Close()
return fmt.Errorf("打开源文件失败: %v", err)
}
// 复制内容
_, err = io.Copy(destFile, srcFile)
srcFile.Close()
destFile.Close()
if err != nil {
return fmt.Errorf("复制文件内容失败: %v", err)
}
}
return nil
}
// ExtractTarGz 解压缩tar.gz文件到指定目录
// tarGzFile: tar.gz文件路径
// destDir: 解压目标目录
func ExtractTarGz(tarGzFile, destDir string) error {
// 打开tar.gz文件
file, err := os.Open(tarGzFile)
if err != nil {
return fmt.Errorf("打开tar.gz文件失败: %v", err)
}
defer file.Close()
// 创建gzip读取器
gzipReader, err := gzip.NewReader(file)
if err != nil {
return fmt.Errorf("创建gzip读取器失败: %v", err)
}
defer gzipReader.Close()
// 创建tar读取器
tarReader := tar.NewReader(gzipReader)
// 确保目标目录存在
if err := os.MkdirAll(destDir, 0755); err != nil {
return fmt.Errorf("创建目标目录失败: %v", err)
}
// 遍历tar文件中的所有文件和目录
for {
header, err := tarReader.Next()
if err == io.EOF {
break // 文件结束
}
if err != nil {
return fmt.Errorf("读取tar文件失败: %v", err)
}
// 构建目标路径
path := filepath.Join(destDir, header.Name)
// 检查路径是否在目标目录内(防止zip slip漏洞)
if !strings.HasPrefix(path, filepath.Clean(destDir)+string(os.PathSeparator)) {
return fmt.Errorf("非法的文件路径: %s", header.Name)
}
switch header.Typeflag {
case tar.TypeDir: // 目录
if err := os.MkdirAll(path, 0755); err != nil {
return fmt.Errorf("创建目录失败: %v", err)
}
case tar.TypeReg: // 普通文件
// 确保父目录存在
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return fmt.Errorf("创建父目录失败: %v", err)
}
// 创建目标文件
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode))
if err != nil {
return fmt.Errorf("创建目标文件失败: %v", err)
}
// 复制内容
if _, err := io.Copy(file, tarReader); err != nil {
file.Close()
return fmt.Errorf("复制文件内容失败: %v", err)
}
file.Close()
}
}
return nil
}