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 }