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  }