From b80e202b5df7fe8b278bab57e26eefc8518be935 Mon Sep 17 00:00:00 2001 From: Kathryn Baldauf Date: Thu, 22 Apr 2021 10:33:11 -0700 Subject: [PATCH] Revert "[Temporary] Revert Implement winio.GetFileStandardInfo FileInfo commits " (cherry picked from commit 8f0d50b3b381b741820d6737cfbda5b9284be616) Signed-off-by: Kirtana Ashok --- backuptar/tar.go | 11 ++++---- fileinfo.go | 51 +++++++++++++++++------------------- fileinfo_test.go | 64 ++++++--------------------------------------- zsyscall_windows.go | 18 ------------- 4 files changed, 38 insertions(+), 106 deletions(-) diff --git a/backuptar/tar.go b/backuptar/tar.go index b32bee3c..689e4da6 100644 --- a/backuptar/tar.go +++ b/backuptar/tar.go @@ -15,6 +15,7 @@ import ( "time" "github.com/Microsoft/go-winio" + "golang.org/x/sys/windows" ) const ( @@ -325,11 +326,11 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win size = hdr.Size } fileInfo = &winio.FileBasicInfo{ - LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()), - LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()), - ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()), + LastAccessTime: windows.NsecToFiletime(hdr.AccessTime.UnixNano()), + LastWriteTime: windows.NsecToFiletime(hdr.ModTime.UnixNano()), + ChangeTime: windows.NsecToFiletime(hdr.ChangeTime.UnixNano()), // Default to ModTime, we'll pull hdrCreationTime below if present - CreationTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()), + CreationTime: windows.NsecToFiletime(hdr.ModTime.UnixNano()), } if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok { attr, err := strconv.ParseUint(attrStr, 10, 32) @@ -347,7 +348,7 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win if err != nil { return "", 0, nil, err } - fileInfo.CreationTime = syscall.NsecToFiletime(creationTime.UnixNano()) + fileInfo.CreationTime = windows.NsecToFiletime(creationTime.UnixNano()) } return } diff --git a/fileinfo.go b/fileinfo.go index 56ab7d8c..4624bddb 100644 --- a/fileinfo.go +++ b/fileinfo.go @@ -6,23 +6,14 @@ package winio import ( "os" "runtime" - "syscall" "unsafe" "golang.org/x/sys/windows" ) -//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx -//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle - -const ( - fileBasicInfo = 0 - fileIDInfo = 0x12 -) - // FileBasicInfo contains file access time and file attributes information. type FileBasicInfo struct { - CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime + CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime FileAttributes uint32 pad uint32 // padding } @@ -38,13 +29,8 @@ type alignedFileBasicInfo struct { // GetFileBasicInfo retrieves times and attributes for a file. func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { - bi := &alignedFileBasicInfo{} - if err := windows.GetFileInformationByHandleEx( - windows.Handle(f.Fd()), - windows.FileBasicInfo, - (*byte)(unsafe.Pointer(bi)), - uint32(unsafe.Sizeof(*bi)), - ); err != nil { + bi := &FileBasicInfo{} + if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) @@ -55,21 +41,32 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { // SetFileBasicInfo sets times and attributes for a file. func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { - // Create an alignedFileBasicInfo based on a FileBasicInfo. The copy is - // suitable to pass to GetFileInformationByHandleEx. - biAligned := *(*alignedFileBasicInfo)(unsafe.Pointer(bi)) - if err := windows.SetFileInformationByHandle( - windows.Handle(f.Fd()), - windows.FileBasicInfo, - (*byte)(unsafe.Pointer(&biAligned)), - uint32(unsafe.Sizeof(biAligned)), - ); err != nil { + if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} } runtime.KeepAlive(f) return nil } +// FileStandardInfo contains extended information for the file. +// FILE_STANDARD_INFO in WinBase.h +// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info +type FileStandardInfo struct { + AllocationSize, EndOfFile int64 + NumberOfLinks uint32 + DeletePending, Directory bool +} + +// GetFileStandardInfo retrieves ended information for the file. +func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) { + si := &FileStandardInfo{} + if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil { + return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} + } + runtime.KeepAlive(f) + return si, nil +} + // FileIDInfo contains the volume serial number and file ID for a file. This pair should be // unique on a system. type FileIDInfo struct { @@ -80,7 +77,7 @@ type FileIDInfo struct { // GetFileID retrieves the unique (volume, file ID) pair for a file. func GetFileID(f *os.File) (*FileIDInfo, error) { fileID := &FileIDInfo{} - if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { + if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) diff --git a/fileinfo_test.go b/fileinfo_test.go index 28b8f84a..8c97f506 100644 --- a/fileinfo_test.go +++ b/fileinfo_test.go @@ -4,9 +4,9 @@ package winio import ( + "io/ioutil" "os" "testing" - "unsafe" "golang.org/x/sys/windows" ) @@ -15,8 +15,6 @@ import ( // so we check that the current.AllocationSize is >= expected.AllocationSize. // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/5afa7f66-619c-48f3-955f-68c4ece704ae func checkFileStandardInfo(t *testing.T, current, expected *FileStandardInfo) { - t.Helper() - if current.AllocationSize < expected.AllocationSize { t.Fatalf("FileStandardInfo unexpectedly had AllocationSize %d, expecting >=%d", current.AllocationSize, expected.AllocationSize) } @@ -47,7 +45,7 @@ func checkFileStandardInfo(t *testing.T, current, expected *FileStandardInfo) { } func TestGetFileStandardInfo_File(t *testing.T) { - f, err := os.CreateTemp("", "tst") + f, err := ioutil.TempFile("", "tst") if err != nil { t.Fatal(err) } @@ -109,7 +107,12 @@ func TestGetFileStandardInfo_File(t *testing.T) { } func TestGetFileStandardInfo_Directory(t *testing.T) { - tempDir := t.TempDir() + tempDir, err := ioutil.TempDir("", "tst") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + // os.Open returns the Search Handle, not the Directory Handle // See https://github.com/golang/go/issues/13738 f, err := OpenForBackup(tempDir, windows.GENERIC_READ, 0, windows.OPEN_EXISTING) @@ -132,54 +135,3 @@ func TestGetFileStandardInfo_Directory(t *testing.T) { } checkFileStandardInfo(t, info, expectedFileInfo) } - -// TestFileInfoStructAlignment checks that the alignment of Go fileinfo structs -// match what is expected by the Windows API. -func TestFileInfoStructAlignment(t *testing.T) { - //nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. - const ( - // The alignment of various types, as named in the Windows APIs. When - // deciding on an expectedAlignment for a struct's test case, use the - // type of the largest field in the struct as written in the Windows - // docs. This is intended to help reviewers by allowing them to first - // check that a new align* const is correct, then independently check - // that the test case is correct, rather than all at once. - alignLARGE_INTEGER = unsafe.Alignof(uint64(0)) - alignULONGLONG = unsafe.Alignof(uint64(0)) - ) - tests := []struct { - name string - actualAlign uintptr - actualSize uintptr - expectedAlignment uintptr - }{ - { - // alignedFileBasicInfo is passed to the Windows API rather than FileBasicInfo. - "alignedFileBasicInfo", unsafe.Alignof(alignedFileBasicInfo{}), unsafe.Sizeof(alignedFileBasicInfo{}), - // https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_basic_info - alignLARGE_INTEGER, - }, - { - "FileStandardInfo", unsafe.Alignof(FileStandardInfo{}), unsafe.Sizeof(FileStandardInfo{}), - // https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info - alignLARGE_INTEGER, - }, - { - "FileIDInfo", unsafe.Alignof(FileIDInfo{}), unsafe.Sizeof(FileIDInfo{}), - // https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_info - alignULONGLONG, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.actualAlign != tt.expectedAlignment { - t.Errorf("alignment mismatch: actual %d, expected %d", tt.actualAlign, tt.expectedAlignment) - } - if r := tt.actualSize % tt.expectedAlignment; r != 0 { - t.Errorf( - "size is not a multiple of alignment: size %% alignment (%d %% %d) is %d, expected 0", - tt.actualSize, tt.expectedAlignment, r) - } - }) - } -} diff --git a/zsyscall_windows.go b/zsyscall_windows.go index 4579c745..176ff75e 100644 --- a/zsyscall_windows.go +++ b/zsyscall_windows.go @@ -63,14 +63,12 @@ var ( procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") - procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") procLocalAlloc = modkernel32.NewProc("LocalAlloc") procLocalFree = modkernel32.NewProc("LocalFree") procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") - procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile") procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl") procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U") @@ -339,14 +337,6 @@ func getCurrentThread() (h syscall.Handle) { return } -func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} - func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) if r1 == 0 { @@ -390,14 +380,6 @@ func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err erro return } -func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} - func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) { r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0) status = ntstatus(r0)