mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
improve untar when using files instead of directories. Specifies behavior on non-existant targets.
Docker-DCO-1.1-Signed-off-by: Tibor Vass <teabee89@gmail.com> (github: tiborvass)
This commit is contained in:
parent
1059e166fd
commit
1c8d3106df
2 changed files with 71 additions and 4 deletions
|
@ -365,10 +365,12 @@ func TarFilter(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
|
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
|
||||||
// and unpacks it into the directory at `path`.
|
// and unpacks it into the directory at `dest`.
|
||||||
// The archive may be compressed with one of the following algorithms:
|
// The archive may be compressed with one of the following algorithms:
|
||||||
// identity (uncompressed), gzip, bzip2, xz.
|
// identity (uncompressed), gzip, bzip2, xz.
|
||||||
// FIXME: specify behavior when target path exists vs. doesn't exist.
|
// If `dest` does not exist, it is created unless there are multiple entries in `archive`.
|
||||||
|
// In the latter case, an error is returned.
|
||||||
|
// An other error is returned if `dest` exists but is not a directory, to prevent overwriting.
|
||||||
func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
||||||
if archive == nil {
|
if archive == nil {
|
||||||
return fmt.Errorf("Empty archive")
|
return fmt.Errorf("Empty archive")
|
||||||
|
@ -382,7 +384,21 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
||||||
|
|
||||||
tr := tar.NewReader(decompressedArchive)
|
tr := tar.NewReader(decompressedArchive)
|
||||||
|
|
||||||
var dirs []*tar.Header
|
var (
|
||||||
|
dirs []*tar.Header
|
||||||
|
destNotExist bool
|
||||||
|
multipleEntries bool
|
||||||
|
)
|
||||||
|
|
||||||
|
if fi, err := os.Lstat(dest); err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// destination does not exist, so it is assumed it has to be created.
|
||||||
|
destNotExist = true
|
||||||
|
} else if !fi.IsDir() {
|
||||||
|
return fmt.Errorf("Trying to untar to `%s`: exists but not a directory", dest)
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate through the files in the archive.
|
// Iterate through the files in the archive.
|
||||||
for {
|
for {
|
||||||
|
@ -395,6 +411,11 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return an error if destination needs to be created and there is more than 1 entry in the tar stream.
|
||||||
|
if destNotExist && multipleEntries {
|
||||||
|
return fmt.Errorf("Trying to untar an archive with multiple entries to an inexistant target `%s`: did you mean `%s` instead?", dest, filepath.Dir(dest))
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize name, for safety and for a simple is-root check
|
// Normalize name, for safety and for a simple is-root check
|
||||||
hdr.Name = filepath.Clean(hdr.Name)
|
hdr.Name = filepath.Clean(hdr.Name)
|
||||||
|
|
||||||
|
@ -410,7 +431,12 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(dest, hdr.Name)
|
var path string
|
||||||
|
if destNotExist {
|
||||||
|
path = dest // we are renaming hdr.Name to dest
|
||||||
|
} else {
|
||||||
|
path = filepath.Join(dest, hdr.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// If path exits we almost always just want to remove and replace it
|
// If path exits we almost always just want to remove and replace it
|
||||||
// The only exception is when it is a directory *and* the file from
|
// The only exception is when it is a directory *and* the file from
|
||||||
|
@ -430,6 +456,9 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Successfully added an entry. Predicting multiple entries for next iteration (not current one).
|
||||||
|
multipleEntries = true
|
||||||
|
|
||||||
// Directory mtimes must be handled at the end to avoid further
|
// Directory mtimes must be handled at the end to avoid further
|
||||||
// file creation in them to modify the directory mtime
|
// file creation in them to modify the directory mtime
|
||||||
if hdr.Typeflag == tar.TypeDir {
|
if hdr.Typeflag == tar.TypeDir {
|
||||||
|
|
|
@ -128,6 +128,44 @@ func TestTarUntar(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTarUntarFile(t *testing.T) {
|
||||||
|
origin, err := ioutil.TempDir("", "docker-test-untar-origin-file")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(origin)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(path.Join(origin, "before"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(path.Join(origin, "after"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(path.Join(origin, "before", "file"), []byte("hello world"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tar, err := TarFilter(path.Join(origin, "before"), &TarOptions{Compression: Uncompressed, Includes: []string{"file"}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Untar(tar, path.Join(origin, "after", "file2"), nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
catCmd := exec.Command("cat", path.Join(origin, "after", "file2"))
|
||||||
|
out, err := CmdStream(catCmd, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to start command: %s", err)
|
||||||
|
}
|
||||||
|
if output, err := ioutil.ReadAll(out); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if string(output) != "hello world" {
|
||||||
|
t.Fatalf("Expected 'hello world', got '%s'", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz
|
// Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz
|
||||||
// use PAX Global Extended Headers.
|
// use PAX Global Extended Headers.
|
||||||
// Failing prevents the archives from being uncompressed during ADD
|
// Failing prevents the archives from being uncompressed during ADD
|
||||||
|
|
Loading…
Add table
Reference in a new issue