171 lines
2.8 KiB
Go
171 lines
2.8 KiB
Go
package unxz
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bufio"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/ulikunitz/xz"
|
|
)
|
|
|
|
var (
|
|
linkFiles = []linkFile{}
|
|
errHeaderType = errors.New("unxz: invalid tar header type")
|
|
)
|
|
|
|
// Unxz - Unxz struct.
|
|
type Unxz struct {
|
|
Src string
|
|
Dest string
|
|
}
|
|
|
|
type linkFile struct {
|
|
filePath string
|
|
targetPath string
|
|
}
|
|
|
|
// New - Create a new Unxz.
|
|
func New(src string, dest string) Unxz {
|
|
return Unxz{src, dest}
|
|
}
|
|
|
|
// Extract - Extract *.tar.xz package.
|
|
func (uz Unxz) Extract() error {
|
|
destPath := uz.Dest
|
|
|
|
xzFile, err := os.Open(uz.Src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer xzFile.Close()
|
|
|
|
r := bufio.NewReader(xzFile)
|
|
xr, err := xz.NewReader(r)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tr := tar.NewReader(xr)
|
|
|
|
os.Mkdir(destPath, 0755)
|
|
|
|
for {
|
|
hdr, err := tr.Next()
|
|
|
|
if err == io.EOF {
|
|
break
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = untarFile(tr, hdr, destPath, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for _, item := range linkFiles {
|
|
err = writeLink(item.filePath, item.targetPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeFile(filePath string, in io.Reader, fm os.FileMode) error {
|
|
err := os.MkdirAll(filepath.Dir(filePath), 0755)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
out, err := os.Create(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
|
|
err = out.Chmod(fm)
|
|
if err != nil && runtime.GOOS != "windows" {
|
|
return err
|
|
}
|
|
|
|
_, err = io.Copy(out, in)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeSymbolicLink(filePath string, targetPath string) error {
|
|
err := os.MkdirAll(filepath.Dir(filePath), 0755)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = os.Symlink(targetPath, filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeLink(filePath string, targetPath string) error {
|
|
err := os.MkdirAll(filepath.Dir(filePath), 0755)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = os.Link(targetPath, filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func untarFile(tr *tar.Reader, header *tar.Header, destPath string, stripInnerFolder bool) error {
|
|
fileName := header.Name
|
|
filePath := filepath.Join(destPath, fileName)
|
|
|
|
if stripInnerFolder {
|
|
slashIndex := strings.Index(fileName, "/")
|
|
if slashIndex != -1 {
|
|
fileName = fileName[slashIndex+1:]
|
|
}
|
|
}
|
|
|
|
switch header.Typeflag {
|
|
case tar.TypeDir:
|
|
err := os.MkdirAll(filePath, 0755)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
case tar.TypeReg, tar.TypeRegA:
|
|
return writeFile(filePath, tr, header.FileInfo().Mode())
|
|
case tar.TypeLink:
|
|
linkFiles = append(linkFiles, linkFile{
|
|
filePath,
|
|
filepath.Join(destPath, header.Linkname),
|
|
})
|
|
return nil
|
|
case tar.TypeSymlink:
|
|
return writeSymbolicLink(filePath, header.Linkname)
|
|
case tar.TypeXGlobalHeader:
|
|
return nil
|
|
default:
|
|
return errHeaderType
|
|
}
|
|
}
|