mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-09-05 16:47:31 -04:00
Implement winio.GetFileStandardInfo
Signed-off-by: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
This commit is contained in:
+19
@@ -36,6 +36,25 @@ func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
|||||||
return nil
|
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
|
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||||
// unique on a system.
|
// unique on a system.
|
||||||
type FileIDInfo struct {
|
type FileIDInfo struct {
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Checks if current matches expected. Note that AllocationSize is filesystem-specific,
|
||||||
|
// 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) {
|
||||||
|
if current.AllocationSize < expected.AllocationSize {
|
||||||
|
t.Fatalf("FileStandardInfo unexpectedly had AllocationSize %d, expecting >=%d", current.AllocationSize, expected.AllocationSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.EndOfFile != expected.EndOfFile {
|
||||||
|
t.Fatalf("FileStandardInfo unexpectedly had EndOfFile %d, expecting %d", current.EndOfFile, expected.EndOfFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.NumberOfLinks != expected.NumberOfLinks {
|
||||||
|
t.Fatalf("FileStandardInfo unexpectedly had NumberOfLinks %d, expecting %d", current.NumberOfLinks, expected.NumberOfLinks)
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.DeletePending != expected.DeletePending {
|
||||||
|
if current.DeletePending {
|
||||||
|
t.Fatalf("FileStandardInfo unexpectedly DeletePending")
|
||||||
|
} else {
|
||||||
|
t.Fatalf("FileStandardInfo unexpectedly not DeletePending")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.Directory != expected.Directory {
|
||||||
|
if current.Directory {
|
||||||
|
t.Fatalf("FileStandardInfo unexpectedly Directory")
|
||||||
|
} else {
|
||||||
|
t.Fatalf("FileStandardInfo unexpectedly not Directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetFileStandardInfo_File(t *testing.T) {
|
||||||
|
f, err := ioutil.TempFile("", "tst")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
|
||||||
|
expectedFileInfo := &FileStandardInfo{
|
||||||
|
AllocationSize: 0,
|
||||||
|
EndOfFile: 0,
|
||||||
|
NumberOfLinks: 1,
|
||||||
|
DeletePending: false,
|
||||||
|
Directory: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := GetFileStandardInfo(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkFileStandardInfo(t, info, expectedFileInfo)
|
||||||
|
|
||||||
|
bytesWritten, err := f.Write([]byte("0123456789"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedFileInfo.EndOfFile = int64(bytesWritten)
|
||||||
|
expectedFileInfo.AllocationSize = int64(bytesWritten)
|
||||||
|
|
||||||
|
info, err = GetFileStandardInfo(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkFileStandardInfo(t, info, expectedFileInfo)
|
||||||
|
|
||||||
|
linkName := f.Name() + ".link"
|
||||||
|
|
||||||
|
if err = os.Link(f.Name(), linkName); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(linkName)
|
||||||
|
|
||||||
|
expectedFileInfo.NumberOfLinks = 2
|
||||||
|
|
||||||
|
info, err = GetFileStandardInfo(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkFileStandardInfo(t, info, expectedFileInfo)
|
||||||
|
|
||||||
|
os.Remove(linkName)
|
||||||
|
|
||||||
|
expectedFileInfo.NumberOfLinks = 1
|
||||||
|
|
||||||
|
info, err = GetFileStandardInfo(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkFileStandardInfo(t, info, expectedFileInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetFileStandardInfo_Directory(t *testing.T) {
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
expectedFileInfo := &FileStandardInfo{
|
||||||
|
AllocationSize: 0,
|
||||||
|
EndOfFile: 0,
|
||||||
|
NumberOfLinks: 1,
|
||||||
|
DeletePending: false,
|
||||||
|
Directory: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := GetFileStandardInfo(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkFileStandardInfo(t, info, expectedFileInfo)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user