Skip to content

Commit 58dba89

Browse files
author
Kathryn Baldauf
authored
Merge pull request #209 from microsoft/revert-204-revert_fileinfo_break
Revert removal of "Implement winio.GetFileStandardInfo FileInfo" commits
2 parents 5c2e05d + 8f0d50b commit 58dba89

File tree

4 files changed

+164
-35
lines changed

4 files changed

+164
-35
lines changed

backuptar/tar.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"time"
1717

1818
"github.com/Microsoft/go-winio"
19+
"golang.org/x/sys/windows"
1920
)
2021

2122
const (
@@ -297,11 +298,11 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win
297298
size = hdr.Size
298299
}
299300
fileInfo = &winio.FileBasicInfo{
300-
LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()),
301-
LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
302-
ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()),
301+
LastAccessTime: windows.NsecToFiletime(hdr.AccessTime.UnixNano()),
302+
LastWriteTime: windows.NsecToFiletime(hdr.ModTime.UnixNano()),
303+
ChangeTime: windows.NsecToFiletime(hdr.ChangeTime.UnixNano()),
303304
// Default to ModTime, we'll pull hdrCreationTime below if present
304-
CreationTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()),
305+
CreationTime: windows.NsecToFiletime(hdr.ModTime.UnixNano()),
305306
}
306307
if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok {
307308
attr, err := strconv.ParseUint(attrStr, 10, 32)
@@ -319,7 +320,7 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win
319320
if err != nil {
320321
return "", 0, nil, err
321322
}
322-
fileInfo.CreationTime = syscall.NsecToFiletime(creationTime.UnixNano())
323+
fileInfo.CreationTime = windows.NsecToFiletime(creationTime.UnixNano())
323324
}
324325
return
325326
}

fileinfo.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,22 @@ package winio
55
import (
66
"os"
77
"runtime"
8-
"syscall"
98
"unsafe"
10-
)
11-
12-
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
13-
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
149

15-
const (
16-
fileBasicInfo = 0
17-
fileIDInfo = 0x12
10+
"golang.org/x/sys/windows"
1811
)
1912

2013
// FileBasicInfo contains file access time and file attributes information.
2114
type FileBasicInfo struct {
22-
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
15+
CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
2316
FileAttributes uint32
2417
pad uint32 // padding
2518
}
2619

2720
// GetFileBasicInfo retrieves times and attributes for a file.
2821
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
2922
bi := &FileBasicInfo{}
30-
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
23+
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
3124
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
3225
}
3326
runtime.KeepAlive(f)
@@ -36,13 +29,32 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
3629

3730
// SetFileBasicInfo sets times and attributes for a file.
3831
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
39-
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
32+
if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
4033
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
4134
}
4235
runtime.KeepAlive(f)
4336
return nil
4437
}
4538

