1
2
mirror of https://github.com/yi-ge/unxz synced 2020-06-03 17:20:45 +00:00
unxz/unxz.go
2019-08-07 20:40:32 +08:00

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
}
}