mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-09-05 16:47:31 -04:00
Use tar fork to encode Windows metadata correctly
This commit is contained in:
+29
-30
@@ -1,7 +1,6 @@
|
|||||||
package backuptar
|
package backuptar
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -13,6 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Microsoft/go-winio"
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/Microsoft/go-winio/archive/tar" // until archive/tar supports pax extensions in its interface
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -29,13 +29,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
xattrFileAttributes = "MSWINDOWS.fileattr"
|
hdrFileAttributes = "fileattr"
|
||||||
xattrAccessTime = "MSWINDOWS.accesstime"
|
hdrAccessTime = "accesstime"
|
||||||
xattrChangeTime = "MSWINDOWS.changetime"
|
hdrChangeTime = "changetime"
|
||||||
xattrCreateTime = "MSWINDOWS.createtime"
|
hdrCreateTime = "createtime"
|
||||||
xattrWriteTime = "MSWINDOWS.writetime"
|
hdrWriteTime = "writetime"
|
||||||
xattrSecurityDescriptor = "MSWINDOWS.sd"
|
hdrSecurityDescriptor = "sd"
|
||||||
xattrMountPoint = "MSWINDOWS.mountpoint"
|
hdrMountPoint = "mountpoint"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeZeroes(w io.Writer, count int64) error {
|
func writeZeroes(w io.Writer, count int64) error {
|
||||||
@@ -82,8 +82,8 @@ func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func win32TimeFromTar(key string, xattrs map[string]string, unixTime time.Time) syscall.Filetime {
|
func win32TimeFromTar(key string, hdrs map[string]string, unixTime time.Time) syscall.Filetime {
|
||||||
if s, ok := xattrs[key]; ok {
|
if s, ok := hdrs[key]; ok {
|
||||||
n, err := strconv.ParseUint(s, 10, 64)
|
n, err := strconv.ParseUint(s, 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return syscall.Filetime{uint32(n & 0xffffffff), uint32(n >> 32)}
|
return syscall.Filetime{uint32(n & 0xffffffff), uint32(n >> 32)}
|
||||||
@@ -98,8 +98,7 @@ func win32TimeToTar(ft syscall.Filetime) (string, time.Time) {
|
|||||||
|
|
||||||
// Writes a file to a tar writer using data from a Win32 backup stream.
|
// Writes a file to a tar writer using data from a Win32 backup stream.
|
||||||
//
|
//
|
||||||
// Currently this encodes Win32 metadata as tar xattrs (i.e. SCHILY.xattr.*), which is incorrect.
|
// This encodes Win32 metadata as tar pax vendor extensions starting with MSWINDOWS.
|
||||||
// This should be fixed archive/tar exposes the ability to write general pax headers.
|
|
||||||
//
|
//
|
||||||
// The additional Win32 metadata is:
|
// The additional Win32 metadata is:
|
||||||
//
|
//
|
||||||
@@ -119,16 +118,16 @@ func win32TimeToTar(ft syscall.Filetime) (string, time.Time) {
|
|||||||
func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size int64, fileInfo *winio.FileBasicInfo) error {
|
func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size int64, fileInfo *winio.FileBasicInfo) error {
|
||||||
name = filepath.ToSlash(name)
|
name = filepath.ToSlash(name)
|
||||||
hdr := &tar.Header{
|
hdr := &tar.Header{
|
||||||
Name: name,
|
Name: name,
|
||||||
Size: size,
|
Size: size,
|
||||||
Typeflag: tar.TypeReg,
|
Typeflag: tar.TypeReg,
|
||||||
Xattrs: make(map[string]string),
|
Winheaders: make(map[string]string),
|
||||||
}
|
}
|
||||||
hdr.Xattrs[xattrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes)
|
hdr.Winheaders[hdrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes)
|
||||||
hdr.Xattrs[xattrAccessTime], hdr.AccessTime = win32TimeToTar(fileInfo.LastAccessTime)
|
hdr.Winheaders[hdrAccessTime], hdr.AccessTime = win32TimeToTar(fileInfo.LastAccessTime)
|
||||||
hdr.Xattrs[xattrChangeTime], hdr.ChangeTime = win32TimeToTar(fileInfo.ChangeTime)
|
hdr.Winheaders[hdrChangeTime], hdr.ChangeTime = win32TimeToTar(fileInfo.ChangeTime)
|
||||||
hdr.Xattrs[xattrCreateTime], _ = win32TimeToTar(fileInfo.CreationTime)
|
hdr.Winheaders[hdrCreateTime], _ = win32TimeToTar(fileInfo.CreationTime)
|
||||||
hdr.Xattrs[xattrWriteTime], hdr.ModTime = win32TimeToTar(fileInfo.LastWriteTime)
|
hdr.Winheaders[hdrWriteTime], hdr.ModTime = win32TimeToTar(fileInfo.LastWriteTime)
|
||||||
|
|
||||||
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||||
hdr.Mode |= c_ISDIR
|
hdr.Mode |= c_ISDIR
|
||||||
@@ -159,7 +158,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hdr.Xattrs[xattrSecurityDescriptor] = sddl
|
hdr.Winheaders[hdrSecurityDescriptor] = sddl
|
||||||
|
|
||||||
case winio.BackupReparseData:
|
case winio.BackupReparseData:
|
||||||
hdr.Mode |= c_ISLNK
|
hdr.Mode |= c_ISLNK
|
||||||
@@ -170,7 +169,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rp.IsMountPoint {
|
if rp.IsMountPoint {
|
||||||
hdr.Xattrs[xattrMountPoint] = "1"
|
hdr.Winheaders[hdrMountPoint] = "1"
|
||||||
}
|
}
|
||||||
hdr.Linkname = rp.Target
|
hdr.Linkname = rp.Target
|
||||||
case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
|
case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData:
|
||||||
@@ -261,12 +260,12 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win
|
|||||||
size = hdr.Size
|
size = hdr.Size
|
||||||
}
|
}
|
||||||
fileInfo = &winio.FileBasicInfo{
|
fileInfo = &winio.FileBasicInfo{
|
||||||
LastAccessTime: win32TimeFromTar(xattrAccessTime, hdr.Xattrs, hdr.AccessTime),
|
LastAccessTime: win32TimeFromTar(hdrAccessTime, hdr.Winheaders, hdr.AccessTime),
|
||||||
LastWriteTime: win32TimeFromTar(xattrWriteTime, hdr.Xattrs, hdr.ModTime),
|
LastWriteTime: win32TimeFromTar(hdrWriteTime, hdr.Winheaders, hdr.ModTime),
|
||||||
ChangeTime: win32TimeFromTar(xattrChangeTime, hdr.Xattrs, hdr.ChangeTime),
|
ChangeTime: win32TimeFromTar(hdrChangeTime, hdr.Winheaders, hdr.ChangeTime),
|
||||||
CreationTime: win32TimeFromTar(xattrCreateTime, hdr.Xattrs, hdr.ModTime),
|
CreationTime: win32TimeFromTar(hdrCreateTime, hdr.Winheaders, hdr.ModTime),
|
||||||
}
|
}
|
||||||
if attrStr, ok := hdr.Xattrs[xattrFileAttributes]; ok {
|
if attrStr, ok := hdr.Winheaders[hdrFileAttributes]; ok {
|
||||||
attr, err := strconv.ParseUint(attrStr, 10, 32)
|
attr, err := strconv.ParseUint(attrStr, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, nil, err
|
return "", 0, nil, err
|
||||||
@@ -285,7 +284,7 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win
|
|||||||
// tar file that was not processed, or io.EOF is there are no more.
|
// 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) {
|
func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) {
|
||||||
bw := winio.NewBackupStreamWriter(w)
|
bw := winio.NewBackupStreamWriter(w)
|
||||||
if sddl, ok := hdr.Xattrs[xattrSecurityDescriptor]; ok {
|
if sddl, ok := hdr.Winheaders[hdrSecurityDescriptor]; ok {
|
||||||
sd, err := winio.SddlToSecurityDescriptor(sddl)
|
sd, err := winio.SddlToSecurityDescriptor(sddl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -304,7 +303,7 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if hdr.Typeflag == tar.TypeSymlink {
|
if hdr.Typeflag == tar.TypeSymlink {
|
||||||
_, isMountPoint := hdr.Xattrs[xattrMountPoint]
|
_, isMountPoint := hdr.Winheaders[hdrMountPoint]
|
||||||
rp := winio.ReparsePoint{
|
rp := winio.ReparsePoint{
|
||||||
Target: hdr.Linkname,
|
Target: hdr.Linkname,
|
||||||
IsMountPoint: isMountPoint,
|
IsMountPoint: isMountPoint,
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package backuptar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/Microsoft/go-winio/archive/tar"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ensurePresent(t *testing.T, m map[string]string, keys ...string) {
|
||||||
|
for _, k := range keys {
|
||||||
|
if _, ok := m[k]; !ok {
|
||||||
|
t.Error(k, "not present in tar header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoundTrip(t *testing.T) {
|
||||||
|
f, err := ioutil.TempFile("", "tst")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
|
||||||
|
if _, err = f.Write([]byte("testing 1 2 3\n")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = f.Seek(0, 0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bi, err := winio.GetFileBasicInfo(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
br := winio.NewBackupFileReader(f, true)
|
||||||
|
defer br.Close()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
tw := tar.NewWriter(&buf)
|
||||||
|
|
||||||
|
err = WriteTarFileFromBackupStream(tw, br, f.Name(), fi.Size(), bi)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := tar.NewReader(&buf)
|
||||||
|
hdr, err := tr.Next()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ensurePresent(t, hdr.Winheaders, "fileattr", "sd", "accesstime", "changetime", "createtime", "writetime")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user