mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-09-03 15:47:38 -04:00
Use existing PAX headers for time
This removes the MSWINDOWS.* time fields and uses PAX times directly. I didn't previously realize that PAX times could be negative, which eliminates the need to store Windows times in a different format. This also uses the LIBARCHIVE.creationtime field for creation times to match OS X behavior.
This commit is contained in:
+15
-41
@@ -30,10 +30,6 @@ const (
|
||||
|
||||
const (
|
||||
hdrFileAttributes = "fileattr"
|
||||
hdrAccessTime = "accesstime"
|
||||
hdrChangeTime = "changetime"
|
||||
hdrCreateTime = "createtime"
|
||||
hdrWriteTime = "writetime"
|
||||
hdrSecurityDescriptor = "sd"
|
||||
hdrMountPoint = "mountpoint"
|
||||
)
|
||||
@@ -82,21 +78,7 @@ func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func win32TimeFromTar(key string, hdrs map[string]string, unixTime time.Time) syscall.Filetime {
|
||||
if s, ok := hdrs[key]; ok {
|
||||
n, err := strconv.ParseUint(s, 10, 64)
|
||||
if err == nil {
|
||||
return syscall.Filetime{uint32(n & 0xffffffff), uint32(n >> 32)}
|
||||
}
|
||||
}
|
||||
return syscall.NsecToFiletime(unixTime.UnixNano())
|
||||
}
|
||||
|
||||
func win32TimeToTar(ft syscall.Filetime) (string, time.Time) {
|
||||
return fmt.Sprintf("%d", uint64(ft.LowDateTime)+(uint64(ft.HighDateTime)<<32)), time.Unix(0, ft.Nanoseconds())
|
||||
}
|
||||
|
||||
// Writes a file to a tar writer using data from a Win32 backup stream.
|
||||
// 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.
|
||||
//
|
||||
@@ -104,30 +86,22 @@ func win32TimeToTar(ft syscall.Filetime) (string, time.Time) {
|
||||
//
|
||||
// MSWINDOWS.fileattr: The Win32 file attributes, as a decimal value
|
||||
//
|
||||
// MSWINDOWS.accesstime: The last access time, as a Filetime expressed as a 64-bit decimal value.
|
||||
//
|
||||
// MSWINDOWS.createtime: The creation time, as a Filetime expressed as a 64-bit decimal value.
|
||||
//
|
||||
// MSWINDOWS.changetime: The creation time, as a Filetime expressed as a 64-bit decimal value.
|
||||
//
|
||||
// MSWINDOWS.writetime: The creation time, as a Filetime expressed as a 64-bit decimal value.
|
||||
//
|
||||
// MSWINDOWS.sd: The Win32 security descriptor, in SDDL (string) 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 := &tar.Header{
|
||||
Name: name,
|
||||
Size: size,
|
||||
Typeflag: tar.TypeReg,
|
||||
Winheaders: make(map[string]string),
|
||||
Name: 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()),
|
||||
CreationTime: time.Unix(0, fileInfo.CreationTime.Nanoseconds()),
|
||||
Winheaders: make(map[string]string),
|
||||
}
|
||||
hdr.Winheaders[hdrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes)
|
||||
hdr.Winheaders[hdrAccessTime], hdr.AccessTime = win32TimeToTar(fileInfo.LastAccessTime)
|
||||
hdr.Winheaders[hdrChangeTime], hdr.ChangeTime = win32TimeToTar(fileInfo.ChangeTime)
|
||||
hdr.Winheaders[hdrCreateTime], _ = win32TimeToTar(fileInfo.CreationTime)
|
||||
hdr.Winheaders[hdrWriteTime], hdr.ModTime = win32TimeToTar(fileInfo.LastWriteTime)
|
||||
|
||||
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||
hdr.Mode |= c_ISDIR
|
||||
@@ -252,7 +226,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size
|
||||
return nil
|
||||
}
|
||||
|
||||
// Retrieves basic Win32 file information from a tar header, using the additional metadata written by
|
||||
// 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
|
||||
@@ -260,10 +234,10 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win
|
||||
size = hdr.Size
|
||||
}
|
||||
fileInfo = &winio.FileBasicInfo{
|
||||
LastAccessTime: win32TimeFromTar(hdrAccessTime, hdr.Winheaders, hdr.AccessTime),
|
||||
LastWriteTime: win32TimeFromTar(hdrWriteTime, hdr.Winheaders, hdr.ModTime),
|
||||
ChangeTime: win32TimeFromTar(hdrChangeTime, hdr.Winheaders, hdr.ChangeTime),
|
||||
CreationTime: win32TimeFromTar(hdrCreateTime, hdr.Winheaders, hdr.ModTime),
|
||||
LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()),
|
||||
LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
|
||||
ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()),
|
||||
CreationTime: syscall.NsecToFiletime(hdr.CreationTime.UnixNano()),
|
||||
}
|
||||
if attrStr, ok := hdr.Winheaders[hdrFileAttributes]; ok {
|
||||
attr, err := strconv.ParseUint(attrStr, 10, 32)
|
||||
@@ -279,7 +253,7 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win
|
||||
return
|
||||
}
|
||||
|
||||
// Writes a Win32 backup stream from the current tar file. Since this function may process multiple
|
||||
// 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) {
|
||||
|
||||
+20
-1
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
@@ -61,5 +63,22 @@ func TestRoundTrip(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ensurePresent(t, hdr.Winheaders, "fileattr", "sd", "accesstime", "changetime", "createtime", "writetime")
|
||||
name, size, bi2, err := FileInfoFromHeader(hdr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if name != filepath.ToSlash(f.Name()) {
|
||||
t.Errorf("got name %s, expected %s", name, filepath.ToSlash(f.Name()))
|
||||
}
|
||||
|
||||
if size != fi.Size() {
|
||||
t.Errorf("got size %d, expected %d", size, fi.Size())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*bi, *bi2) {
|
||||
t.Errorf("got %#v, expected %#v", *bi, *bi2)
|
||||
}
|
||||
|
||||
ensurePresent(t, hdr.Winheaders, "fileattr", "sd")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user