libgo: update to Go 1.11
[official-gcc.git] / libgo / go / cmd / go / internal / modfetch / unzip.go
bloba50431fd8629de18f4cd9b2f1f5ee1b790852bf9
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.
5 package modfetch
7 import (
8 "archive/zip"
9 "fmt"
10 "io"
11 "io/ioutil"
12 "os"
13 "path"
14 "path/filepath"
15 "sort"
16 "strings"
18 "cmd/go/internal/modfetch/codehost"
19 "cmd/go/internal/module"
20 "cmd/go/internal/str"
23 func Unzip(dir, zipfile, prefix string, maxSize int64) error {
24 if maxSize == 0 {
25 maxSize = codehost.MaxZipFile
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)
34 if err := os.MkdirAll(dir, 0777); err != nil {
35 return err
38 f, err := os.Open(zipfile)
39 if err != nil {
40 return err
42 defer f.Close()
43 info, err := f.Stat()
44 if err != nil {
45 return err
48 z, err := zip.NewReader(f, info.Size())
49 if err != nil {
50 return fmt.Errorf("unzip %v: %s", zipfile, err)
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
60 dir := path.Dir(name)
61 if dir != "." {
62 if err := checkFold(dir); err != nil {
63 return err
66 if foldPath[fold] == "" {
67 foldPath[fold] = name
68 return nil
70 other := foldPath[fold]
71 return fmt.Errorf("unzip %v: case-insensitive file name collision: %q and %q", zipfile, other, name)
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)
80 if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") {
81 continue
83 name := zf.Name[len(prefix)+1:]
84 if err := module.CheckFilePath(name); err != nil {
85 return fmt.Errorf("unzip %v: %v", zipfile, err)
87 if err := checkFold(name); err != nil {
88 return err
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)
93 s := int64(zf.UncompressedSize64)
94 if s < 0 || maxSize-size < s {
95 return fmt.Errorf("unzip %v: content too large", zipfile)
97 size += s
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
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)
113 if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
114 return err
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)
120 r, err := zf.Open()
121 if err != nil {
122 w.Close()
123 return fmt.Errorf("unzip %v: %v", zipfile, err)
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)
132 if err := w.Close(); err != nil {
133 return fmt.Errorf("unzip %v: %v", zipfile, err)
135 if lr.N <= 0 {
136 return fmt.Errorf("unzip %v: content too large", zipfile)
140 // Mark directories unwritable, best effort.
141 var dirlist []string
142 for dir := range dirs {
143 dirlist = append(dirlist, dir)
145 sort.Strings(dirlist)
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)
152 return nil