1package winio
2
3import (
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
14func 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
44func 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
106func 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}
135