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.
18 "cmd/go/internal/modfetch/codehost"
19 "cmd/go/internal/module"
23 func Unzip(dir
, zipfile
, prefix
string, maxSize
int64) error
{
25 maxSize
= codehost
.MaxZipFile
28 // Directory can exist, but must be empty.
30 files
, _
:= ioutil
.ReadDir(dir
)
32 return fmt
.Errorf("target directory %v exists and is not empty", dir
)
34 if err
:= os
.MkdirAll(dir
, 0777); err
!= nil {
38 f
, err
:= os
.Open(zipfile
)
48 z
, err
:= zip
.NewReader(f
, info
.Size())
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
{
62 if err
:= checkFold(dir
); err
!= nil {
66 if foldPath
[fold
] == "" {
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.
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
, "/") {
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 {
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
)
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
, "/") {
106 name
:= zf
.Name
[len(prefix
):]
107 dst
:= filepath
.Join(dir
, name
)
108 parent
:= filepath
.Dir(dst
)
111 parent
= filepath
.Dir(parent
)
113 if err
:= os
.MkdirAll(filepath
.Dir(dst
), 0777); err
!= nil {
116 w
, err
:= os
.OpenFile(dst
, os
.O_WRONLY|os
.O_CREATE|os
.O_TRUNC
, 0444)
118 return fmt
.Errorf("unzip %v: %v", zipfile
, err
)
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
)
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
)
136 return fmt
.Errorf("unzip %v: content too large", zipfile
)
140 // Mark directories unwritable, best effort.
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)