github.com/sashka/siva@v1.6.0/cmd/siva/unpack.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"regexp"
     9  	"strings"
    10  
    11  	"gopkg.in/src-d/go-siva.v1"
    12  
    13  	"github.com/dustin/go-humanize"
    14  )
    15  
    16  const writeFlagsDefault = os.O_WRONLY | os.O_CREATE | os.O_TRUNC | os.O_EXCL
    17  const writeFlagsOverwrite = os.O_WRONLY | os.O_CREATE | os.O_TRUNC
    18  
    19  type CmdUnpack struct {
    20  	cmd
    21  	Overwrite   bool   `short:"o" description:"Overwrites the files if already exists"`
    22  	IgnorePerms bool   `short:"i" description:"Ignore files permisisions"`
    23  	Match       string `short:"m" description:"Only extract files matching the given regexp"`
    24  
    25  	Output struct {
    26  		Path string `positional-arg-name:"target" description:"taget directory"`
    27  	} `positional-args:"yes"`
    28  
    29  	flags        int
    30  	regexp       *regexp.Regexp
    31  	matchingFunc func(string) bool
    32  }
    33  
    34  func (c *CmdUnpack) Execute(args []string) error {
    35  	if err := c.validate(); err != nil {
    36  		return err
    37  	}
    38  
    39  	if err := c.buildReader(); err != nil {
    40  		return err
    41  	}
    42  
    43  	defer c.close()
    44  	return c.do()
    45  }
    46  
    47  func (c *CmdUnpack) validate() error {
    48  	err := c.cmd.validate()
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	if _, err := os.Stat(c.Args.File); err != nil {
    54  		return fmt.Errorf("Invalid input file %q, %s\n", c.Args.File, err)
    55  	}
    56  
    57  	if c.Output.Path == "" {
    58  		c.Output.Path = "."
    59  	}
    60  
    61  	c.flags = writeFlagsDefault
    62  	if c.Overwrite {
    63  		c.flags = writeFlagsOverwrite
    64  	}
    65  
    66  	return c.buildMatchingFunc()
    67  }
    68  
    69  func (c *CmdUnpack) buildMatchingFunc() error {
    70  	c.matchingFunc = func(string) bool { return true }
    71  	if c.Match == "" {
    72  		return nil
    73  	}
    74  
    75  	var err error
    76  	c.regexp, err = regexp.Compile(c.Match)
    77  	if err != nil {
    78  		return fmt.Errorf("Invalid match regexp %q, %s\n", c.Match, err.Error())
    79  	}
    80  
    81  	c.matchingFunc = func(name string) bool {
    82  		return c.regexp.MatchString(name)
    83  	}
    84  
    85  	return nil
    86  }
    87  
    88  func (c *CmdUnpack) do() error {
    89  	i, err := c.r.Index()
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	for _, entry := range i.Filter() {
    95  		if !c.matchingFunc(entry.Name) {
    96  			continue
    97  		}
    98  
    99  		if err := c.extract(entry); err != nil {
   100  			return err
   101  		}
   102  	}
   103  
   104  	return nil
   105  }
   106  
   107  func (c *CmdUnpack) extract(entry *siva.IndexEntry) error {
   108  	src, err := c.r.Get(entry)
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	dst, err := c.createFile(entry)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	defer dst.Close()
   119  
   120  	if _, err := io.Copy(dst, src); err != nil {
   121  		return fmt.Errorf("unable to write %q : %s\n", entry.Name, err)
   122  	}
   123  
   124  	c.println(entry.Name, humanize.Bytes(entry.Size))
   125  	return nil
   126  }
   127  
   128  func (c *CmdUnpack) createFile(entry *siva.IndexEntry) (*os.File, error) {
   129  	dstName := filepath.Join(c.Output.Path, entry.Name)
   130  
   131  	if err := c.checkSafePath(c.Output.Path, dstName); err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	dir := filepath.Dir(dstName)
   136  	if err := os.MkdirAll(dir, 0755); err != nil {
   137  		return nil, fmt.Errorf("unable to create dir %q: %s\n", dir, err)
   138  	}
   139  
   140  	perms := os.FileMode(defaultPerms)
   141  	if !c.IgnorePerms {
   142  		perms = entry.Mode.Perm()
   143  	}
   144  
   145  	dst, err := os.OpenFile(dstName, c.flags, perms)
   146  	if err != nil {
   147  		return nil, fmt.Errorf("unable to open %q for writing: %s\n", dstName, err)
   148  	}
   149  
   150  	return dst, nil
   151  }
   152  
   153  func (c *CmdUnpack) checkSafePath(base, target string) error {
   154  	rel, err := filepath.Rel(base, target)
   155  	if err != nil {
   156  		return fmt.Errorf("target path (%s) is not relative to base (%s): %s\n",
   157  			target, base, err)
   158  	}
   159  
   160  	rel = filepath.ToSlash(rel)
   161  	if strings.HasPrefix(rel, "../") {
   162  		return fmt.Errorf("target path (%s) outside base (%s) is not allowed",
   163  			target, base)
   164  	}
   165  
   166  	return nil
   167  }