github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/cmd/go/internal/modfetch/unzip.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package modfetch 6 7 import ( 8 "archive/zip" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "path" 14 "path/filepath" 15 "sort" 16 "strings" 17 18 "cmd/go/internal/modfetch/codehost" 19 "cmd/go/internal/module" 20 "cmd/go/internal/str" 21 ) 22 23 func Unzip(dir, zipfile, prefix string, maxSize int64) error { 24 if maxSize == 0 { 25 maxSize = codehost.MaxZipFile 26 } 27 28 // Directory can exist, but must be empty. 29 // except maybe 30 files, _ := ioutil.ReadDir(dir) 31 if len(files) > 0 { 32 return fmt.Errorf("target directory %v exists and is not empty", dir) 33 } 34 if err := os.MkdirAll(dir, 0777); err != nil { 35 return err 36 } 37 38 f, err := os.Open(zipfile) 39 if err != nil { 40 return err 41 } 42 defer f.Close() 43 info, err := f.Stat() 44 if err != nil { 45 return err 46 } 47 48 z, err := zip.NewReader(f, info.Size()) 49 if err != nil { 50 return fmt.Errorf("unzip %v: %s", zipfile, err) 51 } 52 53 foldPath := make(map[string]string) 54 var checkFold func(string) error 55 checkFold = func(name string) error { 56 fold := str.ToFold(name) 57 if foldPath[fold] == name { 58 return nil 59 } 60 dir := path.Dir(name) 61 if dir != "." { 62 if err := checkFold(dir); err != nil { 63 return err 64 } 65 } 66 if foldPath[fold] == "" { 67 foldPath[fold] = name 68 return nil 69 } 70 other := foldPath[fold] 71 return fmt.Errorf("unzip %v: case-insensitive file name collision: %q and %q", zipfile, other, name) 72 } 73 74 // Check total size, valid file names. 75 var size int64 76 for _, zf := range z.File { 77 if !str.HasPathPrefix(zf.Name, prefix) { 78 return fmt.Errorf("unzip %v: unexpected file name %s", zipfile, zf.Name) 79 } 80 if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") { 81 continue 82 } 83 name := zf.Name[len(prefix)+1:] 84 if err := module.CheckFilePath(name); err != nil { 85 return fmt.Errorf("unzip %v: %v", zipfile, err) 86 } 87 if err := checkFold(name); err != nil { 88 return err 89 } 90 if path.Clean(zf.Name) != zf.Name || strings.HasPrefix(zf.Name[len(prefix)+1:], "/") { 91 return fmt.Errorf("unzip %v: invalid file name %s", zipfile, zf.Name) 92 } 93 s := int64(zf.UncompressedSize64) 94 if s < 0 || maxSize-size < s { 95 return fmt.Errorf("unzip %v: content too large", zipfile) 96 } 97 size += s 98 } 99 100 // Unzip, enforcing sizes checked earlier. 101 dirs := map[string]bool{dir: true} 102 for _, zf := range z.File { 103 if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") { 104 continue 105 } 106 name := zf.Name[len(prefix):] 107 dst := filepath.Join(dir, name) 108 parent := filepath.Dir(dst) 109 for parent != dir { 110 dirs[parent] = true 111 parent = filepath.Dir(parent) 112 } 113 if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil { 114 return err 115 } 116 w, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444) 117 if err != nil { 118 return fmt.Errorf("unzip %v: %v", zipfile, err) 119 } 120 r, err := zf.Open() 121 if err != nil { 122 w.Close() 123 return fmt.Errorf("unzip %v: %v", zipfile, err) 124 } 125 lr := &io.LimitedReader{R: r, N: int64(zf.UncompressedSize64) + 1} 126 _, err = io.Copy(w, lr) 127 r.Close() 128 if err != nil { 129 w.Close() 130 return fmt.Errorf("unzip %v: %v", zipfile, err) 131 } 132 if err := w.Close(); err != nil { 133 return fmt.Errorf("unzip %v: %v", zipfile, err) 134 } 135 if lr.N <= 0 { 136 return fmt.Errorf("unzip %v: content too large", zipfile) 137 } 138 } 139 140 // Mark directories unwritable, best effort. 141 var dirlist []string 142 for dir := range dirs { 143 dirlist = append(dirlist, dir) 144 } 145 sort.Strings(dirlist) 146 147 // Run over list backward to chmod children before parents. 148 for i := len(dirlist) - 1; i >= 0; i-- { 149 os.Chmod(dirlist[i], 0555) 150 } 151 152 return nil 153 }