1
2
3
4 package backuptar
5
6 import (
7 "archive/tar"
8 "bytes"
9 "io"
10 "os"
11 "path/filepath"
12 "reflect"
13 "testing"
14
15 "github.com/Microsoft/go-winio"
16 "golang.org/x/sys/windows"
17 )
18
19 func ensurePresent(t *testing.T, m map[string]string, keys ...string) {
20 for _, k := range keys {
21 if _, ok := m[k]; !ok {
22 t.Error(k, "not present in tar header")
23 }
24 }
25 }
26
27 func setSparse(t *testing.T, f *os.File) {
28 if err := windows.DeviceIoControl(windows.Handle(f.Fd()), windows.FSCTL_SET_SPARSE, nil, 0, nil, 0, nil, nil); err != nil {
29 t.Fatal(err)
30 }
31 }
32
33
34 func compareReaders(t *testing.T, rActual io.Reader, rExpected io.Reader) {
35 const size = 8 * 1024
36 var bufExpected, bufActual [size]byte
37 var readCount int64
38
39
40
41 for {
42
43 nExpected, err := rExpected.Read(bufExpected[:])
44 if err == io.EOF && nExpected == 0 {
45 break
46 } else if err != nil && err != io.EOF {
47 t.Fatalf("Failed reading from rExpected at %d: %s", readCount, err)
48 }
49
50 if nActual, err := io.ReadFull(rActual, bufActual[:nExpected]); err != nil {
51 t.Fatalf("Only read %d bytes out of %d from rActual at %d: %s", nActual, nExpected, readCount, err)
52 }
53 readCount += int64(nExpected)
54 for i, bExpected := range bufExpected[:nExpected] {
55 if bExpected != bufActual[i] {
56 t.Fatalf("Mismatched bytes at %d. got 0x%x, expected 0x%x", i, bufActual[i], bExpected)
57 }
58 }
59 }
60
61 var b [1]byte
62 if n, err := rActual.Read(b[:]); n != 0 || err != io.EOF {
63 t.Fatalf("rActual didn't return EOF at expected end. Read %d bytes with error %s", n, err)
64 }
65 }
66
67 func TestRoundTrip(t *testing.T) {
68
69
70
71
72 for name, setup := range map[string]func(*testing.T) string{
73 "normalFile": func(t *testing.T) string {
74 path := filepath.Join(t.TempDir(), "foo.txt")
75 if err := os.WriteFile(path, []byte("testing 1 2 3\n"), 0644); err != nil {
76 t.Fatal(err)
77 }
78 return path
79 },
80 "normalFileEmpty": func(t *testing.T) string {
81 path := filepath.Join(t.TempDir(), "foo.txt")
82 f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
83 if err != nil {
84 t.Fatal(err)
85 }
86 defer f.Close()
87 return path
88 },
89 "sparseFileEmpty": func(t *testing.T) string {
90 path := filepath.Join(t.TempDir(), "foo.txt")
91 f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
92 if err != nil {
93 t.Fatal(err)
94 }
95 defer f.Close()
96 setSparse(t, f)
97 return path
98 },
99 "sparseFileWithNoAllocatedRanges": func(t *testing.T) string {
100 path := filepath.Join(t.TempDir(), "foo.txt")
101 f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
102 if err != nil {
103 t.Fatal(err)
104 }
105 defer f.Close()
106 setSparse(t, f)
107
108
109 if err := f.Truncate(1000000); err != nil {
110 t.Fatal(err)
111 }
112 return path
113 },
114 "sparseFileWithOneAllocatedRange": func(t *testing.T) string {
115 path := filepath.Join(t.TempDir(), "foo.txt")
116 f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
117 if err != nil {
118 t.Fatal(err)
119 }
120 defer f.Close()
121 setSparse(t, f)
122 if _, err := f.WriteString("test sparse data"); err != nil {
123 t.Fatal(err)
124 }
125 return path
126 },
127 "sparseFileWithMultipleAllocatedRanges": func(t *testing.T) string {
128 path := filepath.Join(t.TempDir(), "foo.txt")
129 f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
130 if err != nil {
131 t.Fatal(err)
132 }
133 defer f.Close()
134 setSparse(t, f)
135 if _, err = f.Write([]byte("testing 1 2 3\n")); err != nil {
136 t.Fatal(err)
137 }
138
139
140 if _, err = f.Seek(1000000, 0); err != nil {
141 t.Fatal(err)
142 }
143 if _, err = f.Write([]byte("more data later\n")); err != nil {
144 t.Fatal(err)
145 }
146 return path
147 },
148 } {
149 t.Run(name, func(t *testing.T) {
150 path := setup(t)
151 f, err := os.Open(path)
152 if err != nil {
153 t.Fatal(err)
154 }
155 defer f.Close()
156
157 fi, err := f.Stat()
158 if err != nil {
159 t.Fatal(err)
160 }
161 bi, err := winio.GetFileBasicInfo(f)
162 if err != nil {
163 t.Fatal(err)
164 }
165
166 br := winio.NewBackupFileReader(f, true)
167 defer br.Close()
168 var buf bytes.Buffer
169 tw := tar.NewWriter(&buf)
170 err = WriteTarFileFromBackupStream(tw, br, f.Name(), fi.Size(), bi)
171 if err != nil {
172 t.Fatal(err)
173 }
174 tr := tar.NewReader(&buf)
175 hdr, err := tr.Next()
176 if err != nil {
177 t.Fatal(err)
178 }
179
180 name, size, bi2, err := FileInfoFromHeader(hdr)
181 if err != nil {
182 t.Fatal(err)
183 }
184 if name != filepath.ToSlash(f.Name()) {
185 t.Errorf("got name %s, expected %s", name, filepath.ToSlash(f.Name()))
186 }
187 if size != fi.Size() {
188 t.Errorf("got size %d, expected %d", size, fi.Size())
189 }
190 if !reflect.DeepEqual(*bi2, *bi) {
191 t.Errorf("got %#v, expected %#v", *bi2, *bi)
192 }
193 ensurePresent(t, hdr.PAXRecords, "MSWINDOWS.fileattr", "MSWINDOWS.rawsd")
194
195
196 if _, err := f.Seek(0, 0); err != nil {
197 t.Fatal(err)
198 }
199 compareReaders(t, tr, f)
200 })
201 }
202 }
203
204 func TestZeroReader(t *testing.T) {
205 const size = 512
206 var b [size]byte
207 var bExpected [size]byte
208 var r zeroReader
209 n, err := r.Read(b[:])
210 if err != nil {
211 t.Fatalf("Unexpected read error: %s", err)
212 }
213 if n != size {
214 t.Errorf("Wrong read size. got %d, expected %d", n, size)
215 }
216 for i := range b {
217 if b[i] != bExpected[i] {
218 t.Errorf("Wrong content at index %d. got %d, expected %d", i, b[i], bExpected[i])
219 }
220 }
221 }
222
View as plain text