Source file src/pkg/cmd/go/internal/modfetch/unzip.go
1
2
3
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
24 if maxSize == 0 {
25 maxSize = codehost.MaxZipFile
26 }
27
28
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
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
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
137
138 func makeDirsReadOnly(dir string) {
139 type pathMode struct {
140 path string
141 mode os.FileMode
142 }
143 var dirs []pathMode
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
154 for i := len(dirs) - 1; i >= 0; i-- {
155 os.Chmod(dirs[i].path, dirs[i].mode&^0222)
156 }
157 }
158
159
160
161 func RemoveAll(dir string) error {
162
163 filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
164 if err != nil {
165 return nil
166 }
167 if info.IsDir() {
168 os.Chmod(path, 0777)
169 }
170 return nil
171 })
172 return os.RemoveAll(dir)
173 }
174
View as plain text