summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mholt
diff options
context:
space:
mode:
authorPhilippHomann <homann.philipp@googlemail.com>2020-06-05 22:47:39 +0200
committerGitHub <noreply@github.com>2020-06-05 16:47:39 -0400
commit684b7a999f6e3b928ad4ae993f92a6b83e0c4486 (patch)
tree64cfa190c811053f0d111ef73ac8e31858db0624 /vendor/github.com/mholt
parent209b17c4e25fe72d2fdf46f412fc388bc274a516 (diff)
downloadgitea-684b7a999f6e3b928ad4ae993f92a6b83e0c4486.tar.gz
gitea-684b7a999f6e3b928ad4ae993f92a6b83e0c4486.zip
Dump: add output format tar and output to stdout (#10376)
* Dump: Use mholt/archive/v3 to support tar including many compressions Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: Allow dump output to stdout Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: Fixed bug present since #6677 where SessionConfig.Provider is never "file" Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: never pack RepoRootPath, LFS.ContentPath and LogRootPath when they are below AppDataPath Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: also dump LFS (fixes #10058) Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Dump: never dump CustomPath if CustomPath is a subdir of or equal to AppDataPath (fixes #10365) Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * Use log.Info instead of fmt.Fprintf Signed-off-by: Philipp Homann <homann.philipp@googlemail.com> * import ordering * make fmt Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Matti R <matti@mdranta.net>
Diffstat (limited to 'vendor/github.com/mholt')
-rw-r--r--vendor/github.com/mholt/archiver/v3/.gitignore5
-rw-r--r--vendor/github.com/mholt/archiver/v3/LICENSE21
-rw-r--r--vendor/github.com/mholt/archiver/v3/README.md271
-rw-r--r--vendor/github.com/mholt/archiver/v3/archiver.go532
-rw-r--r--vendor/github.com/mholt/archiver/v3/azure-pipelines.yml86
-rw-r--r--vendor/github.com/mholt/archiver/v3/brotli.go55
-rw-r--r--vendor/github.com/mholt/archiver/v3/build.bash13
-rw-r--r--vendor/github.com/mholt/archiver/v3/bz2.go64
-rw-r--r--vendor/github.com/mholt/archiver/v3/filecompressor.go67
-rw-r--r--vendor/github.com/mholt/archiver/v3/go.mod16
-rw-r--r--vendor/github.com/mholt/archiver/v3/go.sum26
-rw-r--r--vendor/github.com/mholt/archiver/v3/gz.go76
-rw-r--r--vendor/github.com/mholt/archiver/v3/lz4.go56
-rw-r--r--vendor/github.com/mholt/archiver/v3/rar.go409
-rw-r--r--vendor/github.com/mholt/archiver/v3/sz.go51
-rw-r--r--vendor/github.com/mholt/archiver/v3/tar.go616
-rw-r--r--vendor/github.com/mholt/archiver/v3/tarbrotli.go114
-rw-r--r--vendor/github.com/mholt/archiver/v3/tarbz2.go126
-rw-r--r--vendor/github.com/mholt/archiver/v3/targz.go137
-rw-r--r--vendor/github.com/mholt/archiver/v3/tarlz4.go122
-rw-r--r--vendor/github.com/mholt/archiver/v3/tarsz.go114
-rw-r--r--vendor/github.com/mholt/archiver/v3/tarxz.go119
-rw-r--r--vendor/github.com/mholt/archiver/v3/tarzst.go120
-rw-r--r--vendor/github.com/mholt/archiver/v3/xz.go58
-rw-r--r--vendor/github.com/mholt/archiver/v3/zip.go605
-rw-r--r--vendor/github.com/mholt/archiver/v3/zstd.go59
26 files changed, 3938 insertions, 0 deletions
diff --git a/vendor/github.com/mholt/archiver/v3/.gitignore b/vendor/github.com/mholt/archiver/v3/.gitignore
new file mode 100644
index 0000000000..ac8f8b2516
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/.gitignore
@@ -0,0 +1,5 @@
+.DS_Store
+_gitignore
+builds/
+*.test
+cmd/archiver/archiver
diff --git a/vendor/github.com/mholt/archiver/v3/LICENSE b/vendor/github.com/mholt/archiver/v3/LICENSE
new file mode 100644
index 0000000000..315d04f280
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Matthew Holt
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/vendor/github.com/mholt/archiver/v3/README.md b/vendor/github.com/mholt/archiver/v3/README.md
new file mode 100644
index 0000000000..cbf17d53d4
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/README.md
@@ -0,0 +1,271 @@
+archiver [![archiver GoDoc](https://img.shields.io/badge/reference-godoc-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/mholt/archiver?tab=doc) <a href="https://dev.azure.com/mholt-dev/Archiver/_build"><img src="https://img.shields.io/azure-devops/build/mholt-dev/1e14e7f7-f929-4fec-a1db-fa5a3c0d4ca9/2/master.svg?label=cross-platform%20tests&style=flat-square"></a>
+========
+
+Introducing **Archiver 3.1** - a cross-platform, multi-format archive utility and Go library. A powerful and flexible library meets an elegant CLI in this generic replacement for several platform-specific or format-specific archive utilities.
+
+## Features
+
+Package archiver makes it trivially easy to make and extract common archive formats such as tarball (and its compressed variants) and zip. Simply name the input and output file(s). The `arc` command runs the same on all platforms and has no external dependencies (not even libc). It is powered by the Go standard library and several third-party, pure-Go libraries.
+
+Files are put into the root of the archive; directories are recursively added, preserving structure.
+
+- Make whole archives from a list of files
+- Open whole archives to a folder
+- Extract specific files/folders from archives
+- Stream files in and out of archives without needing actual files on disk
+- Traverse archive contents without loading them
+- Compress files
+- Decompress files
+- Streaming compression and decompression
+- Several archive and compression formats supported
+
+### Format-dependent features
+
+- Gzip is multithreaded
+- Optionally create a top-level folder to avoid littering a directory or archive root with files
+- Toggle overwrite existing files
+- Adjust compression level
+- Zip: store (not compress) already-compressed files
+- Make all necessary directories
+- Open password-protected RAR archives
+- Optionally continue with other files after an error
+
+### Supported compression formats
+
+- brotli (br)
+- bzip2 (bz2)
+- flate (zip)
+- gzip (gz)
+- lz4
+- snappy (sz)
+- xz
+- zstandard (zstd)
+
+### Supported archive formats
+
+- .zip
+- .tar (including any compressed variants like .tar.gz)
+- .rar (read-only)
+
+Tar files can optionally be compressed using any of the above compression formats.
+
+
+## Install
+
+To install the runnable binary to your $GOPATH/bin:
+
+```bash
+$ go install github.com/mholt/archiver/cmd/arc
+```
+
+Or download binaries from the [releases](https://github.com/mholt/archiver/releases) page.
+
+To use as a dependency in your project:
+
+```
+$ go get github.com/mholt/archiver/v3
+```
+
+
+## Command Use
+
+### Make new archive
+
+```bash
+# Syntax: arc archive [archive name] [input files...]
+
+$ arc archive test.tar.gz file1.txt images/file2.jpg folder/subfolder
+```
+
+(At least one input file is required.)
+
+
+### Extract entire archive
+
+```bash
+# Syntax: arc unarchive [archive name] [destination]
+
+$ arc unarchive test.tar.gz
+```
+
+(The destination path is optional; default is current directory.)
+
+The archive name must end with a supported file extension&mdash;this is how it knows what kind of archive to make. Run `arc help` for more help.
+
+
+### List archive contents
+
+```bash
+# Syntax: arc ls [archive name]
+
+$ arc ls caddy_dist.tar.gz
+drwxr-xr-x matt staff 0 2018-09-19 15:47:18 -0600 MDT dist/
+-rw-r--r-- matt staff 6148 2017-08-07 18:34:22 -0600 MDT dist/.DS_Store
+-rw-r--r-- matt staff 22481 2018-09-19 15:47:18 -0600 MDT dist/CHANGES.txt
+-rw-r--r-- matt staff 17189 2018-09-19 15:47:18 -0600 MDT dist/EULA.txt
+-rw-r--r-- matt staff 25261 2016-03-07 16:32:00 -0700 MST dist/LICENSES.txt
+-rw-r--r-- matt staff 1017 2018-09-19 15:47:18 -0600 MDT dist/README.txt
+-rw-r--r-- matt staff 288 2016-03-21 11:52:38 -0600 MDT dist/gitcookie.sh.enc
+...
+```
+
+
+### Extract a specific file or folder from an archive
+
+```bash
+# Syntax: arc extract [archive name] [path in archive] [destination on disk]
+
+$ arc extract test.tar.gz foo/hello.txt extracted/hello.txt
+```
+
+
+### Compress a single file
+
+```bash
+# Syntax: arc compress [input file] [output file]
+
+$ arc compress test.txt compressed_test.txt.gz
+$ arc compress test.txt gz
+```
+
+For convenience, the output file (second argument) may simply be a compression format (without leading dot), in which case the output filename will be the same as the input filename but with the format extension appended, and the input file will be deleted if successful.
+
+### Decompress a single file
+
+```bash
+# Syntax: arc decompress [input file] [output file]
+
+$ arc decompress test.txt.gz original_test.txt
+$ arc decompress test.txt.gz
+```
+
+For convenience, the output file (second argument) may be omitted. In that case, the output filename will have the same name as the input filename, but with the compression extension stripped from the end; and the input file will be deleted if successful.
+
+
+### Flags
+
+Flags are specified before the subcommand. Use `arc help` or `arc -h` to get usage help and a description of flags with their default values.
+
+
+
+## Library Use
+
+The archiver package allows you to easily create and open archives, walk their contents, extract specific files, compress and decompress files, and even stream archives in and out using pure io.Reader and io.Writer interfaces, without ever needing to touch the disk.
+
+```go
+import "github.com/mholt/archiver/v3"
+```
+
+[See the package's GoDoc](https://pkg.go.dev/github.com/mholt/archiver?tab=doc) for full API documentation.
+
+For example, creating or unpacking an archive file:
+
+```go
+err := archiver.Archive([]string{"testdata", "other/file.txt"}, "test.zip")
+// ...
+err = archiver.Unarchive("test.tar.gz", "test")
+```
+
+The archive format is determined by file extension. (There are [several functions in this package](https://pkg.go.dev/github.com/mholt/archiver?tab=doc) which perform a task by inferring the format from file extension or file header, including `Archive()`, `Unarchive()`, `CompressFile()`, and `DecompressFile()`.)
+
+To configure the archiver used or perform, create an instance of the format's type:
+
+```go
+z := archiver.Zip{
+ CompressionLevel: flate.DefaultCompression,
+ MkdirAll: true,
+ SelectiveCompression: true,
+ ContinueOnError: false,
+ OverwriteExisting: false,
+ ImplicitTopLevelFolder: false,
+}
+
+err := z.Archive([]string{"testdata", "other/file.txt"}, "/Users/matt/Desktop/test.zip")
+```
+
+Inspecting an archive:
+
+```go
+err = z.Walk("/Users/matt/Desktop/test.zip", func(f archiver.File) error {
+ zfh, ok := f.Header.(zip.FileHeader)
+ if ok {
+ fmt.Println("Filename:", zfh.Name)
+ }
+ return nil
+})
+```
+
+Streaming files into an archive that is being written to the HTTP response:
+
+```go
+err = z.Create(responseWriter)
+if err != nil {
+ return err
+}
+defer z.Close()
+
+for _, fname := range filenames {
+ info, err := os.Stat(fname)
+ if err != nil {
+ return err
+ }
+
+ // get file's name for the inside of the archive
+ internalName, err := archiver.NameInArchive(info, fname, fname)
+ if err != nil {
+ return err
+ }
+
+ // open the file
+ file, err := os.Open(f)
+ if err != nil {
+ return err
+ }
+
+ // write it to the archive
+ err = z.Write(archiver.File{
+ FileInfo: archiver.FileInfo{
+ FileInfo: info,
+ CustomName: internalName,
+ },
+ ReadCloser: file,
+ })
+ file.Close()
+ if err != nil {
+ return err
+ }
+}
+```
+
+The `archiver.File` type allows you to use actual files with archives, or to mimic files when you only have streams.
+
+There's a lot more that can be done, too. [See the GoDoc](https://pkg.go.dev/github.com/mholt/archiver?tab=doc) for full API documentation.
+
+**Security note: This package does NOT attempt to mitigate zip-slip attacks.** It is [extremely difficult](https://github.com/rubyzip/rubyzip/pull/376) [to do properly](https://github.com/mholt/archiver/pull/65#issuecomment-395988244) and [seemingly impossible to mitigate effectively across platforms](https://github.com/golang/go/issues/20126). [Attempted fixes have broken processing of legitimate files in production](https://github.com/mholt/archiver/pull/70#issuecomment-423267320), rendering the program unusable. Our recommendation instead is to inspect the contents of an untrusted archive before extracting it (this package provides `Walkers`) and decide if you want to proceed with extraction.
+
+
+## Project Values
+
+This project has a few principle-based goals that guide its development:
+
+- **Do our thing really well.** Our thing is creating, opening, inspecting, compressing, and streaming archive files. It is not meant to be a replacement for specific archive format tools like tar, zip, etc. that have lots of features and customizability. (Some customizability is OK, but not to the extent that it becomes overly complicated or error-prone.)
+
+- **Have good tests.** Changes should be covered by tests.
+
+- **Limit dependencies.** Keep the package lightweight.
+
+- **Pure Go.** This means no cgo or other external/system dependencies. This package should be able to stand on its own and cross-compile easily to any platform -- and that includes its library dependencies.
+
+- **Idiomatic Go.** Keep interfaces small, variable names semantic, vet shows no errors, the linter is generally quiet, etc.
+
+- **Be elegant.** This package should be elegant to use and its code should be elegant when reading and testing. If it doesn't feel good, fix it up.
+
+- **Well-documented.** Use comments prudently; explain why non-obvious code is necessary (and use tests to enforce it). Keep the docs updated, and have examples where helpful.
+
+- **Keep it efficient.** This often means keep it simple. Fast code is valuable.
+
+- **Consensus.** Contributions should ideally be approved by multiple reviewers before being merged. Generally, avoid merging multi-chunk changes that do not go through at least one or two iterations/reviews. Except for trivial changes, PRs are seldom ready to merge right away.
+
+- **Have fun contributing.** Coding is awesome!
+
+We welcome contributions and appreciate your efforts! However, please open issues to discuss any changes before spending the time preparing a pull request. This will save time, reduce frustration, and help coordinate the work. Thank you!
diff --git a/vendor/github.com/mholt/archiver/v3/archiver.go b/vendor/github.com/mholt/archiver/v3/archiver.go
new file mode 100644
index 0000000000..ecf315e058
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/archiver.go
@@ -0,0 +1,532 @@
+// Package archiver facilitates convenient, cross-platform, high-level archival
+// and compression operations for a variety of formats and compression algorithms.
+//
+// This package and its dependencies are written in pure Go (not cgo) and
+// have no external dependencies, so they should run on all major platforms.
+// (It also comes with a command for CLI use in the cmd/arc folder.)
+//
+// Each supported format or algorithm has a unique type definition that
+// implements the interfaces corresponding to the tasks they perform. For
+// example, the Tar type implements Reader, Writer, Archiver, Unarchiver,
+// Walker, and several other interfaces.
+//
+// The most common functions are implemented at the package level for
+// convenience: Archive, Unarchive, Walk, Extract, CompressFile, and
+// DecompressFile. With these, the format type is chosen implicitly,
+// and a sane default configuration is used.
+//
+// To customize a format's configuration, create an instance of its struct
+// with its fields set to the desired values. You can also use and customize
+// the handy Default* (replace the wildcard with the format's type name)
+// for a quick, one-off instance of the format's type.
+//
+// To obtain a new instance of a format's struct with the default config, use
+// the provided New*() functions. This is not required, however. An empty
+// struct of any type, for example &Zip{} is perfectly valid, so you may
+// create the structs manually, too. The examples on this page show how
+// either may be done.
+//
+// See the examples in this package for an idea of how to wield this package
+// for common tasks. Most of the examples which are specific to a certain
+// format type, for example Zip, can be applied to other types that implement
+// the same interfaces. For example, using Zip is very similar to using Tar
+// or TarGz (etc), and using Gz is very similar to using Sz or Xz (etc).
+//
+// When creating archives or compressing files using a specific instance of
+// the format's type, the name of the output file MUST match that of the
+// format, to prevent confusion later on. If you absolutely need a different
+// file extension, you may rename the file afterward.
+//
+// Values in this package are NOT safe for concurrent use. There is no
+// performance benefit of reusing them, and since they may contain important
+// state (especially while walking, reading, or writing), it is NOT
+// recommended to reuse values from this package or change their configuration
+// after they are in use.
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "path"
+ "path/filepath"
+ "runtime"
+ "strings"
+)
+
+// Archiver is a type that can create an archive file
+// from a list of source file names.
+type Archiver interface {
+ ExtensionChecker
+
+ // Archive adds all the files or folders in sources
+ // to an archive to be created at destination. Files
+ // are added to the root of the archive, and directories
+ // are walked and recursively added, preserving folder
+ // structure.
+ Archive(sources []string, destination string) error
+}
+
+// ExtensionChecker validates file extensions
+type ExtensionChecker interface {
+ CheckExt(name string) error
+}
+
+// Unarchiver is a type that can extract archive files
+// into a folder.
+type Unarchiver interface {
+ Unarchive(source, destination string) error
+}
+
+// Writer can write discrete byte streams of files to
+// an output stream.
+type Writer interface {
+ Create(out io.Writer) error
+ Write(f File) error
+ Close() error
+}
+
+// Reader can read discrete byte streams of files from
+// an input stream.
+type Reader interface {
+ Open(in io.Reader, size int64) error
+ Read() (File, error)
+ Close() error
+}
+
+// Extractor can extract a specific file from a source
+// archive to a specific destination folder on disk.
+type Extractor interface {
+ Extract(source, target, destination string) error
+}
+
+// File provides methods for accessing information about
+// or contents of a file within an archive.
+type File struct {
+ os.FileInfo
+
+ // The original header info; depends on
+ // type of archive -- could be nil, too.
+ Header interface{}
+
+ // Allow the file contents to be read (and closed)
+ io.ReadCloser
+}
+
+// FileInfo is an os.FileInfo but optionally with
+// a custom name, useful if dealing with files that
+// are not actual files on disk, or which have a
+// different name in an archive than on disk.
+type FileInfo struct {
+ os.FileInfo
+ CustomName string
+}
+
+// Name returns fi.CustomName if not empty;
+// otherwise it returns fi.FileInfo.Name().
+func (fi FileInfo) Name() string {
+ if fi.CustomName != "" {
+ return fi.CustomName
+ }
+ return fi.FileInfo.Name()
+}
+
+// ReadFakeCloser is an io.Reader that has
+// a no-op close method to satisfy the
+// io.ReadCloser interface.
+type ReadFakeCloser struct {
+ io.Reader
+}
+
+// Close implements io.Closer.
+func (rfc ReadFakeCloser) Close() error { return nil }
+
+// Walker can walk an archive file and return information
+// about each item in the archive.
+type Walker interface {
+ Walk(archive string, walkFn WalkFunc) error
+}
+
+// WalkFunc is called at each item visited by Walk.
+// If an error is returned, the walk may continue
+// if the Walker is configured to continue on error.
+// The sole exception is the error value ErrStopWalk,
+// which stops the walk without an actual error.
+type WalkFunc func(f File) error
+
+// ErrStopWalk signals Walk to break without error.
+var ErrStopWalk = fmt.Errorf("walk stopped")
+
+// ErrFormatNotRecognized is an error that will be
+// returned if the file is not a valid archive format.
+var ErrFormatNotRecognized = fmt.Errorf("format not recognized")
+
+// Compressor compresses to out what it reads from in.
+// It also ensures a compatible or matching file extension.
+type Compressor interface {
+ ExtensionChecker
+ Compress(in io.Reader, out io.Writer) error
+}
+
+// Decompressor decompresses to out what it reads from in.
+type Decompressor interface {
+ Decompress(in io.Reader, out io.Writer) error
+}
+
+// Matcher is a type that can return whether the given
+// file appears to match the implementation's format.
+// Implementations should return the file's read position
+// to where it was when the method was called.
+type Matcher interface {
+ Match(io.ReadSeeker) (bool, error)
+}
+
+// Archive creates an archive of the source files to a new file at destination.
+// The archive format is chosen implicitly by file extension.
+func Archive(sources []string, destination string) error {
+ aIface, err := ByExtension(destination)
+ if err != nil {
+ return err
+ }
+ a, ok := aIface.(Archiver)
+ if !ok {
+ return fmt.Errorf("format specified by destination filename is not an archive format: %s (%T)", destination, aIface)
+ }
+ return a.Archive(sources, destination)
+}
+
+// Unarchive unarchives the given archive file into the destination folder.
+// The archive format is selected implicitly.
+func Unarchive(source, destination string) error {
+ uaIface, err := ByExtension(source)
+ if err != nil {
+ return err
+ }
+ u, ok := uaIface.(Unarchiver)
+ if !ok {
+ return fmt.Errorf("format specified by source filename is not an archive format: %s (%T)", source, uaIface)
+ }
+ return u.Unarchive(source, destination)
+}
+
+// Walk calls walkFn for each file within the given archive file.
+// The archive format is chosen implicitly.
+func Walk(archive string, walkFn WalkFunc) error {
+ wIface, err := ByExtension(archive)
+ if err != nil {
+ return err
+ }
+ w, ok := wIface.(Walker)
+ if !ok {
+ return fmt.Errorf("format specified by archive filename is not a walker format: %s (%T)", archive, wIface)
+ }
+ return w.Walk(archive, walkFn)
+}
+
+// Extract extracts a single file from the given source archive. If the target
+// is a directory, the entire folder will be extracted into destination. The
+// archive format is chosen implicitly.
+func Extract(source, target, destination string) error {
+ eIface, err := ByExtension(source)
+ if err != nil {
+ return err
+ }
+ e, ok := eIface.(Extractor)
+ if !ok {
+ return fmt.Errorf("format specified by source filename is not an extractor format: %s (%T)", source, eIface)
+ }
+ return e.Extract(source, target, destination)
+}
+
+// CompressFile is a convenience function to simply compress a file.
+// The compression algorithm is selected implicitly based on the
+// destination's extension.
+func CompressFile(source, destination string) error {
+ cIface, err := ByExtension(destination)
+ if err != nil {
+ return err
+ }
+ c, ok := cIface.(Compressor)
+ if !ok {
+ return fmt.Errorf("format specified by destination filename is not a recognized compression algorithm: %s", destination)
+ }
+ return FileCompressor{Compressor: c}.CompressFile(source, destination)
+}
+
+// DecompressFile is a convenience function to simply decompress a file.
+// The decompression algorithm is selected implicitly based on the
+// source's extension.
+func DecompressFile(source, destination string) error {
+ cIface, err := ByExtension(source)
+ if err != nil {
+ return err
+ }
+ c, ok := cIface.(Decompressor)
+ if !ok {
+ return fmt.Errorf("format specified by source filename is not a recognized compression algorithm: %s", source)
+ }
+ return FileCompressor{Decompressor: c}.DecompressFile(source, destination)
+}
+
+func fileExists(name string) bool {
+ _, err := os.Stat(name)
+ return !os.IsNotExist(err)
+}
+
+func mkdir(dirPath string, dirMode os.FileMode) error {
+ err := os.MkdirAll(dirPath, dirMode)
+ if err != nil {
+ return fmt.Errorf("%s: making directory: %v", dirPath, err)
+ }
+ return nil
+}
+
+func writeNewFile(fpath string, in io.Reader, fm os.FileMode) error {
+ err := os.MkdirAll(filepath.Dir(fpath), 0755)
+ if err != nil {
+ return fmt.Errorf("%s: making directory for file: %v", fpath, err)
+ }
+
+ out, err := os.Create(fpath)
+ if err != nil {
+ return fmt.Errorf("%s: creating new file: %v", fpath, err)
+ }
+ defer out.Close()
+
+ err = out.Chmod(fm)
+ if err != nil && runtime.GOOS != "windows" {
+ return fmt.Errorf("%s: changing file mode: %v", fpath, err)
+ }
+
+ _, err = io.Copy(out, in)
+ if err != nil {
+ return fmt.Errorf("%s: writing file: %v", fpath, err)
+ }
+ return nil
+}
+
+func writeNewSymbolicLink(fpath string, target string) error {
+ err := os.MkdirAll(filepath.Dir(fpath), 0755)
+ if err != nil {
+ return fmt.Errorf("%s: making directory for file: %v", fpath, err)
+ }
+
+ _, err = os.Lstat(fpath)
+ if err == nil {
+ err = os.Remove(fpath)
+ if err != nil {
+ return fmt.Errorf("%s: failed to unlink: %+v", fpath, err)
+ }
+ }
+
+ err = os.Symlink(target, fpath)
+ if err != nil {
+ return fmt.Errorf("%s: making symbolic link for: %v", fpath, err)
+ }
+ return nil
+}
+
+func writeNewHardLink(fpath string, target string) error {
+ err := os.MkdirAll(filepath.Dir(fpath), 0755)
+ if err != nil {
+ return fmt.Errorf("%s: making directory for file: %v", fpath, err)
+ }
+
+ _, err = os.Lstat(fpath)
+ if err == nil {
+ err = os.Remove(fpath)
+ if err != nil {
+ return fmt.Errorf("%s: failed to unlink: %+v", fpath, err)
+ }
+ }
+
+ err = os.Link(target, fpath)
+ if err != nil {
+ return fmt.Errorf("%s: making hard link for: %v", fpath, err)
+ }
+ return nil
+}
+
+func isSymlink(fi os.FileInfo) bool {
+ return fi.Mode()&os.ModeSymlink != 0
+}
+
+// within returns true if sub is within or equal to parent.
+func within(parent, sub string) bool {
+ rel, err := filepath.Rel(parent, sub)
+ if err != nil {
+ return false
+ }
+ return !strings.Contains(rel, "..")
+}
+
+// multipleTopLevels returns true if the paths do not
+// share a common top-level folder.
+func multipleTopLevels(paths []string) bool {
+ if len(paths) < 2 {
+ return false
+ }
+ var lastTop string
+ for _, p := range paths {
+ p = strings.TrimPrefix(strings.Replace(p, `\`, "/", -1), "/")
+ for {
+ next := path.Dir(p)
+ if next == "." {
+ break
+ }
+ p = next
+ }
+ if lastTop == "" {
+ lastTop = p
+ }
+ if p != lastTop {
+ return true
+ }
+ }
+ return false
+}
+
+// folderNameFromFileName returns a name for a folder
+// that is suitable based on the filename, which will
+// be stripped of its extensions.
+func folderNameFromFileName(filename string) string {
+ base := filepath.Base(filename)
+ firstDot := strings.Index(base, ".")
+ if firstDot > -1 {
+ return base[:firstDot]
+ }
+ return base
+}
+
+// makeNameInArchive returns the filename for the file given by fpath to be used within
+// the archive. sourceInfo is the FileInfo obtained by calling os.Stat on source, and baseDir
+// is an optional base directory that becomes the root of the archive. fpath should be the
+// unaltered file path of the file given to a filepath.WalkFunc.
+func makeNameInArchive(sourceInfo os.FileInfo, source, baseDir, fpath string) (string, error) {
+ name := filepath.Base(fpath) // start with the file or dir name
+ if sourceInfo.IsDir() {
+ // preserve internal directory structure; that's the path components
+ // between the source directory's leaf and this file's leaf
+ dir, err := filepath.Rel(filepath.Dir(source), filepath.Dir(fpath))
+ if err != nil {
+ return "", err
+ }
+ // prepend the internal directory structure to the leaf name,
+ // and convert path separators to forward slashes as per spec
+ name = path.Join(filepath.ToSlash(dir), name)
+ }
+ return path.Join(baseDir, name), nil // prepend the base directory
+}
+
+// NameInArchive returns a name for the file at fpath suitable for
+// the inside of an archive. The source and its associated sourceInfo
+// is the path where walking a directory started, and if no directory
+// was walked, source may == fpath. The returned name is essentially
+// the components of the path between source and fpath, preserving
+// the internal directory structure.
+func NameInArchive(sourceInfo os.FileInfo, source, fpath string) (string, error) {
+ return makeNameInArchive(sourceInfo, source, "", fpath)
+}
+
+// ByExtension returns an archiver and unarchiver, or compressor
+// and decompressor, based on the extension of the filename.
+func ByExtension(filename string) (interface{}, error) {
+ var ec interface{}
+ for _, c := range extCheckers {
+ if err := c.CheckExt(filename); err == nil {
+ ec = c
+ break
+ }
+ }
+ switch ec.(type) {
+ case *Rar:
+ return NewRar(), nil
+ case *Tar:
+ return NewTar(), nil
+ case *TarBrotli:
+ return NewTarBrotli(), nil
+ case *TarBz2:
+ return NewTarBz2(), nil
+ case *TarGz:
+ return NewTarGz(), nil
+ case *TarLz4:
+ return NewTarLz4(), nil
+ case *TarSz:
+ return NewTarSz(), nil
+ case *TarXz:
+ return NewTarXz(), nil
+ case *TarZstd:
+ return NewTarZstd(), nil
+ case *Zip:
+ return NewZip(), nil
+ case *Gz:
+ return NewGz(), nil
+ case *Bz2:
+ return NewBz2(), nil
+ case *Lz4:
+ return NewLz4(), nil
+ case *Snappy:
+ return NewSnappy(), nil
+ case *Xz:
+ return NewXz(), nil
+ case *Zstd:
+ return NewZstd(), nil
+ }
+ return nil, fmt.Errorf("format unrecognized by filename: %s", filename)
+}
+
+// ByHeader returns the unarchiver value that matches the input's
+// file header. It does not affect the current read position.
+// If the file's header is not a recognized archive format, then
+// ErrFormatNotRecognized will be returned.
+func ByHeader(input io.ReadSeeker) (Unarchiver, error) {
+ var matcher Matcher
+ for _, m := range matchers {
+ ok, err := m.Match(input)
+ if err != nil {
+ return nil, fmt.Errorf("matching on format %s: %v", m, err)
+ }
+ if ok {
+ matcher = m
+ break
+ }
+ }
+ switch matcher.(type) {
+ case *Zip:
+ return NewZip(), nil
+ case *Tar:
+ return NewTar(), nil
+ case *Rar:
+ return NewRar(), nil
+ }
+ return nil, ErrFormatNotRecognized
+}
+
+// extCheckers is a list of the format implementations
+// that can check extensions. Only to be used for
+// checking extensions - not any archival operations.
+var extCheckers = []ExtensionChecker{
+ &TarBrotli{},
+ &TarBz2{},
+ &TarGz{},
+ &TarLz4{},
+ &TarSz{},
+ &TarXz{},
+ &TarZstd{},
+ &Rar{},
+ &Tar{},
+ &Zip{},
+ &Brotli{},
+ &Gz{},
+ &Bz2{},
+ &Lz4{},
+ &Snappy{},
+ &Xz{},
+ &Zstd{},
+}
+
+var matchers = []Matcher{
+ &Rar{},
+ &Tar{},
+ &Zip{},
+}
diff --git a/vendor/github.com/mholt/archiver/v3/azure-pipelines.yml b/vendor/github.com/mholt/archiver/v3/azure-pipelines.yml
new file mode 100644
index 0000000000..a86a62059f
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/azure-pipelines.yml
@@ -0,0 +1,86 @@
+trigger:
+- master
+
+strategy:
+ matrix:
+ linux:
+ imageName: ubuntu-16.04
+ gorootDir: /usr/local
+ mac:
+ imageName: macos-10.13
+ gorootDir: /usr/local
+ windows:
+ imageName: windows-2019
+ gorootDir: C:\
+
+pool:
+ vmImage: $(imageName)
+
+variables:
+ GOROOT: $(gorootDir)/go
+ GOPATH: $(system.defaultWorkingDirectory)/gopath
+ GOBIN: $(GOPATH)/bin
+ modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)'
+ # TODO: Enable modules after upstream dependency zstd supports them
+ # TODO: modules should be the default in Go 1.13, so this won't be needed
+ #GO111MODULE: on
+
+steps:
+- bash: |
+ latestGo=$(curl "https://golang.org/VERSION?m=text")
+ echo "##vso[task.setvariable variable=LATEST_GO]$latestGo"
+ echo "Latest Go version: $latestGo"
+ displayName: "Get latest Go version"
+
+- bash: |
+ sudo rm -f $(which go)
+ echo '##vso[task.prependpath]$(GOBIN)'
+ echo '##vso[task.prependpath]$(GOROOT)/bin'
+ mkdir -p '$(modulePath)'
+ shopt -s extglob
+ shopt -s dotglob
+ mv !(gopath) '$(modulePath)'
+ displayName: Remove old Go, set GOBIN/GOROOT, and move project into GOPATH
+
+# Install Go (this varies by platform)
+
+- bash: |
+ wget "https://dl.google.com/go/$(LATEST_GO).linux-amd64.tar.gz"
+ sudo tar -C $(gorootDir) -xzf "$(LATEST_GO).linux-amd64.tar.gz"
+ condition: eq( variables['Agent.OS'], 'Linux' )
+ displayName: Install Go on Linux
+
+- bash: |
+ wget "https://dl.google.com/go/$(LATEST_GO).darwin-amd64.tar.gz"
+ sudo tar -C $(gorootDir) -xzf "$(LATEST_GO).darwin-amd64.tar.gz"
+ condition: eq( variables['Agent.OS'], 'Darwin' )
+ displayName: Install Go on macOS
+
+- powershell: |
+ Write-Host "Downloading Go... (please be patient, I am very slow)"
+ (New-Object System.Net.WebClient).DownloadFile("https://dl.google.com/go/$(LATEST_GO).windows-amd64.zip", "$(LATEST_GO).windows-amd64.zip")
+ Write-Host "Extracting Go... (I'm slow too)"
+ Expand-Archive "$(LATEST_GO).windows-amd64.zip" -DestinationPath "$(gorootDir)"
+ condition: eq( variables['Agent.OS'], 'Windows_NT' )
+ displayName: Install Go on Windows
+
+# TODO: When this issue is fixed, replace with installer script:
+# https://github.com/golangci/golangci-lint/issues/472
+- script: go get -v github.com/golangci/golangci-lint/cmd/golangci-lint
+ displayName: Install golangci-lint
+
+- bash: |
+ printf "Using go at: $(which go)\n"
+ printf "Go version: $(go version)\n"
+ printf "\n\nGo environment:\n\n"
+ go env
+ printf "\n\nSystem environment:\n\n"
+ env
+ displayName: Print Go version and environment
+
+- script: |
+ go get -v -t -d ./...
+ golangci-lint run -E gofmt -E goimports -E misspell
+ go test -race ./...
+ workingDirectory: '$(modulePath)'
+ displayName: Run tests
diff --git a/vendor/github.com/mholt/archiver/v3/brotli.go b/vendor/github.com/mholt/archiver/v3/brotli.go
new file mode 100644
index 0000000000..d594d66f2d
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/brotli.go
@@ -0,0 +1,55 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+
+ "github.com/andybalholm/brotli"
+)
+
+// Brotli facilitates brotli compression.
+type Brotli struct {
+ Quality int
+}
+
+// Compress reads in, compresses it, and writes it to out.
+func (br *Brotli) Compress(in io.Reader, out io.Writer) error {
+ w := brotli.NewWriterLevel(out, br.Quality)
+ defer w.Close()
+ _, err := io.Copy(w, in)
+ return err
+}
+
+// Decompress reads in, decompresses it, and writes it to out.
+func (br *Brotli) Decompress(in io.Reader, out io.Writer) error {
+ r := brotli.NewReader(in)
+ _, err := io.Copy(out, r)
+ return err
+}
+
+// CheckExt ensures the file extension matches the format.
+func (br *Brotli) CheckExt(filename string) error {
+ if filepath.Ext(filename) != ".br" {
+ return fmt.Errorf("filename must have a .br extension")
+ }
+ return nil
+}
+
+func (br *Brotli) String() string { return "brotli" }
+
+// NewBrotli returns a new, default instance ready to be customized and used.
+func NewBrotli() *Brotli {
+ return &Brotli{
+ Quality: brotli.DefaultCompression,
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Compressor(new(Brotli))
+ _ = Decompressor(new(Brotli))
+)
+
+// DefaultBrotli is a default instance that is conveniently ready to use.
+var DefaultBrotli = NewBrotli()
diff --git a/vendor/github.com/mholt/archiver/v3/build.bash b/vendor/github.com/mholt/archiver/v3/build.bash
new file mode 100644
index 0000000000..225ffc2da5
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/build.bash
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+set -ex
+
+# This script builds archiver for most common platforms.
+
+export CGO_ENABLED=0
+
+cd cmd/arc
+GOOS=linux GOARCH=amd64 go build -o ../../builds/arc_linux_amd64
+GOOS=linux GOARCH=arm go build -o ../../builds/arc_linux_arm7
+GOOS=darwin GOARCH=amd64 go build -o ../../builds/arc_mac_amd64
+GOOS=windows GOARCH=amd64 go build -o ../../builds/arc_windows_amd64.exe
+cd ../..
diff --git a/vendor/github.com/mholt/archiver/v3/bz2.go b/vendor/github.com/mholt/archiver/v3/bz2.go
new file mode 100644
index 0000000000..2eb4ac2b88
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/bz2.go
@@ -0,0 +1,64 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+
+ "github.com/dsnet/compress/bzip2"
+)
+
+// Bz2 facilitates bzip2 compression.
+type Bz2 struct {
+ CompressionLevel int
+}
+
+// Compress reads in, compresses it, and writes it to out.
+func (bz *Bz2) Compress(in io.Reader, out io.Writer) error {
+ w, err := bzip2.NewWriter(out, &bzip2.WriterConfig{
+ Level: bz.CompressionLevel,
+ })
+ if err != nil {
+ return err
+ }
+ defer w.Close()
+ _, err = io.Copy(w, in)
+ return err
+}
+
+// Decompress reads in, decompresses it, and writes it to out.
+func (bz *Bz2) Decompress(in io.Reader, out io.Writer) error {
+ r, err := bzip2.NewReader(in, nil)
+ if err != nil {
+ return err
+ }
+ defer r.Close()
+ _, err = io.Copy(out, r)
+ return err
+}
+
+// CheckExt ensures the file extension matches the format.
+func (bz *Bz2) CheckExt(filename string) error {
+ if filepath.Ext(filename) != ".bz2" {
+ return fmt.Errorf("filename must have a .bz2 extension")
+ }
+ return nil
+}
+
+func (bz *Bz2) String() string { return "bz2" }
+
+// NewBz2 returns a new, default instance ready to be customized and used.
+func NewBz2() *Bz2 {
+ return &Bz2{
+ CompressionLevel: bzip2.DefaultCompression,
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Compressor(new(Bz2))
+ _ = Decompressor(new(Bz2))
+)
+
+// DefaultBz2 is a default instance that is conveniently ready to use.
+var DefaultBz2 = NewBz2()
diff --git a/vendor/github.com/mholt/archiver/v3/filecompressor.go b/vendor/github.com/mholt/archiver/v3/filecompressor.go
new file mode 100644
index 0000000000..ab1fd3b8c0
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/filecompressor.go
@@ -0,0 +1,67 @@
+package archiver
+
+import (
+ "fmt"
+ "os"
+)
+
+// FileCompressor can compress and decompress single files.
+type FileCompressor struct {
+ Compressor
+ Decompressor
+
+ // Whether to overwrite existing files when creating files.
+ OverwriteExisting bool
+}
+
+// CompressFile reads the source file and compresses it to destination.
+// The destination must have a matching extension.
+func (fc FileCompressor) CompressFile(source, destination string) error {
+ if err := fc.CheckExt(destination); err != nil {
+ return err
+ }
+ if fc.Compressor == nil {
+ return fmt.Errorf("no compressor specified")
+ }
+ if !fc.OverwriteExisting && fileExists(destination) {
+ return fmt.Errorf("file exists: %s", destination)
+ }
+
+ in, err := os.Open(source)
+ if err != nil {
+ return err
+ }
+ defer in.Close()
+
+ out, err := os.Create(destination)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ return fc.Compress(in, out)
+}
+
+// DecompressFile reads the source file and decompresses it to destination.
+func (fc FileCompressor) DecompressFile(source, destination string) error {
+ if fc.Decompressor == nil {
+ return fmt.Errorf("no decompressor specified")
+ }
+ if !fc.OverwriteExisting && fileExists(destination) {
+ return fmt.Errorf("file exists: %s", destination)
+ }
+
+ in, err := os.Open(source)
+ if err != nil {
+ return err
+ }
+ defer in.Close()
+
+ out, err := os.Create(destination)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ return fc.Decompress(in, out)
+}
diff --git a/vendor/github.com/mholt/archiver/v3/go.mod b/vendor/github.com/mholt/archiver/v3/go.mod
new file mode 100644
index 0000000000..8586b7a19b
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/go.mod
@@ -0,0 +1,16 @@
+module github.com/mholt/archiver/v3
+
+go 1.12
+
+require (
+ github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6
+ github.com/dsnet/compress v0.0.1
+ github.com/golang/snappy v0.0.1
+ github.com/google/go-cmp v0.3.0 // indirect
+ github.com/klauspost/compress v1.9.2
+ github.com/klauspost/pgzip v1.2.1
+ github.com/nwaples/rardecode v1.0.0
+ github.com/pierrec/lz4 v2.0.5+incompatible
+ github.com/ulikunitz/xz v0.5.6
+ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8
+)
diff --git a/vendor/github.com/mholt/archiver/v3/go.sum b/vendor/github.com/mholt/archiver/v3/go.sum
new file mode 100644
index 0000000000..29c45105fe
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/go.sum
@@ -0,0 +1,26 @@
+github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6 h1:bZ28Hqta7TFAK3Q08CMvv8y3/8ATaEqv2nGoc6yff6c=
+github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
+github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
+github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
+github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw=
+github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY=
+github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
+github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM=
+github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
+github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
+github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
+github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
+github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
diff --git a/vendor/github.com/mholt/archiver/v3/gz.go b/vendor/github.com/mholt/archiver/v3/gz.go
new file mode 100644
index 0000000000..3922e7a53b
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/gz.go
@@ -0,0 +1,76 @@
+package archiver
+
+import (
+ "compress/gzip"
+ "fmt"
+ "io"
+ "path/filepath"
+
+ pgzip "github.com/klauspost/pgzip"
+)
+
+// Gz facilitates gzip compression.
+type Gz struct {
+ CompressionLevel int
+ SingleThreaded bool
+}
+
+// Compress reads in, compresses it, and writes it to out.
+func (gz *Gz) Compress(in io.Reader, out io.Writer) error {
+ var w io.WriteCloser
+ var err error
+ if gz.SingleThreaded {
+ w, err = gzip.NewWriterLevel(out, gz.CompressionLevel)
+ } else {
+ w, err = pgzip.NewWriterLevel(out, gz.CompressionLevel)
+ }
+ if err != nil {
+ return err
+ }
+ defer w.Close()
+ _, err = io.Copy(w, in)
+ return err
+}
+
+// Decompress reads in, decompresses it, and writes it to out.
+func (gz *Gz) Decompress(in io.Reader, out io.Writer) error {
+ var r io.ReadCloser
+ var err error
+ if gz.SingleThreaded {
+ r, err = gzip.NewReader(in)
+ } else {
+ r, err = pgzip.NewReader(in)
+ }
+ if err != nil {
+ return err
+ }
+ defer r.Close()
+ _, err = io.Copy(out, r)
+ return err
+}
+
+// CheckExt ensures the file extension matches the format.
+func (gz *Gz) CheckExt(filename string) error {
+ if filepath.Ext(filename) != ".gz" {
+ return fmt.Errorf("filename must have a .gz extension")
+ }
+ return nil
+}
+
+func (gz *Gz) String() string { return "gz" }
+
+// NewGz returns a new, default instance ready to be customized and used.
+func NewGz() *Gz {
+ return &Gz{
+ CompressionLevel: gzip.DefaultCompression,
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Compressor(new(Gz))
+ _ = Decompressor(new(Gz))
+)
+
+// DefaultGz is a default instance that is conveniently ready to use.
+var DefaultGz = NewGz()
diff --git a/vendor/github.com/mholt/archiver/v3/lz4.go b/vendor/github.com/mholt/archiver/v3/lz4.go
new file mode 100644
index 0000000000..daff631d6d
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/lz4.go
@@ -0,0 +1,56 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+
+ "github.com/pierrec/lz4"
+)
+
+// Lz4 facilitates LZ4 compression.
+type Lz4 struct {
+ CompressionLevel int
+}
+
+// Compress reads in, compresses it, and writes it to out.
+func (lz *Lz4) Compress(in io.Reader, out io.Writer) error {
+ w := lz4.NewWriter(out)
+ w.Header.CompressionLevel = lz.CompressionLevel
+ defer w.Close()
+ _, err := io.Copy(w, in)
+ return err
+}
+
+// Decompress reads in, decompresses it, and writes it to out.
+func (lz *Lz4) Decompress(in io.Reader, out io.Writer) error {
+ r := lz4.NewReader(in)
+ _, err := io.Copy(out, r)
+ return err
+}
+
+// CheckExt ensures the file extension matches the format.
+func (lz *Lz4) CheckExt(filename string) error {
+ if filepath.Ext(filename) != ".lz4" {
+ return fmt.Errorf("filename must have a .lz4 extension")
+ }
+ return nil
+}
+
+func (lz *Lz4) String() string { return "lz4" }
+
+// NewLz4 returns a new, default instance ready to be customized and used.
+func NewLz4() *Lz4 {
+ return &Lz4{
+ CompressionLevel: 9, // https://github.com/lz4/lz4/blob/1b819bfd633ae285df2dfe1b0589e1ec064f2873/lib/lz4hc.h#L48
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Compressor(new(Lz4))
+ _ = Decompressor(new(Lz4))
+)
+
+// DefaultLz4 is a default instance that is conveniently ready to use.
+var DefaultLz4 = NewLz4()
diff --git a/vendor/github.com/mholt/archiver/v3/rar.go b/vendor/github.com/mholt/archiver/v3/rar.go
new file mode 100644
index 0000000000..84eabda820
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/rar.go
@@ -0,0 +1,409 @@
+package archiver
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/nwaples/rardecode"
+)
+
+// Rar provides facilities for reading RAR archives.
+// See https://www.rarlab.com/technote.htm.
+type Rar struct {
+ // Whether to overwrite existing files; if false,
+ // an error is returned if the file exists.
+ OverwriteExisting bool
+
+ // Whether to make all the directories necessary
+ // to create a rar archive in the desired path.
+ MkdirAll bool
+
+ // A single top-level folder can be implicitly
+ // created by the Unarchive method if the files
+ // to be extracted from the archive do not all
+ // have a common root. This roughly mimics the
+ // behavior of archival tools integrated into OS
+ // file browsers which create a subfolder to
+ // avoid unexpectedly littering the destination
+ // folder with potentially many files, causing a
+ // problematic cleanup/organization situation.
+ // This feature is available for both creation
+ // and extraction of archives, but may be slightly
+ // inefficient with lots and lots of files,
+ // especially on extraction.
+ ImplicitTopLevelFolder bool
+
+ // If true, errors encountered during reading
+ // or writing a single file will be logged and
+ // the operation will continue on remaining files.
+ ContinueOnError bool
+
+ // The password to open archives (optional).
+ Password string
+
+ rr *rardecode.Reader // underlying stream reader
+ rc *rardecode.ReadCloser // supports multi-volume archives (files only)
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*Rar) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".rar") {
+ return fmt.Errorf("filename must have a .rar extension")
+ }
+ return nil
+}
+
+// Unarchive unpacks the .rar file at source to destination.
+// Destination will be treated as a folder name. It supports
+// multi-volume archives.
+func (r *Rar) Unarchive(source, destination string) error {
+ if !fileExists(destination) && r.MkdirAll {
+ err := mkdir(destination, 0755)
+ if err != nil {
+ return fmt.Errorf("preparing destination: %v", err)
+ }
+ }
+
+ // if the files in the archive do not all share a common
+ // root, then make sure we extract to a single subfolder
+ // rather than potentially littering the destination...
+ if r.ImplicitTopLevelFolder {
+ var err error
+ destination, err = r.addTopLevelFolder(source, destination)
+ if err != nil {
+ return fmt.Errorf("scanning source archive: %v", err)
+ }
+ }
+
+ err := r.OpenFile(source)
+ if err != nil {
+ return fmt.Errorf("opening rar archive for reading: %v", err)
+ }
+ defer r.Close()
+
+ for {
+ err := r.unrarNext(destination)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ if r.ContinueOnError {
+ log.Printf("[ERROR] Reading file in rar archive: %v", err)
+ continue
+ }
+ return fmt.Errorf("reading file in rar archive: %v", err)
+ }
+ }
+
+ return nil
+}
+
+// addTopLevelFolder scans the files contained inside
+// the tarball named sourceArchive and returns a modified
+// destination if all the files do not share the same
+// top-level folder.
+func (r *Rar) addTopLevelFolder(sourceArchive, destination string) (string, error) {
+ file, err := os.Open(sourceArchive)
+ if err != nil {
+ return "", fmt.Errorf("opening source archive: %v", err)
+ }
+ defer file.Close()
+
+ rc, err := rardecode.NewReader(file, r.Password)
+ if err != nil {
+ return "", fmt.Errorf("creating archive reader: %v", err)
+ }
+
+ var files []string
+ for {
+ hdr, err := rc.Next()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return "", fmt.Errorf("scanning tarball's file listing: %v", err)
+ }
+ files = append(files, hdr.Name)
+ }
+
+ if multipleTopLevels(files) {
+ destination = filepath.Join(destination, folderNameFromFileName(sourceArchive))
+ }
+
+ return destination, nil
+}
+
+func (r *Rar) unrarNext(to string) error {
+ f, err := r.Read()
+ if err != nil {
+ return err // don't wrap error; calling loop must break on io.EOF
+ }
+ header, ok := f.Header.(*rardecode.FileHeader)
+ if !ok {
+ return fmt.Errorf("expected header to be *rardecode.FileHeader but was %T", f.Header)
+ }
+ return r.unrarFile(f, filepath.Join(to, header.Name))
+}
+
+func (r *Rar) unrarFile(f File, to string) error {
+ // do not overwrite existing files, if configured
+ if !f.IsDir() && !r.OverwriteExisting && fileExists(to) {
+ return fmt.Errorf("file already exists: %s", to)
+ }
+
+ hdr, ok := f.Header.(*rardecode.FileHeader)
+ if !ok {
+ return fmt.Errorf("expected header to be *rardecode.FileHeader but was %T", f.Header)
+ }
+
+ if f.IsDir() {
+ if fileExists("testdata") {
+ err := os.Chmod(to, hdr.Mode())
+ if err != nil {
+ return fmt.Errorf("changing dir mode: %v", err)
+ }
+ } else {
+ err := mkdir(to, hdr.Mode())
+ if err != nil {
+ return fmt.Errorf("making directories: %v", err)
+ }
+ }
+ return nil
+ }
+
+ // if files come before their containing folders, then we must
+ // create their folders before writing the file
+ err := mkdir(filepath.Dir(to), 0755)
+ if err != nil {
+ return fmt.Errorf("making parent directories: %v", err)
+ }
+
+ if (hdr.Mode() & os.ModeSymlink) != 0 {
+ return nil
+ }
+
+ return writeNewFile(to, r.rr, hdr.Mode())
+}
+
+// OpenFile opens filename for reading. This method supports
+// multi-volume archives, whereas Open does not (but Open
+// supports any stream, not just files).
+func (r *Rar) OpenFile(filename string) error {
+ if r.rr != nil {
+ return fmt.Errorf("rar archive is already open for reading")
+ }
+ var err error
+ r.rc, err = rardecode.OpenReader(filename, r.Password)
+ if err != nil {
+ return err
+ }
+ r.rr = &r.rc.Reader
+ return nil
+}
+
+// Open opens t for reading an archive from
+// in. The size parameter is not used.
+func (r *Rar) Open(in io.Reader, size int64) error {
+ if r.rr != nil {
+ return fmt.Errorf("rar archive is already open for reading")
+ }
+ var err error
+ r.rr, err = rardecode.NewReader(in, r.Password)
+ return err
+}
+
+// Read reads the next file from t, which must have
+// already been opened for reading. If there are no
+// more files, the error is io.EOF. The File must
+// be closed when finished reading from it.
+func (r *Rar) Read() (File, error) {
+ if r.rr == nil {
+ return File{}, fmt.Errorf("rar archive is not open")
+ }
+
+ hdr, err := r.rr.Next()
+ if err != nil {
+ return File{}, err // don't wrap error; preserve io.EOF
+ }
+
+ file := File{
+ FileInfo: rarFileInfo{hdr},
+ Header: hdr,
+ ReadCloser: ReadFakeCloser{r.rr},
+ }
+
+ return file, nil
+}
+
+// Close closes the rar archive(s) opened by Create and Open.
+func (r *Rar) Close() error {
+ var err error
+ if r.rc != nil {
+ rc := r.rc
+ r.rc = nil
+ err = rc.Close()
+ }
+ if r.rr != nil {
+ r.rr = nil
+ }
+ return err
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (r *Rar) Walk(archive string, walkFn WalkFunc) error {
+ file, err := os.Open(archive)
+ if err != nil {
+ return fmt.Errorf("opening archive file: %v", err)
+ }
+ defer file.Close()
+
+ err = r.Open(file, 0)
+ if err != nil {
+ return fmt.Errorf("opening archive: %v", err)
+ }
+ defer r.Close()
+
+ for {
+ f, err := r.Read()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ if r.ContinueOnError {
+ log.Printf("[ERROR] Opening next file: %v", err)
+ continue
+ }
+ return fmt.Errorf("opening next file: %v", err)
+ }
+ err = walkFn(f)
+ if err != nil {
+ if err == ErrStopWalk {
+ break
+ }
+ if r.ContinueOnError {
+ log.Printf("[ERROR] Walking %s: %v", f.Name(), err)
+ continue
+ }
+ return fmt.Errorf("walking %s: %v", f.Name(), err)
+ }
+ }
+
+ return nil
+}
+
+// Extract extracts a single file from the rar archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (r *Rar) Extract(source, target, destination string) error {
+ // target refers to a path inside the archive, which should be clean also
+ target = path.Clean(target)
+
+ // if the target ends up being a directory, then
+ // we will continue walking and extracting files
+ // until we are no longer within that directory
+ var targetDirPath string
+
+ return r.Walk(source, func(f File) error {
+ th, ok := f.Header.(*rardecode.FileHeader)
+ if !ok {
+ return fmt.Errorf("expected header to be *rardecode.FileHeader but was %T", f.Header)
+ }
+
+ // importantly, cleaning the path strips tailing slash,
+ // which must be appended to folders within the archive
+ name := path.Clean(th.Name)
+ if f.IsDir() && target == name {
+ targetDirPath = path.Dir(name)
+ }
+
+ if within(target, th.Name) {
+ // either this is the exact file we want, or is
+ // in the directory we want to extract
+
+ // build the filename we will extract to
+ end, err := filepath.Rel(targetDirPath, th.Name)
+ if err != nil {
+ return fmt.Errorf("relativizing paths: %v", err)
+ }
+ joined := filepath.Join(destination, end)
+
+ err = r.unrarFile(f, joined)
+ if err != nil {
+ return fmt.Errorf("extracting file %s: %v", th.Name, err)
+ }
+
+ // if our target was not a directory, stop walk
+ if targetDirPath == "" {
+ return ErrStopWalk
+ }
+ } else if targetDirPath != "" {
+ // finished walking the entire directory
+ return ErrStopWalk
+ }
+
+ return nil
+ })
+}
+
+// Match returns true if the format of file matches this
+// type's format. It should not affect reader position.
+func (*Rar) Match(file io.ReadSeeker) (bool, error) {
+ currentPos, err := file.Seek(0, io.SeekCurrent)
+ if err != nil {
+ return false, err
+ }
+ _, err = file.Seek(0, 0)
+ if err != nil {
+ return false, err
+ }
+ defer file.Seek(currentPos, io.SeekStart)
+
+ buf := make([]byte, 8)
+ if n, err := file.Read(buf); err != nil || n < 8 {
+ return false, nil
+ }
+ hasTarHeader := bytes.Equal(buf[:7], []byte("Rar!\x1a\x07\x00")) || // ver 1.5
+ bytes.Equal(buf, []byte("Rar!\x1a\x07\x01\x00")) // ver 5.0
+ return hasTarHeader, nil
+}
+
+func (r *Rar) String() string { return "rar" }
+
+// NewRar returns a new, default instance ready to be customized and used.
+func NewRar() *Rar {
+ return &Rar{
+ MkdirAll: true,
+ }
+}
+
+type rarFileInfo struct {
+ fh *rardecode.FileHeader
+}
+
+func (rfi rarFileInfo) Name() string { return rfi.fh.Name }
+func (rfi rarFileInfo) Size() int64 { return rfi.fh.UnPackedSize }
+func (rfi rarFileInfo) Mode() os.FileMode { return rfi.fh.Mode() }
+func (rfi rarFileInfo) ModTime() time.Time { return rfi.fh.ModificationTime }
+func (rfi rarFileInfo) IsDir() bool { return rfi.fh.IsDir }
+func (rfi rarFileInfo) Sys() interface{} { return nil }
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(Rar))
+ _ = Unarchiver(new(Rar))
+ _ = Walker(new(Rar))
+ _ = Extractor(new(Rar))
+ _ = Matcher(new(Rar))
+ _ = ExtensionChecker(new(Rar))
+ _ = os.FileInfo(rarFileInfo{})
+)
+
+// DefaultRar is a default instance that is conveniently ready to use.
+var DefaultRar = NewRar()
diff --git a/vendor/github.com/mholt/archiver/v3/sz.go b/vendor/github.com/mholt/archiver/v3/sz.go
new file mode 100644
index 0000000000..39c5865efe
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/sz.go
@@ -0,0 +1,51 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+
+ "github.com/golang/snappy"
+)
+
+// Snappy facilitates Snappy compression.
+type Snappy struct{}
+
+// Compress reads in, compresses it, and writes it to out.
+func (s *Snappy) Compress(in io.Reader, out io.Writer) error {
+ w := snappy.NewWriter(out)
+ defer w.Close()
+ _, err := io.Copy(w, in)
+ return err
+}
+
+// Decompress reads in, decompresses it, and writes it to out.
+func (s *Snappy) Decompress(in io.Reader, out io.Writer) error {
+ r := snappy.NewReader(in)
+ _, err := io.Copy(out, r)
+ return err
+}
+
+// CheckExt ensures the file extension matches the format.
+func (s *Snappy) CheckExt(filename string) error {
+ if filepath.Ext(filename) != ".sz" {
+ return fmt.Errorf("filename must have a .sz extension")
+ }
+ return nil
+}
+
+func (s *Snappy) String() string { return "sz" }
+
+// NewSnappy returns a new, default instance ready to be customized and used.
+func NewSnappy() *Snappy {
+ return new(Snappy)
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Compressor(new(Snappy))
+ _ = Decompressor(new(Snappy))
+)
+
+// DefaultSnappy is a default instance that is conveniently ready to use.
+var DefaultSnappy = NewSnappy()
diff --git a/vendor/github.com/mholt/archiver/v3/tar.go b/vendor/github.com/mholt/archiver/v3/tar.go
new file mode 100644
index 0000000000..e983531533
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/tar.go
@@ -0,0 +1,616 @@
+package archiver
+
+import (
+ "archive/tar"
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+// Tar provides facilities for operating TAR archives.
+// See http://www.gnu.org/software/tar/manual/html_node/Standard.html.
+type Tar struct {
+ // Whether to overwrite existing files; if false,
+ // an error is returned if the file exists.
+ OverwriteExisting bool
+
+ // Whether to make all the directories necessary
+ // to create a tar archive in the desired path.
+ MkdirAll bool
+
+ // A single top-level folder can be implicitly
+ // created by the Archive or Unarchive methods
+ // if the files to be added to the archive
+ // or the files to be extracted from the archive
+ // do not all have a common root. This roughly
+ // mimics the behavior of archival tools integrated
+ // into OS file browsers which create a subfolder
+ // to avoid unexpectedly littering the destination
+ // folder with potentially many files, causing a
+ // problematic cleanup/organization situation.
+ // This feature is available for both creation
+ // and extraction of archives, but may be slightly
+ // inefficient with lots and lots of files,
+ // especially on extraction.
+ ImplicitTopLevelFolder bool
+
+ // If true, errors encountered during reading
+ // or writing a single file will be logged and
+ // the operation will continue on remaining files.
+ ContinueOnError bool
+
+ tw *tar.Writer
+ tr *tar.Reader
+
+ readerWrapFn func(io.Reader) (io.Reader, error)
+ writerWrapFn func(io.Writer) (io.Writer, error)
+ cleanupWrapFn func()
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*Tar) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".tar") {
+ return fmt.Errorf("filename must have a .tar extension")
+ }
+ return nil
+}
+
+// Archive creates a tarball file at destination containing
+// the files listed in sources. The destination must end with
+// ".tar". File paths can be those of regular files or
+// directories; directories will be recursively added.
+func (t *Tar) Archive(sources []string, destination string) error {
+ err := t.CheckExt(destination)
+ if t.writerWrapFn == nil && err != nil {
+ return fmt.Errorf("checking extension: %v", err)
+ }
+ if !t.OverwriteExisting && fileExists(destination) {
+ return fmt.Errorf("file already exists: %s", destination)
+ }
+
+ // make the folder to contain the resulting archive
+ // if it does not already exist
+ destDir := filepath.Dir(destination)
+ if t.MkdirAll && !fileExists(destDir) {
+ err := mkdir(destDir, 0755)
+ if err != nil {
+ return fmt.Errorf("making folder for destination: %v", err)
+ }
+ }
+
+ out, err := os.Create(destination)
+ if err != nil {
+ return fmt.Errorf("creating %s: %v", destination, err)
+ }
+ defer out.Close()
+
+ err = t.Create(out)
+ if err != nil {
+ return fmt.Errorf("creating tar: %v", err)
+ }
+ defer t.Close()
+
+ var topLevelFolder string
+ if t.ImplicitTopLevelFolder && multipleTopLevels(sources) {
+ topLevelFolder = folderNameFromFileName(destination)
+ }
+
+ for _, source := range sources {
+ err := t.writeWalk(source, topLevelFolder, destination)
+ if err != nil {
+ return fmt.Errorf("walking %s: %v", source, err)
+ }
+ }
+
+ return nil
+}
+
+// Unarchive unpacks the .tar file at source to destination.
+// Destination will be treated as a folder name.
+func (t *Tar) Unarchive(source, destination string) error {
+ if !fileExists(destination) && t.MkdirAll {
+ err := mkdir(destination, 0755)
+ if err != nil {
+ return fmt.Errorf("preparing destination: %v", err)
+ }
+ }
+
+ // if the files in the archive do not all share a common
+ // root, then make sure we extract to a single subfolder
+ // rather than potentially littering the destination...
+ if t.ImplicitTopLevelFolder {
+ var err error
+ destination, err = t.addTopLevelFolder(source, destination)
+ if err != nil {
+ return fmt.Errorf("scanning source archive: %v", err)
+ }
+ }
+
+ file, err := os.Open(source)
+ if err != nil {
+ return fmt.Errorf("opening source archive: %v", err)
+ }
+ defer file.Close()
+
+ err = t.Open(file, 0)
+ if err != nil {
+ return fmt.Errorf("opening tar archive for reading: %v", err)
+ }
+ defer t.Close()
+
+ for {
+ err := t.untarNext(destination)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ if t.ContinueOnError {
+ log.Printf("[ERROR] Reading file in tar archive: %v", err)
+ continue
+ }
+ return fmt.Errorf("reading file in tar archive: %v", err)
+ }
+ }
+
+ return nil
+}
+
+// addTopLevelFolder scans the files contained inside
+// the tarball named sourceArchive and returns a modified
+// destination if all the files do not share the same
+// top-level folder.
+func (t *Tar) addTopLevelFolder(sourceArchive, destination string) (string, error) {
+ file, err := os.Open(sourceArchive)
+ if err != nil {
+ return "", fmt.Errorf("opening source archive: %v", err)
+ }
+ defer file.Close()
+
+ // if the reader is to be wrapped, ensure we do that now
+ // or we will not be able to read the archive successfully
+ reader := io.Reader(file)
+ if t.readerWrapFn != nil {
+ reader, err = t.readerWrapFn(reader)
+ if err != nil {
+ return "", fmt.Errorf("wrapping reader: %v", err)
+ }
+ }
+ if t.cleanupWrapFn != nil {
+ defer t.cleanupWrapFn()
+ }
+
+ tr := tar.NewReader(reader)
+
+ var files []string
+ for {
+ hdr, err := tr.Next()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return "", fmt.Errorf("scanning tarball's file listing: %v", err)
+ }
+ files = append(files, hdr.Name)
+ }
+
+ if multipleTopLevels(files) {
+ destination = filepath.Join(destination, folderNameFromFileName(sourceArchive))
+ }
+
+ return destination, nil
+}
+
+func (t *Tar) untarNext(to string) error {
+ f, err := t.Read()
+ if err != nil {
+ return err // don't wrap error; calling loop must break on io.EOF
+ }
+ header, ok := f.Header.(*tar.Header)
+ if !ok {
+ return fmt.Errorf("expected header to be *tar.Header but was %T", f.Header)
+ }
+ return t.untarFile(f, filepath.Join(to, header.Name))
+}
+
+func (t *Tar) untarFile(f File, to string) error {
+ // do not overwrite existing files, if configured
+ if !f.IsDir() && !t.OverwriteExisting && fileExists(to) {
+ return fmt.Errorf("file already exists: %s", to)
+ }
+
+ hdr, ok := f.Header.(*tar.Header)
+ if !ok {
+ return fmt.Errorf("expected header to be *tar.Header but was %T", f.Header)
+ }
+
+ switch hdr.Typeflag {
+ case tar.TypeDir:
+ return mkdir(to, f.Mode())
+ case tar.TypeReg, tar.TypeRegA, tar.TypeChar, tar.TypeBlock, tar.TypeFifo, tar.TypeGNUSparse:
+ return writeNewFile(to, f, f.Mode())
+ case tar.TypeSymlink:
+ return writeNewSymbolicLink(to, hdr.Linkname)
+ case tar.TypeLink:
+ return writeNewHardLink(to, filepath.Join(to, hdr.Linkname))
+ case tar.TypeXGlobalHeader:
+ return nil // ignore the pax global header from git-generated tarballs
+ default:
+ return fmt.Errorf("%s: unknown type flag: %c", hdr.Name, hdr.Typeflag)
+ }
+}
+
+func (t *Tar) writeWalk(source, topLevelFolder, destination string) error {
+ sourceInfo, err := os.Stat(source)
+ if err != nil {
+ return fmt.Errorf("%s: stat: %v", source, err)
+ }
+ destAbs, err := filepath.Abs(destination)
+ if err != nil {
+ return fmt.Errorf("%s: getting absolute path of destination %s: %v", source, destination, err)
+ }
+
+ return filepath.Walk(source, func(fpath string, info os.FileInfo, err error) error {
+ handleErr := func(err error) error {
+ if t.ContinueOnError {
+ log.Printf("[ERROR] Walking %s: %v", fpath, err)
+ return nil
+ }
+ return err
+ }
+ if err != nil {
+ return handleErr(fmt.Errorf("traversing %s: %v", fpath, err))
+ }
+ if info == nil {
+ return handleErr(fmt.Errorf("no file info"))
+ }
+
+ // make sure we do not copy our output file into itself
+ fpathAbs, err := filepath.Abs(fpath)
+ if err != nil {
+ return handleErr(fmt.Errorf("%s: getting absolute path: %v", fpath, err))
+ }
+ if within(fpathAbs, destAbs) {
+ return nil
+ }
+
+ // build the name to be used within the archive
+ nameInArchive, err := makeNameInArchive(sourceInfo, source, topLevelFolder, fpath)
+ if err != nil {
+ return handleErr(err)
+ }
+
+ var file io.ReadCloser
+ if info.Mode().IsRegular() {
+ file, err = os.Open(fpath)
+ if err != nil {
+ return handleErr(fmt.Errorf("%s: opening: %v", fpath, err))
+ }
+ defer file.Close()
+ }
+ err = t.Write(File{
+ FileInfo: FileInfo{
+ FileInfo: info,
+ CustomName: nameInArchive,
+ },
+ ReadCloser: file,
+ })
+ if err != nil {
+ return handleErr(fmt.Errorf("%s: writing: %s", fpath, err))
+ }
+
+ return nil
+ })
+}
+
+// Create opens t for writing a tar archive to out.
+func (t *Tar) Create(out io.Writer) error {
+ if t.tw != nil {
+ return fmt.Errorf("tar archive is already created for writing")
+ }
+
+ // wrapping writers allows us to output
+ // compressed tarballs, for example
+ if t.writerWrapFn != nil {
+ var err error
+ out, err = t.writerWrapFn(out)
+ if err != nil {
+ return fmt.Errorf("wrapping writer: %v", err)
+ }
+ }
+
+ t.tw = tar.NewWriter(out)
+ return nil
+}
+
+// Write writes f to t, which must have been opened for writing first.
+func (t *Tar) Write(f File) error {
+ if t.tw == nil {
+ return fmt.Errorf("tar archive was not created for writing first")
+ }
+ if f.FileInfo == nil {
+ return fmt.Errorf("no file info")
+ }
+ if f.FileInfo.Name() == "" {
+ return fmt.Errorf("missing file name")
+ }
+
+ var linkTarget string
+ if isSymlink(f) {
+ var err error
+ linkTarget, err = os.Readlink(f.Name())
+ if err != nil {
+ return fmt.Errorf("%s: readlink: %v", f.Name(), err)
+ }
+ }
+
+ hdr, err := tar.FileInfoHeader(f, filepath.ToSlash(linkTarget))
+ if err != nil {
+ return fmt.Errorf("%s: making header: %v", f.Name(), err)
+ }
+
+ err = t.tw.WriteHeader(hdr)
+ if err != nil {
+ return fmt.Errorf("%s: writing header: %v", hdr.Name, err)
+ }
+
+ if f.IsDir() {
+ return nil // directories have no contents
+ }
+
+ if hdr.Typeflag == tar.TypeReg {
+ if f.ReadCloser == nil {
+ return fmt.Errorf("%s: no way to read file contents", f.Name())
+ }
+ _, err := io.Copy(t.tw, f)
+ if err != nil {
+ return fmt.Errorf("%s: copying contents: %v", f.Name(), err)
+ }
+ }
+
+ return nil
+}
+
+// Open opens t for reading an archive from
+// in. The size parameter is not used.
+func (t *Tar) Open(in io.Reader, size int64) error {
+ if t.tr != nil {
+ return fmt.Errorf("tar archive is already open for reading")
+ }
+ // wrapping readers allows us to open compressed tarballs
+ if t.readerWrapFn != nil {
+ var err error
+ in, err = t.readerWrapFn(in)
+ if err != nil {
+ return fmt.Errorf("wrapping file reader: %v", err)
+ }
+ }
+ t.tr = tar.NewReader(in)
+ return nil
+}
+
+// Read reads the next file from t, which must have
+// already been opened for reading. If there are no
+// more files, the error is io.EOF. The File must
+// be closed when finished reading from it.
+func (t *Tar) Read() (File, error) {
+ if t.tr == nil {
+ return File{}, fmt.Errorf("tar archive is not open")
+ }
+
+ hdr, err := t.tr.Next()
+ if err != nil {
+ return File{}, err // don't wrap error; preserve io.EOF
+ }
+
+ file := File{
+ FileInfo: hdr.FileInfo(),
+ Header: hdr,
+ ReadCloser: ReadFakeCloser{t.tr},
+ }
+
+ return file, nil
+}
+
+// Close closes the tar archive(s) opened by Create and Open.
+func (t *Tar) Close() error {
+ var err error
+ if t.tr != nil {
+ t.tr = nil
+ }
+ if t.tw != nil {
+ tw := t.tw
+ t.tw = nil
+ err = tw.Close()
+ }
+ // make sure cleanup of "Reader/Writer wrapper"
+ // (say that ten times fast) happens AFTER the
+ // underlying stream is closed
+ if t.cleanupWrapFn != nil {
+ t.cleanupWrapFn()
+ }
+ return err
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (t *Tar) Walk(archive string, walkFn WalkFunc) error {
+ file, err := os.Open(archive)
+ if err != nil {
+ return fmt.Errorf("opening archive file: %v", err)
+ }
+ defer file.Close()
+
+ err = t.Open(file, 0)
+ if err != nil {
+ return fmt.Errorf("opening archive: %v", err)
+ }
+ defer t.Close()
+
+ for {
+ f, err := t.Read()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ if t.ContinueOnError {
+ log.Printf("[ERROR] Opening next file: %v", err)
+ continue
+ }
+ return fmt.Errorf("opening next file: %v", err)
+ }
+ err = walkFn(f)
+ if err != nil {
+ if err == ErrStopWalk {
+ break
+ }
+ if t.ContinueOnError {
+ log.Printf("[ERROR] Walking %s: %v", f.Name(), err)
+ continue
+ }
+ return fmt.Errorf("walking %s: %v", f.Name(), err)
+ }
+ }
+
+ return nil
+}
+
+// Extract extracts a single file from the tar archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (t *Tar) Extract(source, target, destination string) error {
+ // target refers to a path inside the archive, which should be clean also
+ target = path.Clean(target)
+
+ // if the target ends up being a directory, then
+ // we will continue walking and extracting files
+ // until we are no longer within that directory
+ var targetDirPath string
+
+ return t.Walk(source, func(f File) error {
+ th, ok := f.Header.(*tar.Header)
+ if !ok {
+ return fmt.Errorf("expected header to be *tar.Header but was %T", f.Header)
+ }
+
+ // importantly, cleaning the path strips tailing slash,
+ // which must be appended to folders within the archive
+ name := path.Clean(th.Name)
+ if f.IsDir() && target == name {
+ targetDirPath = path.Dir(name)
+ }
+
+ if within(target, th.Name) {
+ // either this is the exact file we want, or is
+ // in the directory we want to extract
+
+ // build the filename we will extract to
+ end, err := filepath.Rel(targetDirPath, th.Name)
+ if err != nil {
+ return fmt.Errorf("relativizing paths: %v", err)
+ }
+ joined := filepath.Join(destination, end)
+
+ err = t.untarFile(f, joined)
+ if err != nil {
+ return fmt.Errorf("extracting file %s: %v", th.Name, err)
+ }
+
+ // if our target was not a directory, stop walk
+ if targetDirPath == "" {
+ return ErrStopWalk
+ }
+ } else if targetDirPath != "" {
+ // finished walking the entire directory
+ return ErrStopWalk
+ }
+
+ return nil
+ })
+}
+
+// Match returns true if the format of file matches this
+// type's format. It should not affect reader position.
+func (*Tar) Match(file io.ReadSeeker) (bool, error) {
+ currentPos, err := file.Seek(0, io.SeekCurrent)
+ if err != nil {
+ return false, err
+ }
+ _, err = file.Seek(0, 0)
+ if err != nil {
+ return false, err
+ }
+ defer file.Seek(currentPos, io.SeekStart)
+
+ buf := make([]byte, tarBlockSize)
+ if _, err = io.ReadFull(file, buf); err != nil {
+ return false, nil
+ }
+ return hasTarHeader(buf), nil
+}
+
+// hasTarHeader checks passed bytes has a valid tar header or not. buf must
+// contain at least 512 bytes and if not, it always returns false.
+func hasTarHeader(buf []byte) bool {
+ if len(buf) < tarBlockSize {
+ return false
+ }
+
+ b := buf[148:156]
+ b = bytes.Trim(b, " \x00") // clean up all spaces and null bytes
+ if len(b) == 0 {
+ return false // unknown format
+ }
+ hdrSum, err := strconv.ParseUint(string(b), 8, 64)
+ if err != nil {
+ return false
+ }
+
+ // According to the go official archive/tar, Sun tar uses signed byte
+ // values so this calcs both signed and unsigned
+ var usum uint64
+ var sum int64
+ for i, c := range buf {
+ if 148 <= i && i < 156 {
+ c = ' ' // checksum field itself is counted as branks
+ }
+ usum += uint64(uint8(c))
+ sum += int64(int8(c))
+ }
+
+ if hdrSum != usum && int64(hdrSum) != sum {
+ return false // invalid checksum
+ }
+
+ return true
+}
+
+func (t *Tar) String() string { return "tar" }
+
+// NewTar returns a new, default instance ready to be customized and used.
+func NewTar() *Tar {
+ return &Tar{
+ MkdirAll: true,
+ }
+}
+
+const tarBlockSize = 512
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(Tar))
+ _ = Writer(new(Tar))
+ _ = Archiver(new(Tar))
+ _ = Unarchiver(new(Tar))
+ _ = Walker(new(Tar))
+ _ = Extractor(new(Tar))
+ _ = Matcher(new(Tar))
+ _ = ExtensionChecker(new(Tar))
+)
+
+// DefaultTar is a default instance that is conveniently ready to use.
+var DefaultTar = NewTar()
diff --git a/vendor/github.com/mholt/archiver/v3/tarbrotli.go b/vendor/github.com/mholt/archiver/v3/tarbrotli.go
new file mode 100644
index 0000000000..83a455d66a
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/tarbrotli.go
@@ -0,0 +1,114 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/andybalholm/brotli"
+)
+
+// TarBrotli facilitates brotli compression of tarball archives.
+type TarBrotli struct {
+ *Tar
+ Quality int
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*TarBrotli) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".tar.br") &&
+ !strings.HasSuffix(filename, ".tbr") {
+ return fmt.Errorf("filename must have a .tar.br or .tbr extension")
+ }
+ return nil
+}
+
+// Archive creates a compressed tar file at destination
+// containing the files listed in sources. The destination
+// must end with ".tar.br" or ".tbr". File paths can be
+// those of regular files or directories; directories will
+// be recursively added.
+func (tbr *TarBrotli) Archive(sources []string, destination string) error {
+ err := tbr.CheckExt(destination)
+ if err != nil {
+ return fmt.Errorf("output %s", err.Error())
+ }
+ tbr.wrapWriter()
+ return tbr.Tar.Archive(sources, destination)
+}
+
+// Unarchive unpacks the compressed tarball at
+// source to destination. Destination will be
+// treated as a folder name.
+func (tbr *TarBrotli) Unarchive(source, destination string) error {
+ tbr.wrapReader()
+ return tbr.Tar.Unarchive(source, destination)
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (tbr *TarBrotli) Walk(archive string, walkFn WalkFunc) error {
+ tbr.wrapReader()
+ return tbr.Tar.Walk(archive, walkFn)
+}
+
+// Create opens txz for writing a compressed
+// tar archive to out.
+func (tbr *TarBrotli) Create(out io.Writer) error {
+ tbr.wrapWriter()
+ return tbr.Tar.Create(out)
+}
+
+// Open opens t for reading a compressed archive from
+// in. The size parameter is not used.
+func (tbr *TarBrotli) Open(in io.Reader, size int64) error {
+ tbr.wrapReader()
+ return tbr.Tar.Open(in, size)
+}
+
+// Extract extracts a single file from the tar archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (tbr *TarBrotli) Extract(source, target, destination string) error {
+ tbr.wrapReader()
+ return tbr.Tar.Extract(source, target, destination)
+}
+
+func (tbr *TarBrotli) wrapWriter() {
+ var brw *brotli.Writer
+ tbr.Tar.writerWrapFn = func(w io.Writer) (io.Writer, error) {
+ brw = brotli.NewWriterLevel(w, tbr.Quality)
+ return brw, nil
+ }
+ tbr.Tar.cleanupWrapFn = func() {
+ brw.Close()
+ }
+}
+
+func (tbr *TarBrotli) wrapReader() {
+ tbr.Tar.readerWrapFn = func(r io.Reader) (io.Reader, error) {
+ return brotli.NewReader(r), nil
+ }
+}
+
+func (tbr *TarBrotli) String() string { return "tar.br" }
+
+// NewTarBrotli returns a new, default instance ready to be customized and used.
+func NewTarBrotli() *TarBrotli {
+ return &TarBrotli{
+ Tar: NewTar(),
+ Quality: brotli.DefaultCompression,
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(TarBrotli))
+ _ = Writer(new(TarBrotli))
+ _ = Archiver(new(TarBrotli))
+ _ = Unarchiver(new(TarBrotli))
+ _ = Walker(new(TarBrotli))
+ _ = Extractor(new(TarBrotli))
+)
+
+// DefaultTarBrotli is a convenient archiver ready to use.
+var DefaultTarBrotli = NewTarBrotli()
diff --git a/vendor/github.com/mholt/archiver/v3/tarbz2.go b/vendor/github.com/mholt/archiver/v3/tarbz2.go
new file mode 100644
index 0000000000..e5870a7d29
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/tarbz2.go
@@ -0,0 +1,126 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/dsnet/compress/bzip2"
+)
+
+// TarBz2 facilitates bzip2 compression
+// (https://github.com/dsnet/compress/blob/master/doc/bzip2-format.pdf)
+// of tarball archives.
+type TarBz2 struct {
+ *Tar
+
+ CompressionLevel int
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*TarBz2) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".tar.bz2") &&
+ !strings.HasSuffix(filename, ".tbz2") {
+ return fmt.Errorf("filename must have a .tar.bz2 or .tbz2 extension")
+ }
+ return nil
+}
+
+// Archive creates a compressed tar file at destination
+// containing the files listed in sources. The destination
+// must end with ".tar.bz2" or ".tbz2". File paths can be
+// those of regular files or directories; directories will
+// be recursively added.
+func (tbz2 *TarBz2) Archive(sources []string, destination string) error {
+ err := tbz2.CheckExt(destination)
+ if err != nil {
+ return fmt.Errorf("output %s", err.Error())
+ }
+ tbz2.wrapWriter()
+ return tbz2.Tar.Archive(sources, destination)
+}
+
+// Unarchive unpacks the compressed tarball at
+// source to destination. Destination will be
+// treated as a folder name.
+func (tbz2 *TarBz2) Unarchive(source, destination string) error {
+ tbz2.wrapReader()
+ return tbz2.Tar.Unarchive(source, destination)
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (tbz2 *TarBz2) Walk(archive string, walkFn WalkFunc) error {
+ tbz2.wrapReader()
+ return tbz2.Tar.Walk(archive, walkFn)
+}
+
+// Create opens tbz2 for writing a compressed
+// tar archive to out.
+func (tbz2 *TarBz2) Create(out io.Writer) error {
+ tbz2.wrapWriter()
+ return tbz2.Tar.Create(out)
+}
+
+// Open opens t for reading a compressed archive from
+// in. The size parameter is not used.
+func (tbz2 *TarBz2) Open(in io.Reader, size int64) error {
+ tbz2.wrapReader()
+ return tbz2.Tar.Open(in, size)
+}
+
+// Extract extracts a single file from the tar archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (tbz2 *TarBz2) Extract(source, target, destination string) error {
+ tbz2.wrapReader()
+ return tbz2.Tar.Extract(source, target, destination)
+}
+
+func (tbz2 *TarBz2) wrapWriter() {
+ var bz2w *bzip2.Writer
+ tbz2.Tar.writerWrapFn = func(w io.Writer) (io.Writer, error) {
+ var err error
+ bz2w, err = bzip2.NewWriter(w, &bzip2.WriterConfig{
+ Level: tbz2.CompressionLevel,
+ })
+ return bz2w, err
+ }
+ tbz2.Tar.cleanupWrapFn = func() {
+ bz2w.Close()
+ }
+}
+
+func (tbz2 *TarBz2) wrapReader() {
+ var bz2r *bzip2.Reader
+ tbz2.Tar.readerWrapFn = func(r io.Reader) (io.Reader, error) {
+ var err error
+ bz2r, err = bzip2.NewReader(r, nil)
+ return bz2r, err
+ }
+ tbz2.Tar.cleanupWrapFn = func() {
+ bz2r.Close()
+ }
+}
+
+func (tbz2 *TarBz2) String() string { return "tar.bz2" }
+
+// NewTarBz2 returns a new, default instance ready to be customized and used.
+func NewTarBz2() *TarBz2 {
+ return &TarBz2{
+ CompressionLevel: bzip2.DefaultCompression,
+ Tar: NewTar(),
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(TarBz2))
+ _ = Writer(new(TarBz2))
+ _ = Archiver(new(TarBz2))
+ _ = Unarchiver(new(TarBz2))
+ _ = Walker(new(TarBz2))
+ _ = Extractor(new(TarBz2))
+)
+
+// DefaultTarBz2 is a convenient archiver ready to use.
+var DefaultTarBz2 = NewTarBz2()
diff --git a/vendor/github.com/mholt/archiver/v3/targz.go b/vendor/github.com/mholt/archiver/v3/targz.go
new file mode 100644
index 0000000000..07777d0cfa
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/targz.go
@@ -0,0 +1,137 @@
+package archiver
+
+import (
+ "compress/gzip"
+ "fmt"
+ "io"
+ "strings"
+
+ pgzip "github.com/klauspost/pgzip"
+)
+
+// TarGz facilitates gzip compression
+// (RFC 1952) of tarball archives.
+type TarGz struct {
+ *Tar
+
+ // The compression level to use, as described
+ // in the compress/gzip package.
+ CompressionLevel int
+
+ // Disables parallel gzip.
+ SingleThreaded bool
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*TarGz) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".tar.gz") &&
+ !strings.HasSuffix(filename, ".tgz") {
+ return fmt.Errorf("filename must have a .tar.gz or .tgz extension")
+ }
+ return nil
+}
+
+// Archive creates a compressed tar file at destination
+// containing the files listed in sources. The destination
+// must end with ".tar.gz" or ".tgz". File paths can be
+// those of regular files or directories; directories will
+// be recursively added.
+func (tgz *TarGz) Archive(sources []string, destination string) error {
+ err := tgz.CheckExt(destination)
+ if err != nil {
+ return fmt.Errorf("output %s", err.Error())
+ }
+ tgz.wrapWriter()
+ return tgz.Tar.Archive(sources, destination)
+}
+
+// Unarchive unpacks the compressed tarball at
+// source to destination. Destination will be
+// treated as a folder name.
+func (tgz *TarGz) Unarchive(source, destination string) error {
+ tgz.wrapReader()
+ return tgz.Tar.Unarchive(source, destination)
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (tgz *TarGz) Walk(archive string, walkFn WalkFunc) error {
+ tgz.wrapReader()
+ return tgz.Tar.Walk(archive, walkFn)
+}
+
+// Create opens txz for writing a compressed
+// tar archive to out.
+func (tgz *TarGz) Create(out io.Writer) error {
+ tgz.wrapWriter()
+ return tgz.Tar.Create(out)
+}
+
+// Open opens t for reading a compressed archive from
+// in. The size parameter is not used.
+func (tgz *TarGz) Open(in io.Reader, size int64) error {
+ tgz.wrapReader()
+ return tgz.Tar.Open(in, size)
+}
+
+// Extract extracts a single file from the tar archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (tgz *TarGz) Extract(source, target, destination string) error {
+ tgz.wrapReader()
+ return tgz.Tar.Extract(source, target, destination)
+}
+
+func (tgz *TarGz) wrapWriter() {
+ var gzw io.WriteCloser
+ tgz.Tar.writerWrapFn = func(w io.Writer) (io.Writer, error) {
+ var err error
+ if tgz.SingleThreaded {
+ gzw, err = gzip.NewWriterLevel(w, tgz.CompressionLevel)
+ } else {
+ gzw, err = pgzip.NewWriterLevel(w, tgz.CompressionLevel)
+ }
+ return gzw, err
+ }
+ tgz.Tar.cleanupWrapFn = func() {
+ gzw.Close()
+ }
+}
+
+func (tgz *TarGz) wrapReader() {
+ var gzr io.ReadCloser
+ tgz.Tar.readerWrapFn = func(r io.Reader) (io.Reader, error) {
+ var err error
+ if tgz.SingleThreaded {
+ gzr, err = gzip.NewReader(r)
+ } else {
+ gzr, err = pgzip.NewReader(r)
+ }
+ return gzr, err
+ }
+ tgz.Tar.cleanupWrapFn = func() {
+ gzr.Close()
+ }
+}
+
+func (tgz *TarGz) String() string { return "tar.gz" }
+
+// NewTarGz returns a new, default instance ready to be customized and used.
+func NewTarGz() *TarGz {
+ return &TarGz{
+ CompressionLevel: gzip.DefaultCompression,
+ Tar: NewTar(),
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(TarGz))
+ _ = Writer(new(TarGz))
+ _ = Archiver(new(TarGz))
+ _ = Unarchiver(new(TarGz))
+ _ = Walker(new(TarGz))
+ _ = Extractor(new(TarGz))
+)
+
+// DefaultTarGz is a convenient archiver ready to use.
+var DefaultTarGz = NewTarGz()
diff --git a/vendor/github.com/mholt/archiver/v3/tarlz4.go b/vendor/github.com/mholt/archiver/v3/tarlz4.go
new file mode 100644
index 0000000000..4a178f691c
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/tarlz4.go
@@ -0,0 +1,122 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/pierrec/lz4"
+)
+
+// TarLz4 facilitates lz4 compression
+// (https://github.com/lz4/lz4/tree/master/doc)
+// of tarball archives.
+type TarLz4 struct {
+ *Tar
+
+ // The compression level to use when writing.
+ // Minimum 0 (fast compression), maximum 12
+ // (most space savings).
+ CompressionLevel int
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*TarLz4) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".tar.lz4") &&
+ !strings.HasSuffix(filename, ".tlz4") {
+
+ return fmt.Errorf("filename must have a .tar.lz4 or .tlz4 extension")
+ }
+ return nil
+}
+
+// Archive creates a compressed tar file at destination
+// containing the files listed in sources. The destination
+// must end with ".tar.lz4" or ".tlz4". File paths can be
+// those of regular files or directories; directories will
+// be recursively added.
+func (tlz4 *TarLz4) Archive(sources []string, destination string) error {
+ err := tlz4.CheckExt(destination)
+ if err != nil {
+ return fmt.Errorf("output %s", err.Error())
+ }
+ tlz4.wrapWriter()
+ return tlz4.Tar.Archive(sources, destination)
+}
+
+// Unarchive unpacks the compressed tarball at
+// source to destination. Destination will be
+// treated as a folder name.
+func (tlz4 *TarLz4) Unarchive(source, destination string) error {
+ tlz4.wrapReader()
+ return tlz4.Tar.Unarchive(source, destination)
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (tlz4 *TarLz4) Walk(archive string, walkFn WalkFunc) error {
+ tlz4.wrapReader()
+ return tlz4.Tar.Walk(archive, walkFn)
+}
+
+// Create opens tlz4 for writing a compressed
+// tar archive to out.
+func (tlz4 *TarLz4) Create(out io.Writer) error {
+ tlz4.wrapWriter()
+ return tlz4.Tar.Create(out)
+}
+
+// Open opens t for reading a compressed archive from
+// in. The size parameter is not used.
+func (tlz4 *TarLz4) Open(in io.Reader, size int64) error {
+ tlz4.wrapReader()
+ return tlz4.Tar.Open(in, size)
+}
+
+// Extract extracts a single file from the tar archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (tlz4 *TarLz4) Extract(source, target, destination string) error {
+ tlz4.wrapReader()
+ return tlz4.Tar.Extract(source, target, destination)
+}
+
+func (tlz4 *TarLz4) wrapWriter() {
+ var lz4w *lz4.Writer
+ tlz4.Tar.writerWrapFn = func(w io.Writer) (io.Writer, error) {
+ lz4w = lz4.NewWriter(w)
+ lz4w.Header.CompressionLevel = tlz4.CompressionLevel
+ return lz4w, nil
+ }
+ tlz4.Tar.cleanupWrapFn = func() {
+ lz4w.Close()
+ }
+}
+
+func (tlz4 *TarLz4) wrapReader() {
+ tlz4.Tar.readerWrapFn = func(r io.Reader) (io.Reader, error) {
+ return lz4.NewReader(r), nil
+ }
+}
+
+func (tlz4 *TarLz4) String() string { return "tar.lz4" }
+
+// NewTarLz4 returns a new, default instance ready to be customized and used.
+func NewTarLz4() *TarLz4 {
+ return &TarLz4{
+ CompressionLevel: 9, // https://github.com/lz4/lz4/blob/1b819bfd633ae285df2dfe1b0589e1ec064f2873/lib/lz4hc.h#L48
+ Tar: NewTar(),
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(TarLz4))
+ _ = Writer(new(TarLz4))
+ _ = Archiver(new(TarLz4))
+ _ = Unarchiver(new(TarLz4))
+ _ = Walker(new(TarLz4))
+ _ = Extractor(new(TarLz4))
+)
+
+// DefaultTarLz4 is a convenient archiver ready to use.
+var DefaultTarLz4 = NewTarLz4()
diff --git a/vendor/github.com/mholt/archiver/v3/tarsz.go b/vendor/github.com/mholt/archiver/v3/tarsz.go
new file mode 100644
index 0000000000..0569e66473
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/tarsz.go
@@ -0,0 +1,114 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/golang/snappy"
+)
+
+// TarSz facilitates Snappy compression
+// (https://github.com/google/snappy)
+// of tarball archives.
+type TarSz struct {
+ *Tar
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*TarSz) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".tar.sz") &&
+ !strings.HasSuffix(filename, ".tsz") {
+ return fmt.Errorf("filename must have a .tar.sz or .tsz extension")
+ }
+ return nil
+}
+
+// Archive creates a compressed tar file at destination
+// containing the files listed in sources. The destination
+// must end with ".tar.sz" or ".tsz". File paths can be
+// those of regular files or directories; directories will
+// be recursively added.
+func (tsz *TarSz) Archive(sources []string, destination string) error {
+ err := tsz.CheckExt(destination)
+ if err != nil {
+ return fmt.Errorf("output %s", err.Error())
+ }
+ tsz.wrapWriter()
+ return tsz.Tar.Archive(sources, destination)
+}
+
+// Unarchive unpacks the compressed tarball at
+// source to destination. Destination will be
+// treated as a folder name.
+func (tsz *TarSz) Unarchive(source, destination string) error {
+ tsz.wrapReader()
+ return tsz.Tar.Unarchive(source, destination)
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (tsz *TarSz) Walk(archive string, walkFn WalkFunc) error {
+ tsz.wrapReader()
+ return tsz.Tar.Walk(archive, walkFn)
+}
+
+// Create opens tsz for writing a compressed
+// tar archive to out.
+func (tsz *TarSz) Create(out io.Writer) error {
+ tsz.wrapWriter()
+ return tsz.Tar.Create(out)
+}
+
+// Open opens t for reading a compressed archive from
+// in. The size parameter is not used.
+func (tsz *TarSz) Open(in io.Reader, size int64) error {
+ tsz.wrapReader()
+ return tsz.Tar.Open(in, size)
+}
+
+// Extract extracts a single file from the tar archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (tsz *TarSz) Extract(source, target, destination string) error {
+ tsz.wrapReader()
+ return tsz.Tar.Extract(source, target, destination)
+}
+
+func (tsz *TarSz) wrapWriter() {
+ var sw *snappy.Writer
+ tsz.Tar.writerWrapFn = func(w io.Writer) (io.Writer, error) {
+ sw = snappy.NewWriter(w)
+ return sw, nil
+ }
+ tsz.Tar.cleanupWrapFn = func() {
+ sw.Close()
+ }
+}
+
+func (tsz *TarSz) wrapReader() {
+ tsz.Tar.readerWrapFn = func(r io.Reader) (io.Reader, error) {
+ return snappy.NewReader(r), nil
+ }
+}
+
+func (tsz *TarSz) String() string { return "tar.sz" }
+
+// NewTarSz returns a new, default instance ready to be customized and used.
+func NewTarSz() *TarSz {
+ return &TarSz{
+ Tar: NewTar(),
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(TarSz))
+ _ = Writer(new(TarSz))
+ _ = Archiver(new(TarSz))
+ _ = Unarchiver(new(TarSz))
+ _ = Walker(new(TarSz))
+ _ = Extractor(new(TarSz))
+)
+
+// DefaultTarSz is a convenient archiver ready to use.
+var DefaultTarSz = NewTarSz()
diff --git a/vendor/github.com/mholt/archiver/v3/tarxz.go b/vendor/github.com/mholt/archiver/v3/tarxz.go
new file mode 100644
index 0000000000..5679a067be
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/tarxz.go
@@ -0,0 +1,119 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/ulikunitz/xz"
+ fastxz "github.com/xi2/xz"
+)
+
+// TarXz facilitates xz compression
+// (https://tukaani.org/xz/format.html)
+// of tarball archives.
+type TarXz struct {
+ *Tar
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*TarXz) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".tar.xz") &&
+ !strings.HasSuffix(filename, ".txz") {
+ return fmt.Errorf("filename must have a .tar.xz or .txz extension")
+ }
+ return nil
+}
+
+// Archive creates a compressed tar file at destination
+// containing the files listed in sources. The destination
+// must end with ".tar.xz" or ".txz". File paths can be
+// those of regular files or directories; directories will
+// be recursively added.
+func (txz *TarXz) Archive(sources []string, destination string) error {
+ err := txz.CheckExt(destination)
+ if err != nil {
+ return fmt.Errorf("output %s", err.Error())
+ }
+ txz.wrapWriter()
+ return txz.Tar.Archive(sources, destination)
+}
+
+// Unarchive unpacks the compressed tarball at
+// source to destination. Destination will be
+// treated as a folder name.
+func (txz *TarXz) Unarchive(source, destination string) error {
+ txz.wrapReader()
+ return txz.Tar.Unarchive(source, destination)
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (txz *TarXz) Walk(archive string, walkFn WalkFunc) error {
+ txz.wrapReader()
+ return txz.Tar.Walk(archive, walkFn)
+}
+
+// Create opens txz for writing a compressed
+// tar archive to out.
+func (txz *TarXz) Create(out io.Writer) error {
+ txz.wrapWriter()
+ return txz.Tar.Create(out)
+}
+
+// Open opens t for reading a compressed archive from
+// in. The size parameter is not used.
+func (txz *TarXz) Open(in io.Reader, size int64) error {
+ txz.wrapReader()
+ return txz.Tar.Open(in, size)
+}
+
+// Extract extracts a single file from the tar archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (txz *TarXz) Extract(source, target, destination string) error {
+ txz.wrapReader()
+ return txz.Tar.Extract(source, target, destination)
+}
+
+func (txz *TarXz) wrapWriter() {
+ var xzw *xz.Writer
+ txz.Tar.writerWrapFn = func(w io.Writer) (io.Writer, error) {
+ var err error
+ xzw, err = xz.NewWriter(w)
+ return xzw, err
+ }
+ txz.Tar.cleanupWrapFn = func() {
+ xzw.Close()
+ }
+}
+
+func (txz *TarXz) wrapReader() {
+ var xzr *fastxz.Reader
+ txz.Tar.readerWrapFn = func(r io.Reader) (io.Reader, error) {
+ var err error
+ xzr, err = fastxz.NewReader(r, 0)
+ return xzr, err
+ }
+}
+
+func (txz *TarXz) String() string { return "tar.xz" }
+
+// NewTarXz returns a new, default instance ready to be customized and used.
+func NewTarXz() *TarXz {
+ return &TarXz{
+ Tar: NewTar(),
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(TarXz))
+ _ = Writer(new(TarXz))
+ _ = Archiver(new(TarXz))
+ _ = Unarchiver(new(TarXz))
+ _ = Walker(new(TarXz))
+ _ = Extractor(new(TarXz))
+)
+
+// DefaultTarXz is a convenient archiver ready to use.
+var DefaultTarXz = NewTarXz()
diff --git a/vendor/github.com/mholt/archiver/v3/tarzst.go b/vendor/github.com/mholt/archiver/v3/tarzst.go
new file mode 100644
index 0000000000..3b2fe43189
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/tarzst.go
@@ -0,0 +1,120 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/klauspost/compress/zstd"
+)
+
+// TarZstd facilitates Zstandard compression
+// (RFC 8478) of tarball archives.
+type TarZstd struct {
+ *Tar
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*TarZstd) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".tar.zst") {
+ return fmt.Errorf("filename must have a .tar.zst extension")
+ }
+ return nil
+}
+
+// Archive creates a compressed tar file at destination
+// containing the files listed in sources. The destination
+// must end with ".tar.zst" or ".tzst". File paths can be
+// those of regular files or directories; directories will
+// be recursively added.
+func (tzst *TarZstd) Archive(sources []string, destination string) error {
+ err := tzst.CheckExt(destination)
+ if err != nil {
+ return fmt.Errorf("output %s", err.Error())
+ }
+ tzst.wrapWriter()
+ return tzst.Tar.Archive(sources, destination)
+}
+
+// Unarchive unpacks the compressed tarball at
+// source to destination. Destination will be
+// treated as a folder name.
+func (tzst *TarZstd) Unarchive(source, destination string) error {
+ tzst.wrapReader()
+ return tzst.Tar.Unarchive(source, destination)
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (tzst *TarZstd) Walk(archive string, walkFn WalkFunc) error {
+ tzst.wrapReader()
+ return tzst.Tar.Walk(archive, walkFn)
+}
+
+// Create opens txz for writing a compressed
+// tar archive to out.
+func (tzst *TarZstd) Create(out io.Writer) error {
+ tzst.wrapWriter()
+ return tzst.Tar.Create(out)
+}
+
+// Open opens t for reading a compressed archive from
+// in. The size parameter is not used.
+func (tzst *TarZstd) Open(in io.Reader, size int64) error {
+ tzst.wrapReader()
+ return tzst.Tar.Open(in, size)
+}
+
+// Extract extracts a single file from the tar archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (tzst *TarZstd) Extract(source, target, destination string) error {
+ tzst.wrapReader()
+ return tzst.Tar.Extract(source, target, destination)
+}
+
+func (tzst *TarZstd) wrapWriter() {
+ var zstdw *zstd.Encoder
+ tzst.Tar.writerWrapFn = func(w io.Writer) (io.Writer, error) {
+ var err error
+ zstdw, err = zstd.NewWriter(w)
+ return zstdw, err
+ }
+ tzst.Tar.cleanupWrapFn = func() {
+ zstdw.Close()
+ }
+}
+
+func (tzst *TarZstd) wrapReader() {
+ var zstdr *zstd.Decoder
+ tzst.Tar.readerWrapFn = func(r io.Reader) (io.Reader, error) {
+ var err error
+ zstdr, err = zstd.NewReader(r)
+ return zstdr, err
+ }
+ tzst.Tar.cleanupWrapFn = func() {
+ zstdr.Close()
+ }
+}
+
+func (tzst *TarZstd) String() string { return "tar.zst" }
+
+// NewTarZstd returns a new, default instance ready to be customized and used.
+func NewTarZstd() *TarZstd {
+ return &TarZstd{
+ Tar: NewTar(),
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(TarZstd))
+ _ = Writer(new(TarZstd))
+ _ = Archiver(new(TarZstd))
+ _ = Unarchiver(new(TarZstd))
+ _ = Walker(new(TarZstd))
+ _ = ExtensionChecker(new(TarZstd))
+ _ = Extractor(new(TarZstd))
+)
+
+// DefaultTarZstd is a convenient archiver ready to use.
+var DefaultTarZstd = NewTarZstd()
diff --git a/vendor/github.com/mholt/archiver/v3/xz.go b/vendor/github.com/mholt/archiver/v3/xz.go
new file mode 100644
index 0000000000..c60d5eaec6
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/xz.go
@@ -0,0 +1,58 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+
+ "github.com/ulikunitz/xz"
+ fastxz "github.com/xi2/xz"
+)
+
+// Xz facilitates XZ compression.
+type Xz struct{}
+
+// Compress reads in, compresses it, and writes it to out.
+func (x *Xz) Compress(in io.Reader, out io.Writer) error {
+ w, err := xz.NewWriter(out)
+ if err != nil {
+ return err
+ }
+ defer w.Close()
+ _, err = io.Copy(w, in)
+ return err
+}
+
+// Decompress reads in, decompresses it, and writes it to out.
+func (x *Xz) Decompress(in io.Reader, out io.Writer) error {
+ r, err := fastxz.NewReader(in, 0)
+ if err != nil {
+ return err
+ }
+ _, err = io.Copy(out, r)
+ return err
+}
+
+// CheckExt ensures the file extension matches the format.
+func (x *Xz) CheckExt(filename string) error {
+ if filepath.Ext(filename) != ".xz" {
+ return fmt.Errorf("filename must have a .xz extension")
+ }
+ return nil
+}
+
+func (x *Xz) String() string { return "xz" }
+
+// NewXz returns a new, default instance ready to be customized and used.
+func NewXz() *Xz {
+ return new(Xz)
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Compressor(new(Xz))
+ _ = Decompressor(new(Xz))
+)
+
+// DefaultXz is a default instance that is conveniently ready to use.
+var DefaultXz = NewXz()
diff --git a/vendor/github.com/mholt/archiver/v3/zip.go b/vendor/github.com/mholt/archiver/v3/zip.go
new file mode 100644
index 0000000000..192bf60766
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/zip.go
@@ -0,0 +1,605 @@
+package archiver
+
+import (
+ "archive/zip"
+ "bytes"
+ "compress/flate"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+)
+
+// Zip provides facilities for operating ZIP archives.
+// See https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT.
+type Zip struct {
+ // The compression level to use, as described
+ // in the compress/flate package.
+ CompressionLevel int
+
+ // Whether to overwrite existing files; if false,
+ // an error is returned if the file exists.
+ OverwriteExisting bool
+
+ // Whether to make all the directories necessary
+ // to create a zip archive in the desired path.
+ MkdirAll bool
+
+ // If enabled, selective compression will only
+ // compress files which are not already in a
+ // compressed format; this is decided based
+ // simply on file extension.
+ SelectiveCompression bool
+
+ // A single top-level folder can be implicitly
+ // created by the Archive or Unarchive methods
+ // if the files to be added to the archive
+ // or the files to be extracted from the archive
+ // do not all have a common root. This roughly
+ // mimics the behavior of archival tools integrated
+ // into OS file browsers which create a subfolder
+ // to avoid unexpectedly littering the destination
+ // folder with potentially many files, causing a
+ // problematic cleanup/organization situation.
+ // This feature is available for both creation
+ // and extraction of archives, but may be slightly
+ // inefficient with lots and lots of files,
+ // especially on extraction.
+ ImplicitTopLevelFolder bool
+
+ // If true, errors encountered during reading
+ // or writing a single file will be logged and
+ // the operation will continue on remaining files.
+ ContinueOnError bool
+
+ zw *zip.Writer
+ zr *zip.Reader
+ ridx int
+}
+
+// CheckExt ensures the file extension matches the format.
+func (*Zip) CheckExt(filename string) error {
+ if !strings.HasSuffix(filename, ".zip") {
+ return fmt.Errorf("filename must have a .zip extension")
+ }
+ return nil
+}
+
+// Archive creates a .zip file at destination containing
+// the files listed in sources. The destination must end
+// with ".zip". File paths can be those of regular files
+// or directories. Regular files are stored at the 'root'
+// of the archive, and directories are recursively added.
+func (z *Zip) Archive(sources []string, destination string) error {
+ err := z.CheckExt(destination)
+ if err != nil {
+ return fmt.Errorf("checking extension: %v", err)
+ }
+ if !z.OverwriteExisting && fileExists(destination) {
+ return fmt.Errorf("file already exists: %s", destination)
+ }
+
+ // make the folder to contain the resulting archive
+ // if it does not already exist
+ destDir := filepath.Dir(destination)
+ if z.MkdirAll && !fileExists(destDir) {
+ err := mkdir(destDir, 0755)
+ if err != nil {
+ return fmt.Errorf("making folder for destination: %v", err)
+ }
+ }
+
+ out, err := os.Create(destination)
+ if err != nil {
+ return fmt.Errorf("creating %s: %v", destination, err)
+ }
+ defer out.Close()
+
+ err = z.Create(out)
+ if err != nil {
+ return fmt.Errorf("creating zip: %v", err)
+ }
+ defer z.Close()
+
+ var topLevelFolder string
+ if z.ImplicitTopLevelFolder && multipleTopLevels(sources) {
+ topLevelFolder = folderNameFromFileName(destination)
+ }
+
+ for _, source := range sources {
+ err := z.writeWalk(source, topLevelFolder, destination)
+ if err != nil {
+ return fmt.Errorf("walking %s: %v", source, err)
+ }
+ }
+
+ return nil
+}
+
+// Unarchive unpacks the .zip file at source to destination.
+// Destination will be treated as a folder name.
+func (z *Zip) Unarchive(source, destination string) error {
+ if !fileExists(destination) && z.MkdirAll {
+ err := mkdir(destination, 0755)
+ if err != nil {
+ return fmt.Errorf("preparing destination: %v", err)
+ }
+ }
+
+ file, err := os.Open(source)
+ if err != nil {
+ return fmt.Errorf("opening source file: %v", err)
+ }
+ defer file.Close()
+
+ fileInfo, err := file.Stat()
+ if err != nil {
+ return fmt.Errorf("statting source file: %v", err)
+ }
+
+ err = z.Open(file, fileInfo.Size())
+ if err != nil {
+ return fmt.Errorf("opening zip archive for reading: %v", err)
+ }
+ defer z.Close()
+
+ // if the files in the archive do not all share a common
+ // root, then make sure we extract to a single subfolder
+ // rather than potentially littering the destination...
+ if z.ImplicitTopLevelFolder {
+ files := make([]string, len(z.zr.File))
+ for i := range z.zr.File {
+ files[i] = z.zr.File[i].Name
+ }
+ if multipleTopLevels(files) {
+ destination = filepath.Join(destination, folderNameFromFileName(source))
+ }
+ }
+
+ for {
+ err := z.extractNext(destination)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ if z.ContinueOnError {
+ log.Printf("[ERROR] Reading file in zip archive: %v", err)
+ continue
+ }
+ return fmt.Errorf("reading file in zip archive: %v", err)
+ }
+ }
+
+ return nil
+}
+
+func (z *Zip) extractNext(to string) error {
+ f, err := z.Read()
+ if err != nil {
+ return err // don't wrap error; calling loop must break on io.EOF
+ }
+ defer f.Close()
+ return z.extractFile(f, to)
+}
+
+func (z *Zip) extractFile(f File, to string) error {
+ header, ok := f.Header.(zip.FileHeader)
+ if !ok {
+ return fmt.Errorf("expected header to be zip.FileHeader but was %T", f.Header)
+ }
+
+ to = filepath.Join(to, header.Name)
+
+ // if a directory, no content; simply make the directory and return
+ if f.IsDir() {
+ return mkdir(to, f.Mode())
+ }
+
+ // do not overwrite existing files, if configured
+ if !z.OverwriteExisting && fileExists(to) {
+ return fmt.Errorf("file already exists: %s", to)
+ }
+
+ // extract symbolic links as symbolic links
+ if isSymlink(header.FileInfo()) {
+ // symlink target is the contents of the file
+ buf := new(bytes.Buffer)
+ _, err := io.Copy(buf, f)
+ if err != nil {
+ return fmt.Errorf("%s: reading symlink target: %v", header.Name, err)
+ }
+ return writeNewSymbolicLink(to, strings.TrimSpace(buf.String()))
+ }
+
+ return writeNewFile(to, f, f.Mode())
+}
+
+func (z *Zip) writeWalk(source, topLevelFolder, destination string) error {
+ sourceInfo, err := os.Stat(source)
+ if err != nil {
+ return fmt.Errorf("%s: stat: %v", source, err)
+ }
+ destAbs, err := filepath.Abs(destination)
+ if err != nil {
+ return fmt.Errorf("%s: getting absolute path of destination %s: %v", source, destination, err)
+ }
+
+ return filepath.Walk(source, func(fpath string, info os.FileInfo, err error) error {
+ handleErr := func(err error) error {
+ if z.ContinueOnError {
+ log.Printf("[ERROR] Walking %s: %v", fpath, err)
+ return nil
+ }
+ return err
+ }
+ if err != nil {
+ return handleErr(fmt.Errorf("traversing %s: %v", fpath, err))
+ }
+ if info == nil {
+ return handleErr(fmt.Errorf("%s: no file info", fpath))
+ }
+
+ // make sure we do not copy the output file into the output
+ // file; that results in an infinite loop and disk exhaustion!
+ fpathAbs, err := filepath.Abs(fpath)
+ if err != nil {
+ return handleErr(fmt.Errorf("%s: getting absolute path: %v", fpath, err))
+ }
+ if within(fpathAbs, destAbs) {
+ return nil
+ }
+
+ // build the name to be used within the archive
+ nameInArchive, err := makeNameInArchive(sourceInfo, source, topLevelFolder, fpath)
+ if err != nil {
+ return handleErr(err)
+ }
+
+ var file io.ReadCloser
+ if info.Mode().IsRegular() {
+ file, err = os.Open(fpath)
+ if err != nil {
+ return handleErr(fmt.Errorf("%s: opening: %v", fpath, err))
+ }
+ defer file.Close()
+ }
+ err = z.Write(File{
+ FileInfo: FileInfo{
+ FileInfo: info,
+ CustomName: nameInArchive,
+ },
+ ReadCloser: file,
+ })
+ if err != nil {
+ return handleErr(fmt.Errorf("%s: writing: %s", fpath, err))
+ }
+
+ return nil
+ })
+}
+
+// Create opens z for writing a ZIP archive to out.
+func (z *Zip) Create(out io.Writer) error {
+ if z.zw != nil {
+ return fmt.Errorf("zip archive is already created for writing")
+ }
+ z.zw = zip.NewWriter(out)
+ if z.CompressionLevel != flate.DefaultCompression {
+ z.zw.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
+ return flate.NewWriter(out, z.CompressionLevel)
+ })
+ }
+ return nil
+}
+
+// Write writes f to z, which must have been opened for writing first.
+func (z *Zip) Write(f File) error {
+ if z.zw == nil {
+ return fmt.Errorf("zip archive was not created for writing first")
+ }
+ if f.FileInfo == nil {
+ return fmt.Errorf("no file info")
+ }
+ if f.FileInfo.Name() == "" {
+ return fmt.Errorf("missing file name")
+ }
+
+ header, err := zip.FileInfoHeader(f)
+ if err != nil {
+ return fmt.Errorf("%s: getting header: %v", f.Name(), err)
+ }
+
+ if f.IsDir() {
+ header.Name += "/" // required - strangely no mention of this in zip spec? but is in godoc...
+ header.Method = zip.Store
+ } else {
+ ext := strings.ToLower(path.Ext(header.Name))
+ if _, ok := compressedFormats[ext]; ok && z.SelectiveCompression {
+ header.Method = zip.Store
+ } else {
+ header.Method = zip.Deflate
+ }
+ }
+
+ writer, err := z.zw.CreateHeader(header)
+ if err != nil {
+ return fmt.Errorf("%s: making header: %v", f.Name(), err)
+ }
+
+ return z.writeFile(f, writer)
+}
+
+func (z *Zip) writeFile(f File, writer io.Writer) error {
+ if f.IsDir() {
+ return nil // directories have no contents
+ }
+ if isSymlink(f) {
+ // file body for symlinks is the symlink target
+ linkTarget, err := os.Readlink(f.Name())
+ if err != nil {
+ return fmt.Errorf("%s: readlink: %v", f.Name(), err)
+ }
+ _, err = writer.Write([]byte(filepath.ToSlash(linkTarget)))
+ if err != nil {
+ return fmt.Errorf("%s: writing symlink target: %v", f.Name(), err)
+ }
+ return nil
+ }
+
+ if f.ReadCloser == nil {
+ return fmt.Errorf("%s: no way to read file contents", f.Name())
+ }
+ _, err := io.Copy(writer, f)
+ if err != nil {
+ return fmt.Errorf("%s: copying contents: %v", f.Name(), err)
+ }
+
+ return nil
+}
+
+// Open opens z for reading an archive from in,
+// which is expected to have the given size and
+// which must be an io.ReaderAt.
+func (z *Zip) Open(in io.Reader, size int64) error {
+ inRdrAt, ok := in.(io.ReaderAt)
+ if !ok {
+ return fmt.Errorf("reader must be io.ReaderAt")
+ }
+ if z.zr != nil {
+ return fmt.Errorf("zip archive is already open for reading")
+ }
+ var err error
+ z.zr, err = zip.NewReader(inRdrAt, size)
+ if err != nil {
+ return fmt.Errorf("creating reader: %v", err)
+ }
+ z.ridx = 0
+ return nil
+}
+
+// Read reads the next file from z, which must have
+// already been opened for reading. If there are no
+// more files, the error is io.EOF. The File must
+// be closed when finished reading from it.
+func (z *Zip) Read() (File, error) {
+ if z.zr == nil {
+ return File{}, fmt.Errorf("zip archive is not open")
+ }
+ if z.ridx >= len(z.zr.File) {
+ return File{}, io.EOF
+ }
+
+ // access the file and increment counter so that
+ // if there is an error processing this file, the
+ // caller can still iterate to the next file
+ zf := z.zr.File[z.ridx]
+ z.ridx++
+
+ file := File{
+ FileInfo: zf.FileInfo(),
+ Header: zf.FileHeader,
+ }
+
+ rc, err := zf.Open()
+ if err != nil {
+ return file, fmt.Errorf("%s: open compressed file: %v", zf.Name, err)
+ }
+ file.ReadCloser = rc
+
+ return file, nil
+}
+
+// Close closes the zip archive(s) opened by Create and Open.
+func (z *Zip) Close() error {
+ if z.zr != nil {
+ z.zr = nil
+ }
+ if z.zw != nil {
+ zw := z.zw
+ z.zw = nil
+ return zw.Close()
+ }
+ return nil
+}
+
+// Walk calls walkFn for each visited item in archive.
+func (z *Zip) Walk(archive string, walkFn WalkFunc) error {
+ zr, err := zip.OpenReader(archive)
+ if err != nil {
+ return fmt.Errorf("opening zip reader: %v", err)
+ }
+ defer zr.Close()
+
+ for _, zf := range zr.File {
+ zfrc, err := zf.Open()
+ if err != nil {
+ zfrc.Close()
+ if z.ContinueOnError {
+ log.Printf("[ERROR] Opening %s: %v", zf.Name, err)
+ continue
+ }
+ return fmt.Errorf("opening %s: %v", zf.Name, err)
+ }
+
+ err = walkFn(File{
+ FileInfo: zf.FileInfo(),
+ Header: zf.FileHeader,
+ ReadCloser: zfrc,
+ })
+ zfrc.Close()
+ if err != nil {
+ if err == ErrStopWalk {
+ break
+ }
+ if z.ContinueOnError {
+ log.Printf("[ERROR] Walking %s: %v", zf.Name, err)
+ continue
+ }
+ return fmt.Errorf("walking %s: %v", zf.Name, err)
+ }
+ }
+
+ return nil
+}
+
+// Extract extracts a single file from the zip archive.
+// If the target is a directory, the entire folder will
+// be extracted into destination.
+func (z *Zip) Extract(source, target, destination string) error {
+ // target refers to a path inside the archive, which should be clean also
+ target = path.Clean(target)
+
+ // if the target ends up being a directory, then
+ // we will continue walking and extracting files
+ // until we are no longer within that directory
+ var targetDirPath string
+
+ return z.Walk(source, func(f File) error {
+ zfh, ok := f.Header.(zip.FileHeader)
+ if !ok {
+ return fmt.Errorf("expected header to be zip.FileHeader but was %T", f.Header)
+ }
+
+ // importantly, cleaning the path strips tailing slash,
+ // which must be appended to folders within the archive
+ name := path.Clean(zfh.Name)
+ if f.IsDir() && target == name {
+ targetDirPath = path.Dir(name)
+ }
+
+ if within(target, zfh.Name) {
+ // either this is the exact file we want, or is
+ // in the directory we want to extract
+
+ // build the filename we will extract to
+ end, err := filepath.Rel(targetDirPath, zfh.Name)
+ if err != nil {
+ return fmt.Errorf("relativizing paths: %v", err)
+ }
+ joined := filepath.Join(destination, end)
+
+ err = z.extractFile(f, joined)
+ if err != nil {
+ return fmt.Errorf("extracting file %s: %v", zfh.Name, err)
+ }
+
+ // if our target was not a directory, stop walk
+ if targetDirPath == "" {
+ return ErrStopWalk
+ }
+ } else if targetDirPath != "" {
+ // finished walking the entire directory
+ return ErrStopWalk
+ }
+
+ return nil
+ })
+}
+
+// Match returns true if the format of file matches this
+// type's format. It should not affect reader position.
+func (*Zip) Match(file io.ReadSeeker) (bool, error) {
+ currentPos, err := file.Seek(0, io.SeekCurrent)
+ if err != nil {
+ return false, err
+ }
+ _, err = file.Seek(0, 0)
+ if err != nil {
+ return false, err
+ }
+ defer file.Seek(currentPos, io.SeekStart)
+
+ buf := make([]byte, 4)
+ if n, err := file.Read(buf); err != nil || n < 4 {
+ return false, nil
+ }
+ return bytes.Equal(buf, []byte("PK\x03\x04")), nil
+}
+
+func (z *Zip) String() string { return "zip" }
+
+// NewZip returns a new, default instance ready to be customized and used.
+func NewZip() *Zip {
+ return &Zip{
+ CompressionLevel: flate.DefaultCompression,
+ MkdirAll: true,
+ SelectiveCompression: true,
+ }
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Reader(new(Zip))
+ _ = Writer(new(Zip))
+ _ = Archiver(new(Zip))
+ _ = Unarchiver(new(Zip))
+ _ = Walker(new(Zip))
+ _ = Extractor(new(Zip))
+ _ = Matcher(new(Zip))
+ _ = ExtensionChecker(new(Zip))
+)
+
+// compressedFormats is a (non-exhaustive) set of lowercased
+// file extensions for formats that are typically already
+// compressed. Compressing files that are already compressed
+// is inefficient, so use this set of extension to avoid that.
+var compressedFormats = map[string]struct{}{
+ ".7z": {},
+ ".avi": {},
+ ".br": {},
+ ".bz2": {},
+ ".cab": {},
+ ".docx": {},
+ ".gif": {},
+ ".gz": {},
+ ".jar": {},
+ ".jpeg": {},
+ ".jpg": {},
+ ".lz": {},
+ ".lz4": {},
+ ".lzma": {},
+ ".m4v": {},
+ ".mov": {},
+ ".mp3": {},
+ ".mp4": {},
+ ".mpeg": {},
+ ".mpg": {},
+ ".png": {},
+ ".pptx": {},
+ ".rar": {},
+ ".sz": {},
+ ".tbz2": {},
+ ".tgz": {},
+ ".tsz": {},
+ ".txz": {},
+ ".xlsx": {},
+ ".xz": {},
+ ".zip": {},
+ ".zipx": {},
+}
+
+// DefaultZip is a default instance that is conveniently ready to use.
+var DefaultZip = NewZip()
diff --git a/vendor/github.com/mholt/archiver/v3/zstd.go b/vendor/github.com/mholt/archiver/v3/zstd.go
new file mode 100644
index 0000000000..3955628c7f
--- /dev/null
+++ b/vendor/github.com/mholt/archiver/v3/zstd.go
@@ -0,0 +1,59 @@
+package archiver
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+
+ "github.com/klauspost/compress/zstd"
+)
+
+// Zstd facilitates Zstandard compression.
+type Zstd struct {
+}
+
+// Compress reads in, compresses it, and writes it to out.
+func (zs *Zstd) Compress(in io.Reader, out io.Writer) error {
+ w, err := zstd.NewWriter(out)
+ if err != nil {
+ return err
+ }
+ defer w.Close()
+ _, err = io.Copy(w, in)
+ return err
+}
+
+// Decompress reads in, decompresses it, and writes it to out.
+func (zs *Zstd) Decompress(in io.Reader, out io.Writer) error {
+ r, err := zstd.NewReader(in)
+ if err != nil {
+ return err
+ }
+ defer r.Close()
+ _, err = io.Copy(out, r)
+ return err
+}
+
+// CheckExt ensures the file extension matches the format.
+func (zs *Zstd) CheckExt(filename string) error {
+ if filepath.Ext(filename) != ".zst" {
+ return fmt.Errorf("filename must have a .zst extension")
+ }
+ return nil
+}
+
+func (zs *Zstd) String() string { return "zstd" }
+
+// NewZstd returns a new, default instance ready to be customized and used.
+func NewZstd() *Zstd {
+ return new(Zstd)
+}
+
+// Compile-time checks to ensure type implements desired interfaces.
+var (
+ _ = Compressor(new(Zstd))
+ _ = Decompressor(new(Zstd))
+)
+
+// DefaultZstd is a default instance that is conveniently ready to use.
+var DefaultZstd = NewZstd()