mirror of
https://github.com/ipfs/kubo.git
synced 2025-05-17 15:06:47 +08:00
Merge pull request #4857 from ipfs/extract/thirdparty-tar
Extract: thirdparty/tar
This commit is contained in:
@ -13,9 +13,9 @@ import (
|
||||
e "github.com/ipfs/go-ipfs/core/commands/e"
|
||||
dag "github.com/ipfs/go-ipfs/merkledag"
|
||||
path "github.com/ipfs/go-ipfs/path"
|
||||
tar "github.com/ipfs/go-ipfs/thirdparty/tar"
|
||||
uarchive "github.com/ipfs/go-ipfs/unixfs/archive"
|
||||
|
||||
tar "gx/ipfs/QmYk64JEF4QWPB9Kqib63g8vfYfv78AmSUyeFaMaX6F9vQ/tar-utils"
|
||||
"gx/ipfs/QmceUdzxkimdYsgtX733uNgzf1DLHyBKN6ehGSp85ayppM/go-ipfs-cmdkit"
|
||||
"gx/ipfs/QmeWjRodbcZFKe5tMN7poEx3izym6osrLSnTLf9UjJZBbs/pb"
|
||||
"gx/ipfs/QmfAkMSt9Fwzk48QDJecPcwCUjnf2uG7MLnmCGTp4C6ouL/go-ipfs-cmds"
|
||||
|
@ -575,6 +575,12 @@
|
||||
"hash": "QmdbxjQWogRCHRaxhhGnYdT1oQJzL9GdqSKzCdqWr85AP2",
|
||||
"name": "pubsub",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmYk64JEF4QWPB9Kqib63g8vfYfv78AmSUyeFaMaX6F9vQ",
|
||||
"name": "tar-utils",
|
||||
"version": "0.0.2"
|
||||
}
|
||||
],
|
||||
"gxVersion": "0.10.0",
|
||||
|
132
thirdparty/tar/extractor.go
vendored
132
thirdparty/tar/extractor.go
vendored
@ -1,132 +0,0 @@
|
||||
package tar
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
gopath "path"
|
||||
fp "path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Extractor struct {
|
||||
Path string
|
||||
Progress func(int64) int64
|
||||
}
|
||||
|
||||
func (te *Extractor) Extract(reader io.Reader) error {
|
||||
tarReader := tar.NewReader(reader)
|
||||
|
||||
// Check if the output path already exists, so we know whether we should
|
||||
// create our output with that name, or if we should put the output inside
|
||||
// a preexisting directory
|
||||
rootExists := true
|
||||
rootIsDir := false
|
||||
if stat, err := os.Stat(te.Path); err != nil && os.IsNotExist(err) {
|
||||
rootExists = false
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else if stat.IsDir() {
|
||||
rootIsDir = true
|
||||
}
|
||||
|
||||
// files come recursively in order (i == 0 is root directory)
|
||||
for i := 0; ; i++ {
|
||||
header, err := tarReader.Next()
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
if header == nil || err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
if err := te.extractDir(header, i); err != nil {
|
||||
return err
|
||||
}
|
||||
case tar.TypeReg:
|
||||
if err := te.extractFile(header, tarReader, i, rootExists, rootIsDir); err != nil {
|
||||
return err
|
||||
}
|
||||
case tar.TypeSymlink:
|
||||
if err := te.extractSymlink(header); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unrecognized tar header type: %d", header.Typeflag)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// outputPath returns the path at whicht o place tarPath
|
||||
func (te *Extractor) outputPath(tarPath string) string {
|
||||
elems := strings.Split(tarPath, "/") // break into elems
|
||||
elems = elems[1:] // remove original root
|
||||
|
||||
path := fp.Join(elems...) // join elems
|
||||
path = fp.Join(te.Path, path) // rebase on extractor root
|
||||
return path
|
||||
}
|
||||
|
||||
func (te *Extractor) extractDir(h *tar.Header, depth int) error {
|
||||
path := te.outputPath(h.Name)
|
||||
|
||||
if depth == 0 {
|
||||
// if this is the root root directory, use it as the output path for remaining files
|
||||
te.Path = path
|
||||
}
|
||||
|
||||
return os.MkdirAll(path, 0755)
|
||||
}
|
||||
|
||||
func (te *Extractor) extractSymlink(h *tar.Header) error {
|
||||
return os.Symlink(h.Linkname, te.outputPath(h.Name))
|
||||
}
|
||||
|
||||
func (te *Extractor) extractFile(h *tar.Header, r *tar.Reader, depth int, rootExists bool, rootIsDir bool) error {
|
||||
path := te.outputPath(h.Name)
|
||||
|
||||
if depth == 0 { // if depth is 0, this is the only file (we aren't 'ipfs get'ing a directory)
|
||||
if rootExists && rootIsDir {
|
||||
// putting file inside of a root dir.
|
||||
fnameo := gopath.Base(h.Name)
|
||||
fnamen := fp.Base(path)
|
||||
// add back original name if lost.
|
||||
if fnameo != fnamen {
|
||||
path = fp.Join(path, fnameo)
|
||||
}
|
||||
} // else if old file exists, just overwrite it.
|
||||
}
|
||||
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return copyWithProgress(file, r, te.Progress)
|
||||
}
|
||||
|
||||
func copyWithProgress(to io.Writer, from io.Reader, cb func(int64) int64) error {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
n, err := from.Read(buf)
|
||||
if n != 0 {
|
||||
cb(int64(n))
|
||||
_, err2 := to.Write(buf[:n])
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user