Use go-winio tar-application code instead of our own
applyFunc now takes an io.Reader instead of a tar.Reader because I'm trying to mirror the API of the not-yet-exposed implementation of this same behaviour in github.com/Microsoft/hcsshim/internal/ociwclayer, with an eye to later moving to that implementation it is ever exposed. Signed-off-by: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
This commit is contained in:
parent
05647251a5
commit
3e47cdf70f
@ -114,16 +114,18 @@ func Apply(ctx context.Context, root string, r io.Reader, opts ...ApplyOpt) (int
|
|||||||
options.applyFunc = applyNaive
|
options.applyFunc = applyNaive
|
||||||
}
|
}
|
||||||
|
|
||||||
return options.applyFunc(ctx, root, tar.NewReader(r), options)
|
return options.applyFunc(ctx, root, r, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyNaive applies a tar stream of an OCI style diff tar to a directory
|
// applyNaive applies a tar stream of an OCI style diff tar to a directory
|
||||||
// applying each file as either a whole file or whiteout.
|
// applying each file as either a whole file or whiteout.
|
||||||
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets
|
||||||
func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) {
|
func applyNaive(ctx context.Context, root string, r io.Reader, options ApplyOptions) (size int64, err error) {
|
||||||
var (
|
var (
|
||||||
dirs []*tar.Header
|
dirs []*tar.Header
|
||||||
|
|
||||||
|
tr = tar.NewReader(r)
|
||||||
|
|
||||||
// Used for handling opaque directory markers which
|
// Used for handling opaque directory markers which
|
||||||
// may occur out of order
|
// may occur out of order
|
||||||
unpackedPaths = make(map[string]struct{})
|
unpackedPaths = make(map[string]struct{})
|
||||||
|
@ -19,6 +19,7 @@ package archive
|
|||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ApplyOptions provides additional options for an Apply operation
|
// ApplyOptions provides additional options for an Apply operation
|
||||||
@ -27,7 +28,7 @@ type ApplyOptions struct {
|
|||||||
ConvertWhiteout ConvertWhiteout // Convert whiteout files
|
ConvertWhiteout ConvertWhiteout // Convert whiteout files
|
||||||
Parents []string // Parent directories to handle inherited attributes without CoW
|
Parents []string // Parent directories to handle inherited attributes without CoW
|
||||||
|
|
||||||
applyFunc func(context.Context, string, *tar.Reader, ApplyOptions) (int64, error)
|
applyFunc func(context.Context, string, io.Reader, ApplyOptions) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyOpt allows setting mutable archive apply properties on creation
|
// ApplyOpt allows setting mutable archive apply properties on creation
|
||||||
|
@ -22,38 +22,20 @@ import (
|
|||||||
"archive/tar"
|
"archive/tar"
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/Microsoft/go-winio"
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/Microsoft/go-winio/backuptar"
|
||||||
"github.com/Microsoft/hcsshim"
|
"github.com/Microsoft/hcsshim"
|
||||||
"github.com/containerd/containerd/sys"
|
"github.com/containerd/containerd/sys"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// MSWINDOWS pax vendor extensions
|
|
||||||
hdrMSWindowsPrefix = "MSWINDOWS."
|
|
||||||
|
|
||||||
hdrFileAttributes = hdrMSWindowsPrefix + "fileattr"
|
|
||||||
hdrSecurityDescriptor = hdrMSWindowsPrefix + "sd"
|
|
||||||
hdrRawSecurityDescriptor = hdrMSWindowsPrefix + "rawsd"
|
|
||||||
hdrMountPoint = hdrMSWindowsPrefix + "mountpoint"
|
|
||||||
hdrEaPrefix = hdrMSWindowsPrefix + "xattr."
|
|
||||||
|
|
||||||
// LIBARCHIVE pax vendor extensions
|
|
||||||
hdrLibArchivePrefix = "LIBARCHIVE."
|
|
||||||
|
|
||||||
hdrCreateTime = hdrLibArchivePrefix + "creationtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// mutatedFiles is a list of files that are mutated by the import process
|
// mutatedFiles is a list of files that are mutated by the import process
|
||||||
// and must be backed up and restored.
|
// and must be backed up and restored.
|
||||||
@ -152,7 +134,7 @@ func setxattr(path, key, value string) error {
|
|||||||
// applyWindowsLayer applies a tar stream of an OCI style diff tar of a Windows
|
// applyWindowsLayer applies a tar stream of an OCI style diff tar of a Windows
|
||||||
// layer using the hcsshim layer writer and backup streams.
|
// layer using the hcsshim layer writer and backup streams.
|
||||||
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets
|
// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets
|
||||||
func applyWindowsLayer(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) {
|
func applyWindowsLayer(ctx context.Context, root string, r io.Reader, options ApplyOptions) (size int64, err error) {
|
||||||
home, id := filepath.Split(root)
|
home, id := filepath.Split(root)
|
||||||
info := hcsshim.DriverInfo{
|
info := hcsshim.DriverInfo{
|
||||||
HomeDir: home,
|
HomeDir: home,
|
||||||
@ -172,6 +154,7 @@ func applyWindowsLayer(ctx context.Context, root string, tr *tar.Reader, options
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
tr := tar.NewReader(r)
|
||||||
buf := bufio.NewWriter(nil)
|
buf := bufio.NewWriter(nil)
|
||||||
hdr, nextErr := tr.Next()
|
hdr, nextErr := tr.Next()
|
||||||
// Iterate through the files in the archive.
|
// Iterate through the files in the archive.
|
||||||
@ -208,7 +191,7 @@ func applyWindowsLayer(ctx context.Context, root string, tr *tar.Reader, options
|
|||||||
}
|
}
|
||||||
hdr, nextErr = tr.Next()
|
hdr, nextErr = tr.Next()
|
||||||
} else {
|
} else {
|
||||||
name, fileSize, fileInfo, err := fileInfoFromHeader(hdr)
|
name, fileSize, fileInfo, err := backuptar.FileInfoFromHeader(hdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -223,42 +206,6 @@ func applyWindowsLayer(ctx context.Context, root string, tr *tar.Reader, options
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// fileInfoFromHeader retrieves basic Win32 file information from a tar header, using the additional metadata written by
|
|
||||||
// WriteTarFileFromBackupStream.
|
|
||||||
func fileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *winio.FileBasicInfo, err error) {
|
|
||||||
name = hdr.Name
|
|
||||||
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
|
||||||
size = hdr.Size
|
|
||||||
}
|
|
||||||
fileInfo = &winio.FileBasicInfo{
|
|
||||||
LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()),
|
|
||||||
LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
|
|
||||||
ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()),
|
|
||||||
|
|
||||||
// Default CreationTime to ModTime, updated below if MSWINDOWS.createtime exists
|
|
||||||
CreationTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
|
|
||||||
}
|
|
||||||
if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok {
|
|
||||||
attr, err := strconv.ParseUint(attrStr, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, nil, err
|
|
||||||
}
|
|
||||||
fileInfo.FileAttributes = uint32(attr)
|
|
||||||
} else {
|
|
||||||
if hdr.Typeflag == tar.TypeDir {
|
|
||||||
fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if createStr, ok := hdr.PAXRecords[hdrCreateTime]; ok {
|
|
||||||
createTime, err := parsePAXTime(createStr)
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, nil, err
|
|
||||||
}
|
|
||||||
fileInfo.CreationTime = syscall.NsecToFiletime(createTime.UnixNano())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// tarToBackupStreamWithMutatedFiles reads data from a tar stream and
|
// tarToBackupStreamWithMutatedFiles reads data from a tar stream and
|
||||||
// writes it to a backup stream, and also saves any files that will be mutated
|
// writes it to a backup stream, and also saves any files that will be mutated
|
||||||
// by the import layer process to a backup location.
|
// by the import layer process to a backup location.
|
||||||
@ -299,137 +246,7 @@ func tarToBackupStreamWithMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Re
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return writeBackupStreamFromTarFile(buf, t, hdr)
|
return backuptar.WriteBackupStreamFromTarFile(buf, t, hdr)
|
||||||
}
|
|
||||||
|
|
||||||
// writeBackupStreamFromTarFile writes a Win32 backup stream from the current tar file. Since this function may process multiple
|
|
||||||
// tar file entries in order to collect all the alternate data streams for the file, it returns the next
|
|
||||||
// tar file that was not processed, or io.EOF is there are no more.
|
|
||||||
func writeBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) {
|
|
||||||
bw := winio.NewBackupStreamWriter(w)
|
|
||||||
var sd []byte
|
|
||||||
var err error
|
|
||||||
// Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written
|
|
||||||
// by this library will have raw binary for the security descriptor.
|
|
||||||
if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok {
|
|
||||||
sd, err = winio.SddlToSecurityDescriptor(sddl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok {
|
|
||||||
sd, err = base64.StdEncoding.DecodeString(sdraw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(sd) != 0 {
|
|
||||||
bhdr := winio.BackupHeader{
|
|
||||||
Id: winio.BackupSecurity,
|
|
||||||
Size: int64(len(sd)),
|
|
||||||
}
|
|
||||||
err := bw.WriteHeader(&bhdr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = bw.Write(sd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var eas []winio.ExtendedAttribute
|
|
||||||
for k, v := range hdr.PAXRecords {
|
|
||||||
if !strings.HasPrefix(k, hdrEaPrefix) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data, err := base64.StdEncoding.DecodeString(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
eas = append(eas, winio.ExtendedAttribute{
|
|
||||||
Name: k[len(hdrEaPrefix):],
|
|
||||||
Value: data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if len(eas) != 0 {
|
|
||||||
eadata, err := winio.EncodeExtendedAttributes(eas)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
bhdr := winio.BackupHeader{
|
|
||||||
Id: winio.BackupEaData,
|
|
||||||
Size: int64(len(eadata)),
|
|
||||||
}
|
|
||||||
err = bw.WriteHeader(&bhdr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = bw.Write(eadata)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hdr.Typeflag == tar.TypeSymlink {
|
|
||||||
_, isMountPoint := hdr.PAXRecords[hdrMountPoint]
|
|
||||||
rp := winio.ReparsePoint{
|
|
||||||
Target: filepath.FromSlash(hdr.Linkname),
|
|
||||||
IsMountPoint: isMountPoint,
|
|
||||||
}
|
|
||||||
reparse := winio.EncodeReparsePoint(&rp)
|
|
||||||
bhdr := winio.BackupHeader{
|
|
||||||
Id: winio.BackupReparseData,
|
|
||||||
Size: int64(len(reparse)),
|
|
||||||
}
|
|
||||||
err := bw.WriteHeader(&bhdr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = bw.Write(reparse)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := bufPool.Get().(*[]byte)
|
|
||||||
defer bufPool.Put(buf)
|
|
||||||
|
|
||||||
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
|
||||||
bhdr := winio.BackupHeader{
|
|
||||||
Id: winio.BackupData,
|
|
||||||
Size: hdr.Size,
|
|
||||||
}
|
|
||||||
err := bw.WriteHeader(&bhdr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = io.CopyBuffer(bw, t, *buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Copy all the alternate data streams and return the next non-ADS header.
|
|
||||||
for {
|
|
||||||
ahdr, err := t.Next()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") {
|
|
||||||
return ahdr, nil
|
|
||||||
}
|
|
||||||
bhdr := winio.BackupHeader{
|
|
||||||
Id: winio.BackupAlternateData,
|
|
||||||
Size: ahdr.Size,
|
|
||||||
Name: ahdr.Name[len(hdr.Name):] + ":$DATA",
|
|
||||||
}
|
|
||||||
err = bw.WriteHeader(&bhdr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = io.CopyBuffer(bw, t, *buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyDirInfo(fi os.FileInfo, path string) error {
|
func copyDirInfo(fi os.FileInfo, path string) error {
|
||||||
|
4
go.sum
4
go.sum
@ -28,6 +28,10 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt
|
|||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
|
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||||
|
github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||||
github.com/Microsoft/go-winio v0.4.15 h1:qkLXKzb1QoVatRyd/YlXZ/Kg0m5K3SPuoD82jjSOaBc=
|
github.com/Microsoft/go-winio v0.4.15 h1:qkLXKzb1QoVatRyd/YlXZ/Kg0m5K3SPuoD82jjSOaBc=
|
||||||
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||||
|
4
vendor/github.com/Microsoft/go-winio/backuptar/noop.go
generated
vendored
Normal file
4
vendor/github.com/Microsoft/go-winio/backuptar/noop.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// +build !windows
|
||||||
|
// This file only exists to allow go get on non-Windows platforms.
|
||||||
|
|
||||||
|
package backuptar
|
50
archive/strconv.go → vendor/github.com/Microsoft/go-winio/backuptar/strconv.go
generated
vendored
50
archive/strconv.go → vendor/github.com/Microsoft/go-winio/backuptar/strconv.go
generated
vendored
@ -1,34 +1,16 @@
|
|||||||
// +build windows
|
package backuptar
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package archive
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"archive/tar"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Forked from https://github.com/golang/go/blob/master/src/archive/tar/strconv.go
|
// Functions copied from https://github.com/golang/go/blob/master/src/archive/tar/strconv.go
|
||||||
// as archive/tar doesn't support CreationTime, but does handle PAX time parsing,
|
// as we need to manage the LIBARCHIVE.creationtime PAXRecord manually.
|
||||||
// and there's no need to re-invent the wheel.
|
// Idea taken from containerd which did the same thing.
|
||||||
|
|
||||||
// parsePAXTime takes a string of the form %d.%d as described in the PAX
|
// parsePAXTime takes a string of the form %d.%d as described in the PAX
|
||||||
// specification. Note that this implementation allows for negative timestamps,
|
// specification. Note that this implementation allows for negative timestamps,
|
||||||
@ -62,7 +44,25 @@ func parsePAXTime(s string) (time.Time, error) {
|
|||||||
}
|
}
|
||||||
nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
|
nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
|
||||||
if len(ss) > 0 && ss[0] == '-' {
|
if len(ss) > 0 && ss[0] == '-' {
|
||||||
return time.Unix(secs, -nsecs), nil // Negative correction
|
return time.Unix(secs, -1*nsecs), nil // Negative correction
|
||||||
}
|
}
|
||||||
return time.Unix(secs, nsecs), nil
|
return time.Unix(secs, nsecs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// formatPAXTime converts ts into a time of the form %d.%d as described in the
|
||||||
|
// PAX specification. This function is capable of negative timestamps.
|
||||||
|
func formatPAXTime(ts time.Time) (s string) {
|
||||||
|
secs, nsecs := ts.Unix(), ts.Nanosecond()
|
||||||
|
if nsecs == 0 {
|
||||||
|
return strconv.FormatInt(secs, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If seconds is negative, then perform correction.
|
||||||
|
sign := ""
|
||||||
|
if secs < 0 {
|
||||||
|
sign = "-" // Remember sign
|
||||||
|
secs = -(secs + 1) // Add a second to secs
|
||||||
|
nsecs = -(nsecs - 1e9) // Take that second away from nsecs
|
||||||
|
}
|
||||||
|
return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0")
|
||||||
|
}
|
451
vendor/github.com/Microsoft/go-winio/backuptar/tar.go
generated
vendored
Normal file
451
vendor/github.com/Microsoft/go-winio/backuptar/tar.go
generated
vendored
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package backuptar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
c_ISUID = 04000 // Set uid
|
||||||
|
c_ISGID = 02000 // Set gid
|
||||||
|
c_ISVTX = 01000 // Save text (sticky bit)
|
||||||
|
c_ISDIR = 040000 // Directory
|
||||||
|
c_ISFIFO = 010000 // FIFO
|
||||||
|
c_ISREG = 0100000 // Regular file
|
||||||
|
c_ISLNK = 0120000 // Symbolic link
|
||||||
|
c_ISBLK = 060000 // Block special file
|
||||||
|
c_ISCHR = 020000 // Character special file
|
||||||
|
c_ISSOCK = 0140000 // Socket
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
hdrFileAttributes = "MSWINDOWS.fileattr"
|
||||||
|
hdrSecurityDescriptor = "MSWINDOWS.sd"
|
||||||
|
hdrRawSecurityDescriptor = "MSWINDOWS.rawsd"
|
||||||
|
hdrMountPoint = "MSWINDOWS.mountpoint"
|
||||||
|
hdrEaPrefix = "MSWINDOWS.xattr."
|
||||||
|
|
||||||
|
hdrCreationTime = "LIBARCHIVE.creationtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeZeroes(w io.Writer, count int64) error {
|
||||||
|
buf := make([]byte, 8192)
|
||||||
|
c := len(buf)
|
||||||
|
for i := int64(0); i < count; i += int64(c) {
|
||||||
|
if int64(c) > count-i {
|
||||||
|
c = int(count - i)
|
||||||
|
}
|
||||||
|
_, err := w.Write(buf[:c])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error {
|
||||||
|
curOffset := int64(0)
|
||||||
|
for {
|
||||||
|
bhdr, err := br.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if bhdr.Id != winio.BackupSparseBlock {
|
||||||
|
return fmt.Errorf("unexpected stream %d", bhdr.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// archive/tar does not support writing sparse files
|
||||||
|
// so just write zeroes to catch up to the current offset.
|
||||||
|
err = writeZeroes(t, bhdr.Offset-curOffset)
|
||||||
|
if bhdr.Size == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n, err := io.Copy(t, br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
curOffset = bhdr.Offset + n
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicInfoHeader creates a tar header from basic file information.
|
||||||
|
func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *tar.Header {
|
||||||
|
hdr := &tar.Header{
|
||||||
|
Format: tar.FormatPAX,
|
||||||
|
Name: filepath.ToSlash(name),
|
||||||
|
Size: size,
|
||||||
|
Typeflag: tar.TypeReg,
|
||||||
|
ModTime: time.Unix(0, fileInfo.LastWriteTime.Nanoseconds()),
|
||||||
|
ChangeTime: time.Unix(0, fileInfo.ChangeTime.Nanoseconds()),
|
||||||
|
AccessTime: time.Unix(0, fileInfo.LastAccessTime.Nanoseconds()),
|
||||||
|
PAXRecords: make(map[string]string),
|
||||||
|
}
|
||||||
|
hdr.PAXRecords[hdrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes)
|
||||||
|
hdr.PAXRecords[hdrCreationTime] = formatPAXTime(time.Unix(0, fileInfo.CreationTime.Nanoseconds()))
|
||||||
|
|
||||||
|
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||||
|
hdr.Mode |= c_ISDIR
|
||||||
|
hdr.Size = 0
|
||||||
|
hdr.Typeflag = tar.TypeDir
|
||||||
|
}
|
||||||
|
return hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTarFileFromBackupStream writes a file to a tar writer using data from a Win32 backup stream.
|
||||||
|
//
|
||||||
|
// This encodes Win32 metadata as tar pax vendor extensions starting with MSWINDOWS.
|
||||||
|
//
|
||||||
|
// The additional Win32 metadata is:
|
||||||
|
//
|
||||||
|
// MSWINDOWS.fileattr: The Win32 file attributes, as a decimal value
|
||||||
|
//
|
||||||
|
// MSWINDOWS.rawsd: The Win32 security descriptor, in raw binary format
|
||||||
|
//
|
||||||
|
// MSWINDOWS.mountpoint: If present, this is a mount point and not a symlink, even though the type is '2' (symlink)
|
||||||
|
func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size int64, fileInfo *winio.FileBasicInfo) error {
|
||||||
|
name = filepath.ToSlash(name)
|
||||||
|
hdr := BasicInfoHeader(name, size, fileInfo)
|
||||||
|
|
||||||
|
// If r can be seeked, then this function is two-pass: pass 1 collects the
|
||||||
|
// tar header data, and pass 2 copies the data stream. If r cannot be
|
||||||
|
// seeked, then some header data (in particular EAs) will be silently lost.
|
||||||
|
var (
|
||||||
|
restartPos int64
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
sr, readTwice := r.(io.Seeker)
|
||||||
|
if readTwice {
|
||||||
|
if restartPos, err = sr.Seek(0, io.SeekCurrent); err != nil {
|
||||||
|
readTwice = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
br := winio.NewBackupStreamReader(r)
|
||||||
|
var dataHdr *winio.BackupHeader
|
||||||
|
for dataHdr == nil {
|
||||||
|
bhdr, err := br.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch bhdr.Id {
|
||||||
|
case winio.BackupData:
|
||||||
|
hdr.Mode |= c_ISREG
|
||||||
|
if !readTwice {
|
||||||
|
dataHdr = bhdr
|
||||||
|
}
|
||||||
|
case winio.BackupSecurity:
|
||||||
|
sd, err := ioutil.ReadAll(br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hdr.PAXRecords[hdrRawSecurityDescriptor] = base64.StdEncoding.EncodeToString(sd)
|
||||||
|
|
||||||
|
case winio.BackupReparseData:
|
||||||
|
hdr.Mode |= c_ISLNK
|
||||||
|
hdr.Typeflag = tar.TypeSymlink
|
||||||
|
reparseBuffer, err := ioutil.ReadAll(br)
|
||||||
|
rp, err := winio.DecodeReparsePoint(reparseBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rp.IsMountPoint {
|
||||||
|
hdr.PAXRecords[hdrMountPoint] = "1"
|
||||||
|
}
|
||||||
|
hdr.Linkname = rp.Target
|
||||||
|
|
||||||
|
case winio.BackupEaData:
|
||||||
|
eab, err := ioutil.ReadAll(br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
eas, err := winio.DecodeExtendedAttributes(eab)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, ea := range eas {
|
||||||
|
// Use base64 encoding for the binary value. Note that there
|
||||||
|
// is no way to encode the EA's flags, since their use doesn't
|
||||||
|
// make any sense for persisted EAs.
|
||||||
|
hdr.PAXRecords[hdrEaPrefix+ea.Name] = base64.StdEncoding.EncodeToString(ea.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
case winio.BackupAlternateData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
|
||||||
|
// ignore these streams
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%s: unknown stream ID %d", name, bhdr.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.WriteHeader(hdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if readTwice {
|
||||||
|
// Get back to the data stream.
|
||||||
|
if _, err = sr.Seek(restartPos, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dataHdr == nil {
|
||||||
|
bhdr, err := br.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if bhdr.Id == winio.BackupData {
|
||||||
|
dataHdr = bhdr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataHdr != nil {
|
||||||
|
// A data stream was found. Copy the data.
|
||||||
|
if (dataHdr.Attributes & winio.StreamSparseAttributes) == 0 {
|
||||||
|
if size != dataHdr.Size {
|
||||||
|
return fmt.Errorf("%s: mismatch between file size %d and header size %d", name, size, dataHdr.Size)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(t, br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = copySparse(t, br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for streams after the data stream. The only ones we handle are alternate data streams.
|
||||||
|
// Other streams may have metadata that could be serialized, but the tar header has already
|
||||||
|
// been written. In practice, this means that we don't get EA or TXF metadata.
|
||||||
|
for {
|
||||||
|
bhdr, err := br.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch bhdr.Id {
|
||||||
|
case winio.BackupAlternateData:
|
||||||
|
altName := bhdr.Name
|
||||||
|
if strings.HasSuffix(altName, ":$DATA") {
|
||||||
|
altName = altName[:len(altName)-len(":$DATA")]
|
||||||
|
}
|
||||||
|
if (bhdr.Attributes & winio.StreamSparseAttributes) == 0 {
|
||||||
|
hdr = &tar.Header{
|
||||||
|
Format: hdr.Format,
|
||||||
|
Name: name + altName,
|
||||||
|
Mode: hdr.Mode,
|
||||||
|
Typeflag: tar.TypeReg,
|
||||||
|
Size: bhdr.Size,
|
||||||
|
ModTime: hdr.ModTime,
|
||||||
|
AccessTime: hdr.AccessTime,
|
||||||
|
ChangeTime: hdr.ChangeTime,
|
||||||
|
}
|
||||||
|
err = t.WriteHeader(hdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(t, br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Unsupported for now, since the size of the alternate stream is not present
|
||||||
|
// in the backup stream until after the data has been read.
|
||||||
|
return errors.New("tar of sparse alternate data streams is unsupported")
|
||||||
|
}
|
||||||
|
case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
|
||||||
|
// ignore these streams
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%s: unknown stream ID %d after data", name, bhdr.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileInfoFromHeader retrieves basic Win32 file information from a tar header, using the additional metadata written by
|
||||||
|
// WriteTarFileFromBackupStream.
|
||||||
|
func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *winio.FileBasicInfo, err error) {
|
||||||
|
name = hdr.Name
|
||||||
|
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
||||||
|
size = hdr.Size
|
||||||
|
}
|
||||||
|
fileInfo = &winio.FileBasicInfo{
|
||||||
|
LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()),
|
||||||
|
LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
|
||||||
|
ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()),
|
||||||
|
// Default to ModTime, we'll pull hdrCreationTime below if present
|
||||||
|
CreationTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
|
||||||
|
}
|
||||||
|
if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok {
|
||||||
|
attr, err := strconv.ParseUint(attrStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, nil, err
|
||||||
|
}
|
||||||
|
fileInfo.FileAttributes = uint32(attr)
|
||||||
|
} else {
|
||||||
|
if hdr.Typeflag == tar.TypeDir {
|
||||||
|
fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if creationTimeStr, ok := hdr.PAXRecords[hdrCreationTime]; ok {
|
||||||
|
creationTime, err := parsePAXTime(creationTimeStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, nil, err
|
||||||
|
}
|
||||||
|
fileInfo.CreationTime = syscall.NsecToFiletime(creationTime.UnixNano())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteBackupStreamFromTarFile writes a Win32 backup stream from the current tar file. Since this function may process multiple
|
||||||
|
// tar file entries in order to collect all the alternate data streams for the file, it returns the next
|
||||||
|
// tar file that was not processed, or io.EOF is there are no more.
|
||||||
|
func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) {
|
||||||
|
bw := winio.NewBackupStreamWriter(w)
|
||||||
|
var sd []byte
|
||||||
|
var err error
|
||||||
|
// Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written
|
||||||
|
// by this library will have raw binary for the security descriptor.
|
||||||
|
if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok {
|
||||||
|
sd, err = winio.SddlToSecurityDescriptor(sddl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok {
|
||||||
|
sd, err = base64.StdEncoding.DecodeString(sdraw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(sd) != 0 {
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupSecurity,
|
||||||
|
Size: int64(len(sd)),
|
||||||
|
}
|
||||||
|
err := bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = bw.Write(sd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var eas []winio.ExtendedAttribute
|
||||||
|
for k, v := range hdr.PAXRecords {
|
||||||
|
if !strings.HasPrefix(k, hdrEaPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, err := base64.StdEncoding.DecodeString(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
eas = append(eas, winio.ExtendedAttribute{
|
||||||
|
Name: k[len(hdrEaPrefix):],
|
||||||
|
Value: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(eas) != 0 {
|
||||||
|
eadata, err := winio.EncodeExtendedAttributes(eas)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupEaData,
|
||||||
|
Size: int64(len(eadata)),
|
||||||
|
}
|
||||||
|
err = bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = bw.Write(eadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hdr.Typeflag == tar.TypeSymlink {
|
||||||
|
_, isMountPoint := hdr.PAXRecords[hdrMountPoint]
|
||||||
|
rp := winio.ReparsePoint{
|
||||||
|
Target: filepath.FromSlash(hdr.Linkname),
|
||||||
|
IsMountPoint: isMountPoint,
|
||||||
|
}
|
||||||
|
reparse := winio.EncodeReparsePoint(&rp)
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupReparseData,
|
||||||
|
Size: int64(len(reparse)),
|
||||||
|
}
|
||||||
|
err := bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = bw.Write(reparse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupData,
|
||||||
|
Size: hdr.Size,
|
||||||
|
}
|
||||||
|
err := bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(bw, t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Copy all the alternate data streams and return the next non-ADS header.
|
||||||
|
for {
|
||||||
|
ahdr, err := t.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") {
|
||||||
|
return ahdr, nil
|
||||||
|
}
|
||||||
|
bhdr := winio.BackupHeader{
|
||||||
|
Id: winio.BackupAlternateData,
|
||||||
|
Size: ahdr.Size,
|
||||||
|
Name: ahdr.Name[len(hdr.Name):] + ":$DATA",
|
||||||
|
}
|
||||||
|
err = bw.WriteHeader(&bhdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(bw, t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -4,6 +4,7 @@ github.com/BurntSushi/toml
|
|||||||
# github.com/Microsoft/go-winio v0.4.15
|
# github.com/Microsoft/go-winio v0.4.15
|
||||||
## explicit
|
## explicit
|
||||||
github.com/Microsoft/go-winio
|
github.com/Microsoft/go-winio
|
||||||
|
github.com/Microsoft/go-winio/backuptar
|
||||||
github.com/Microsoft/go-winio/pkg/etw
|
github.com/Microsoft/go-winio/pkg/etw
|
||||||
github.com/Microsoft/go-winio/pkg/etwlogrus
|
github.com/Microsoft/go-winio/pkg/etwlogrus
|
||||||
github.com/Microsoft/go-winio/pkg/fs
|
github.com/Microsoft/go-winio/pkg/fs
|
||||||
|
Loading…
Reference in New Issue
Block a user