github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/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 "strings" 16 17 "cmd/go/internal/modfetch/codehost" 18 "cmd/go/internal/module" 19 "cmd/go/internal/str" 20 ) 21 22 func Unzip(dir, zipfile, prefix string, maxSize int64) error { 23 // TODO(bcmills): The maxSize parameter is invariantly 0. Remove it. 24 if maxSize == 0 { 25 maxSize = codehost.MaxZipFile 26 } 27 28 // Directory can exist, but must be empty. 29 files, _ := ioutil.ReadDir(dir) 30 if len(files) > 0 { 31 return fmt.Errorf("target directory %v exists and is not empty", dir) 32 } 33 if err := os.MkdirAll(dir, 0777); err != nil { 34 return err 35 } 36 37 f, err := os.Open(zipfile) 38 if err != nil { 39 return err 40 } 41 defer f.Close() 42 info, err := f.Stat() 43 if err != nil { 44 return err 45 } 46 47 z, err := zip.NewReader(f, info.Size()) 48 if err != nil { 49 return fmt.Errorf("unzip %v: %s", zipfile, err) 50 } 51 52 foldPath := make(map[string]string) 53 var checkFold func(string) error 54 checkFold = func(name string) error { 55 fold := str.ToFold(name) 56 if foldPath[fold] == name { 57 return nil 58 } 59 dir := path.Dir(name) 60 if dir != "." { 61 if err := checkFold(dir); err != nil { 62 return err 63 } 64 } 65 if foldPath[fold] == "" { 66 foldPath[fold] = name 67 return nil 68 } 69 other := foldPath[fold] 70 return fmt.Errorf("unzip %v: case-insensitive file name collision: %q and %q", zipfile, other, name) 71 } 72 73 // Check total size, valid file names. 74 var size int64 75 for _, zf := range z.File { 76 if !str.HasPathPrefix(zf.Name, prefix) { 77 return fmt.Errorf("unzip %v: unexpected file name %s", zipfile, zf.Name) 78 } 79 if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") { 80 continue 81 } 82 name := zf.Name[len(prefix)+1:] 83 if err := module.CheckFilePath(name); err != nil { 84 return fmt.Errorf("unzip %v: %v", zipfile, err) 85 } 86 if err := checkFold(name); err != nil { 87 return err 88 } 89 if path.Clean(zf.Name) != zf.Name || strings.HasPrefix(zf.Name[len(prefix)+1:], "/") { 90 return fmt.Errorf("unzip %v: invalid file name %s", zipfile, zf.Name) 91 } 92 s := int64(zf.UncompressedSize64) 93 if s < 0 || maxSize-size < s { 94 return fmt.Errorf("unzip %v: content too large", zipfile) 95 } 96 size += s 97 } 98 99 // Unzip, enforcing sizes checked earlier. 100 for _, zf := range z.File { 101 if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") { 102 continue 103 } 104 name := zf.Name[len(prefix):] 105 dst := filepath.Join(dir, name) 106 if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil { 107 return err 108 } 109 w, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0444) 110 if err != nil { 111 return fmt.Errorf("unzip %v: %v", zipfile, err) 112 } 113 r, err := zf.Open() 114 if err != nil { 115 w.Close() 116 return fmt.Errorf("unzip %v: %v", zipfile, err) 117 } 118 lr := &io.LimitedReader{R: r, N: int64(zf.UncompressedSize64) + 1} 119 _, err = io.Copy(w, lr) 120 r.Close() 121 if err != nil { 122 w.Close() 123 return fmt.Errorf("unzip %v: %v", zipfile, err) 124 } 125 if err := w.Close(); err != nil { 126 return fmt.Errorf("unzip %v: %v", zipfile, err) 127 } 128 if lr.N <= 0 { 129 return fmt.Errorf("unzip %v: content too large", zipfile) 130 } 131 } 132 133 return nil 134 } 135 136 // makeDirsReadOnly makes a best-effort attempt to remove write permissions for dir 137 // and its transitive contents. 138 func makeDirsReadOnly(dir string) { 139 type pathMode struct { 140 path string 141 mode os.FileMode 142 } 143 var dirs []pathMode // in lexical order 144 filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 145 if err == nil && info.Mode()&0222 != 0 { 146 if info.IsDir() { 147 dirs = append(dirs, pathMode{path, info.Mode()}) 148 } 149 } 150 return nil 151 }) 152 153 // Run over list backward to chmod children before parents. 154 for i := len(dirs) - 1; i >= 0; i-- { 155 os.Chmod(dirs[i].path, dirs[i].mode&^0222) 156 } 157 } 158 159 // RemoveAll removes a directory written by Download or Unzip, first applying 160 // any permission changes needed to do so. 161 func RemoveAll(dir string) error { 162 // Module cache has 0555 directories; make them writable in order to remove content. 163 filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 164 if err != nil { 165 return nil // ignore errors walking in file system 166 } 167 if info.IsDir() { 168 os.Chmod(path, 0777) 169 } 170 return nil 171 }) 172 return os.RemoveAll(dir) 173 }