Skip to content

Commit

Permalink
Simplify / refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
djdv committed May 8, 2018
1 parent a374e49 commit c34d2ce
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 46 deletions.
66 changes: 28 additions & 38 deletions extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,12 @@ type Extractor struct {
Path string
Progress func(int64) int64

// SanitizePathFunc can be provided if you wish to modify the source path
// The source path is divided by "/" into elements. A legal path base is expected to be returned, it will be joined with the extraction root.
// "extractionRoot/a/b" -> SanitizePathFunc(["a", "b"])
SanitizePathFunc func(pathComponents []string) (joinedComponents string)

// SanitizePathCallback is called with the original input path and the sanitized output path from SanitizePathFunc
// This is useful if you wish to log changes between the source and destination, or fail when encountering an incompatible source->dest
// returning an error will abort the extraction
SanitizePathCallback func(input, output string) (userDefined error)
// SanitizePathFunc can be provided if you wish to inspect and/or modify the source path
// returning an error from this function will abort extraction
SanitizePathFunc func(path string) (saferPath string, userDefined error)

// LinkFunc can be provided for user specified handling of filesystem links
// returning an error from this function aborts extraction
LinkFunc func(Link) error
}

Expand Down Expand Up @@ -73,7 +68,7 @@ func (te *Extractor) Extract(reader io.Reader) error {
return err
}
case tar.TypeSymlink:
if err := te.extractSymlink(header, i); err != nil {
if err := te.extractSymlink(header); err != nil {
return err
}
default:
Expand All @@ -83,41 +78,40 @@ func (te *Extractor) Extract(reader io.Reader) error {
return nil
}

// Sanitize toggles output sanitation, using default sanitation functions
// Sanitize toggles output sanitation, using built in sanitation functions
// (Modify paths to be platform legal, symlinks may not escape extraction root)
func (te *Extractor) Sanitize(toggle bool) {
te.SanitizePathFunc = sanitizePath
te.LinkFunc = func(inLink Link) error {
if err := childrenOnly(inLink); err != nil {
return err
}
if err := platformAnalyzeLink(inLink); err != nil {
if err := platformLink(inLink); err != nil {
return err
}
return os.Symlink(inLink.Target, inLink.Name)
}
}

// outputPath returns the path at which to place tarPath
func (te *Extractor) outputPath(tarPath string) string {
var outPath string
elems := strings.Split(tarPath, "/") // break into elems
elems = elems[1:] // remove original root
func (te *Extractor) outputPath(tarPath string) (outPath string, err error) {
elems := strings.Split(tarPath, "/") // break into elems
elems = elems[1:] // remove original root
outPath = strings.Join(elems, "/") // join elems
outPath = gopath.Join(te.Path, outPath) // rebase on to extraction target root
// sanitize path to be platform legal
if te.SanitizePathFunc != nil {
outPath = te.SanitizePathFunc(elems) // sanitize base path elements to be platform legal
outPath, err = te.SanitizePathFunc(outPath)
} else {
outPath = fp.Join(elems...) // join elems
outPath = fp.FromSlash(outPath)
}
outPath = fp.Join(te.Path, outPath) // rebase on to extraction target root
return outPath
return
}

func (te *Extractor) extractDir(h *tar.Header, depth int) error {
path := te.outputPath(h.Name)
if te.SanitizePathCallback != nil && depth != 0 {
if err := te.SanitizePathCallback(gopath.Join(te.Path, h.Name), path); err != nil {
return err
}
path, err := te.outputPath(h.Name)
if err != nil {
return err
}

if depth == 0 {
Expand All @@ -128,12 +122,10 @@ func (te *Extractor) extractDir(h *tar.Header, depth int) error {
return os.MkdirAll(path, 0755)
}

func (te *Extractor) extractSymlink(h *tar.Header, depth int) error {
path := te.outputPath(h.Name)
if te.SanitizePathCallback != nil && depth != 0 {
if err := te.SanitizePathCallback(h.Name, path); err != nil {
return err
}
func (te *Extractor) extractSymlink(h *tar.Header) error {
path, err := te.outputPath(h.Name)
if err != nil {
return err
}

if te.LinkFunc != nil {
Expand All @@ -144,11 +136,9 @@ func (te *Extractor) extractSymlink(h *tar.Header, depth int) error {
}

func (te *Extractor) extractFile(h *tar.Header, r *tar.Reader, depth int, rootExists bool, rootIsDir bool) error {
path := te.outputPath(h.Name)
if te.SanitizePathCallback != nil && depth != 0 {
if err := te.SanitizePathCallback(h.Name, path); err != nil {
return err
}
path, err := te.outputPath(h.Name)
if err != nil {
return err
}

if depth == 0 { // if depth is 0, this is the only file (we aren't extracting a directory)
Expand Down Expand Up @@ -194,10 +184,10 @@ func copyWithProgress(to io.Writer, from io.Reader, cb func(int64) int64) error
}
}

// childrenOnly is a link-filter base, called before platform specific filters
// childrenOnly will return an error if link targets escape their root
func childrenOnly(inLink Link) error {
if fp.IsAbs(inLink.Target) {
return fmt.Errorf("Link target %q is an absolute path (forbidden)", inLink.Target) //TODO: discuss
return fmt.Errorf("Link target %q is an absolute path (forbidden)", inLink.Target)
}

resolvedTarget := fp.Join(inLink.Name, inLink.Target)
Expand Down
8 changes: 3 additions & 5 deletions sanitize.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,16 @@ package tar

import (
"os"
"path/filepath"
)

func isNullDevice(path string) bool {
return path == os.DevNull
}

func sanitizePath(pathElements []string) (string, error) {
return filepath.Join(pathElements...), nil
func sanitizePath(path string) (string, error) {
return path, nil
}

//func sanitizeLink(targetRoot, link Link) (Link, error) {
func platformAnalyzeLink(inLink Link) error {
func platformLink(inLink Link) error {
return nil
}
8 changes: 5 additions & 3 deletions sanitize_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ func isNullDevice(path string) bool {
return true
}

func sanitizePath(pathElements []string) string {
func sanitizePath(path string) (string, error) {
pathElements := strings.Split(path, "/")

//first pass: strip illegal tail & prefix reserved names `CON .` -> `_CON`
for pi := range pathElements {
pathElements[pi] = strings.TrimRight(pathElements[pi], ". ") //MSDN: Do not end a file or directory name with a space or a period
Expand Down Expand Up @@ -69,10 +71,10 @@ func sanitizePath(pathElements []string) string {
res = builder.String()
}

return filepath.FromSlash(res)
return filepath.FromSlash(res), nil
}

func platformAnalyzeLink(inLink Link) error {
func platformLink(inLink Link) error {
if strings.HasPrefix(inLink.Target, string(os.PathSeparator)) || strings.HasPrefix(inLink.Target, "/") {
return fmt.Errorf("Link target %q is relative to drive root (forbidden)", inLink.Target) //TODO: discuss
}
Expand Down

0 comments on commit c34d2ce

Please sign in to comment.