39+
// FileStandardInfo contains extended information for the file.
40+
// FILE_STANDARD_INFO in WinBase.h
41+
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
42+
type FileStandardInfo struct {
43+
AllocationSize, EndOfFile int64
44+
NumberOfLinks uint32
45+
DeletePending, Directory bool
46+
}
47+
48+
// GetFileStandardInfo retrieves ended information for the file.
49+
func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
50+
si := &FileStandardInfo{}
51+
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil {
52+
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
53+
}
54+
runtime.KeepAlive(f)
55+
return si, nil
56+
}
57+
4658
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
4759
// unique on a system.
4860
type FileIDInfo struct {
@@ -53,7 +65,7 @@ type FileIDInfo struct {
5365
// GetFileID retrieves the unique (volume, file ID) pair for a file.
5466
func GetFileID(f *os.File) (*FileIDInfo, error) {
5567
fileID := &FileIDInfo{}
56-
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
68+
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
5769
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
5870
}
5971
runtime.KeepAlive(f)

fileinfo_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package winio
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"testing"
7+
8+
"golang.org/x/sys/windows"
9+
)
10+
11+
// Checks if current matches expected. Note that AllocationSize is filesystem-specific,
12+
// so we check that the current.AllocationSize is >= expected.AllocationSize.
13+
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/5afa7f66-619c-48f3-955f-68c4ece704ae
14+
func checkFileStandardInfo(t *testing.T, current, expected *FileStandardInfo) {
15+
if current.AllocationSize < expected.AllocationSize {
16+
t.Fatalf("FileStandardInfo unexpectedly had AllocationSize %d, expecting >=%d", current.AllocationSize, expected.AllocationSize)
17+
}
18+
19+
if current.EndOfFile != expected.EndOfFile {
20+
t.Fatalf("FileStandardInfo unexpectedly had EndOfFile %d, expecting %d", current.EndOfFile, expected.EndOfFile)
21+
}
22+
23+
if current.NumberOfLinks != expected.NumberOfLinks {
24+
t.Fatalf("FileStandardInfo unexpectedly had NumberOfLinks %d, expecting %d", current.NumberOfLinks, expected.NumberOfLinks)
25+
}
26+
27+
if current.DeletePending != expected.DeletePending {
28+
if current.DeletePending {
29+
t.Fatalf("FileStandardInfo unexpectedly DeletePending")
30+
} else {
31+
t.Fatalf("FileStandardInfo unexpectedly not DeletePending")
32+
}
33+
}
34+
35+
if current.Directory != expected.Directory {
36+
if current.Directory {
37+
t.Fatalf("FileStandardInfo unexpectedly Directory")
38+
} else {
39+
t.Fatalf("FileStandardInfo unexpectedly not Directory")
40+
}
41+
}
42+
}
43+
44+
func TestGetFileStandardInfo_File(t *testing.T) {
45+
f, err := ioutil.TempFile("", "tst")
46+
if err != nil {
47+
t.Fatal(err)
48+
}
49+
defer f.Close()
50+
defer os.Remove(f.Name())
51+
52+
expectedFileInfo := &FileStandardInfo{
53+
AllocationSize: 0,
54+
EndOfFile: 0,
55+
NumberOfLinks: 1,
56+
DeletePending: false,
57+
Directory: false,
58+
}
59+
60+
info, err := GetFileStandardInfo(f)
61+
if err != nil {
62+
t.Fatal(err)
63+
}
64+
checkFileStandardInfo(t, info, expectedFileInfo)
65+
66+
bytesWritten, err := f.Write([]byte("0123456789"))
67+
if err != nil {
68+
t.Fatal(err)
69+
}
70+
71+
expectedFileInfo.EndOfFile = int64(bytesWritten)
72+
expectedFileInfo.AllocationSize = int64(bytesWritten)
73+
74+
info, err = GetFileStandardInfo(f)
75+
if err != nil {
76+
t.Fatal(err)
77+
}
78+
checkFileStandardInfo(t, info, expectedFileInfo)
79+
80+
linkName := f.Name() + ".link"
81+
82+
if err = os.Link(f.Name(), linkName); err != nil {
83+
t.Fatal(err)
84+
}
85+
defer os.Remove(linkName)
86+
87+
expectedFileInfo.NumberOfLinks = 2
88+
89+
info, err = GetFileStandardInfo(f)
90+
if err != nil {
91+
t.Fatal(err)
92+
}
93+
checkFileStandardInfo(t, info, expectedFileInfo)
94+
95+
os.Remove(linkName)
96+
97+
expectedFileInfo.NumberOfLinks = 1
98+
99+
info, err = GetFileStandardInfo(f)
100+
if err != nil {
101+
t.Fatal(err)
102+
}
103+
checkFileStandardInfo(t, info, expectedFileInfo)
104+
}
105+
106+
func TestGetFileStandardInfo_Directory(t *testing.T) {
107+
tempDir, err := ioutil.TempDir("", "tst")
108+
if err != nil {
109+
t.Fatal(err)
110+
}
111+
defer os.RemoveAll(tempDir)
112+
113+
// os.Open returns the Search Handle, not the Directory Handle
114+
// See https://github.com/golang/go/issues/13738
115+
f, err := OpenForBackup(tempDir, windows.GENERIC_READ, 0, windows.OPEN_EXISTING)
116+
if err != nil {
117+
t.Fatal(err)
118+
}
119+
defer f.Close()
120+
121+
expectedFileInfo := &FileStandardInfo{
122+
AllocationSize: 0,
123+
EndOfFile: 0,
124+
NumberOfLinks: 1,
125+
DeletePending: false,
126+
Directory: true,
127+
}
128+
129+
info, err := GetFileStandardInfo(f)
130+
if err != nil {
131+
t.Fatal(err)
132+
}
133+
checkFileStandardInfo(t, info, expectedFileInfo)
134+
}

zsyscall_windows.go

Lines changed: 0 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)