128 lines
3.8 KiB
Go
128 lines
3.8 KiB
Go
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// +build windows
|
|
|
|
package archive
|
|
|
|
import (
|
|
"archive/tar"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/vmware/vic/pkg/trace"
|
|
)
|
|
|
|
const (
|
|
// fileWriteFlags is a collection of flags configuring our writes for general tar behavior
|
|
//
|
|
// O_CREATE = Create file if it does not exist
|
|
// O_TRUNC = truncate file to 0 length if it does exist(overwrite the file)
|
|
// O_WRONLY = We use this since we do not intend to read, we only need to write.
|
|
fileWriteFlags = os.O_CREATE | os.O_TRUNC | os.O_WRONLY
|
|
)
|
|
|
|
// combined with any rebase from the path spec
|
|
//
|
|
// the pathSpec will include the following elements
|
|
// - include : any tar entry that has a path below(after stripping) the include path will be written
|
|
// - strip : The strip string will indicate the
|
|
// - exlude : marks paths that are to be excluded from the write operation
|
|
// - rebase : marks the the write path that will be tacked onto the "root". e.g /tmp/unpack + /my/target/path = /tmp/unpack/my/target/path
|
|
func Unpack(op trace.Operation, tarStream io.Reader, filter *FilterSpec, root string, _ string) error {
|
|
op.Debugf("unpacking archive to root: %s, filter: %+v", root, filter)
|
|
|
|
// Online datasource is sending a tar reader instead of an io reader.
|
|
// Type check here to see if we actually need to create a tar reader.
|
|
var tr *tar.Reader
|
|
if trCheck, ok := tarStream.(*tar.Reader); ok {
|
|
tr = trCheck
|
|
} else {
|
|
tr = tar.NewReader(tarStream)
|
|
}
|
|
|
|
fi, err := os.Stat(root)
|
|
if err != nil {
|
|
// the target unpack path does not exist. We should not get here.
|
|
op.Errorf("tar unpack target does not exist: %s", root)
|
|
return err
|
|
}
|
|
|
|
if !fi.IsDir() {
|
|
err := fmt.Errorf("unpack root target is not a directory: %s", root)
|
|
op.Error(err)
|
|
return err
|
|
}
|
|
|
|
op.Debugf("using FilterSpec : (%#v)", *filter)
|
|
// process the tarball onto the filesystem
|
|
for {
|
|
header, err := tr.Next()
|
|
if err == io.EOF {
|
|
// This indicates the end of the archive
|
|
break
|
|
}
|
|
|
|
if err != nil {
|
|
op.Errorf("Error reading tar header: %s", err)
|
|
return err
|
|
}
|
|
|
|
op.Debugf("processing tar header: asset(%s), size(%d)", header.Name, header.Size)
|
|
// skip excluded elements unless explicitly included
|
|
if filter.Excludes(op, header.Name) {
|
|
continue
|
|
}
|
|
|
|
// fix up path
|
|
stripped := strings.TrimPrefix(header.Name, filter.StripPath)
|
|
rebased := filepath.Join(filter.RebasePath, stripped)
|
|
absPath := filepath.Join(root, rebased)
|
|
|
|
switch header.Typeflag {
|
|
case tar.TypeDir:
|
|
err = os.MkdirAll(absPath, header.FileInfo().Mode())
|
|
if err != nil {
|
|
op.Errorf("Failed to create directory%s: %s", absPath, err)
|
|
return err
|
|
}
|
|
case tar.TypeSymlink:
|
|
err := os.Symlink(header.Linkname, absPath)
|
|
if err != nil {
|
|
op.Errorf("Failed to create symlink %s->%s: %s", absPath, header.Linkname, err)
|
|
return err
|
|
}
|
|
case tar.TypeReg:
|
|
f, err := os.OpenFile(absPath, fileWriteFlags, header.FileInfo().Mode())
|
|
if err != nil {
|
|
op.Errorf("Failed to open file %s: %s", absPath, err)
|
|
return err
|
|
}
|
|
_, err = io.Copy(f, tr)
|
|
// TODO: add ctx.Done cancellation
|
|
f.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
// TODO: add support for special file types - otherwise we will do absurd things such as read infinitely from /dev/random
|
|
}
|
|
op.Debugf("Finished writing to: %s", absPath)
|
|
}
|
|
return nil
|
|
}
|