github.com/ccccaoqing/test@v0.0.0-20220510085219-3985d23445c0/src/archive/tar/writer_test.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package tar 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "reflect" 14 "strings" 15 "testing" 16 "testing/iotest" 17 "time" 18 ) 19 20 type writerTestEntry struct { 21 header *Header 22 contents string 23 } 24 25 type writerTest struct { 26 file string // filename of expected output 27 entries []*writerTestEntry 28 } 29 30 var writerTests = []*writerTest{ 31 // The writer test file was produced with this command: 32 // tar (GNU tar) 1.26 33 // ln -s small.txt link.txt 34 // tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt 35 { 36 file: "testdata/writer.tar", 37 entries: []*writerTestEntry{ 38 { 39 header: &Header{ 40 Name: "small.txt", 41 Mode: 0640, 42 Uid: 73025, 43 Gid: 5000, 44 Size: 5, 45 ModTime: time.Unix(1246508266, 0), 46 Typeflag: '0', 47 Uname: "dsymonds", 48 Gname: "eng", 49 }, 50 contents: "Kilts", 51 }, 52 { 53 header: &Header{ 54 Name: "small2.txt", 55 Mode: 0640, 56 Uid: 73025, 57 Gid: 5000, 58 Size: 11, 59 ModTime: time.Unix(1245217492, 0), 60 Typeflag: '0', 61 Uname: "dsymonds", 62 Gname: "eng", 63 }, 64 contents: "Google.com\n", 65 }, 66 { 67 header: &Header{ 68 Name: "link.txt", 69 Mode: 0777, 70 Uid: 1000, 71 Gid: 1000, 72 Size: 0, 73 ModTime: time.Unix(1314603082, 0), 74 Typeflag: '2', 75 Linkname: "small.txt", 76 Uname: "strings", 77 Gname: "strings", 78 }, 79 // no contents 80 }, 81 }, 82 }, 83 // The truncated test file was produced using these commands: 84 // dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt 85 // tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar 86 { 87 file: "testdata/writer-big.tar", 88 entries: []*writerTestEntry{ 89 { 90 header: &Header{ 91 Name: "tmp/16gig.txt", 92 Mode: 0640, 93 Uid: 73025, 94 Gid: 5000, 95 Size: 16 << 30, 96 ModTime: time.Unix(1254699560, 0), 97 Typeflag: '0', 98 Uname: "dsymonds", 99 Gname: "eng", 100 }, 101 // fake contents 102 contents: strings.Repeat("\x00", 4<<10), 103 }, 104 }, 105 }, 106 // The truncated test file was produced using these commands: 107 // dd if=/dev/zero bs=1048576 count=16384 > (longname/)*15 /16gig.txt 108 // tar -b 1 -c -f- (longname/)*15 /16gig.txt | dd bs=512 count=8 > writer-big-long.tar 109 { 110 file: "testdata/writer-big-long.tar", 111 entries: []*writerTestEntry{ 112 { 113 header: &Header{ 114 Name: strings.Repeat("longname/", 15) + "16gig.txt", 115 Mode: 0644, 116 Uid: 1000, 117 Gid: 1000, 118 Size: 16 << 30, 119 ModTime: time.Unix(1399583047, 0), 120 Typeflag: '0', 121 Uname: "guillaume", 122 Gname: "guillaume", 123 }, 124 // fake contents 125 contents: strings.Repeat("\x00", 4<<10), 126 }, 127 }, 128 }, 129 // This file was produced using gnu tar 1.17 130 // gnutar -b 4 --format=ustar (longname/)*15 + file.txt 131 { 132 file: "testdata/ustar.tar", 133 entries: []*writerTestEntry{ 134 { 135 header: &Header{ 136 Name: strings.Repeat("longname/", 15) + "file.txt", 137 Mode: 0644, 138 Uid: 0765, 139 Gid: 024, 140 Size: 06, 141 ModTime: time.Unix(1360135598, 0), 142 Typeflag: '0', 143 Uname: "shane", 144 Gname: "staff", 145 }, 146 contents: "hello\n", 147 }, 148 }, 149 }, 150 } 151 152 // Render byte array in a two-character hexadecimal string, spaced for easy visual inspection. 153 func bytestr(offset int, b []byte) string { 154 const rowLen = 32 155 s := fmt.Sprintf("%04x ", offset) 156 for _, ch := range b { 157 switch { 158 case '0' <= ch && ch <= '9', 'A' <= ch && ch <= 'Z', 'a' <= ch && ch <= 'z': 159 s += fmt.Sprintf(" %c", ch) 160 default: 161 s += fmt.Sprintf(" %02x", ch) 162 } 163 } 164 return s 165 } 166 167 // Render a pseudo-diff between two blocks of bytes. 168 func bytediff(a []byte, b []byte) string { 169 const rowLen = 32 170 s := fmt.Sprintf("(%d bytes vs. %d bytes)\n", len(a), len(b)) 171 for offset := 0; len(a)+len(b) > 0; offset += rowLen { 172 na, nb := rowLen, rowLen 173 if na > len(a) { 174 na = len(a) 175 } 176 if nb > len(b) { 177 nb = len(b) 178 } 179 sa := bytestr(offset, a[0:na]) 180 sb := bytestr(offset, b[0:nb]) 181 if sa != sb { 182 s += fmt.Sprintf("-%v\n+%v\n", sa, sb) 183 } 184 a = a[na:] 185 b = b[nb:] 186 } 187 return s 188 } 189 190 func TestWriter(t *testing.T) { 191 testLoop: 192 for i, test := range writerTests { 193 expected, err := ioutil.ReadFile(test.file) 194 if err != nil { 195 t.Errorf("test %d: Unexpected error: %v", i, err) 196 continue 197 } 198 199 buf := new(bytes.Buffer) 200 tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB 201 big := false 202 for j, entry := range test.entries { 203 big = big || entry.header.Size > 1<<10 204 if err := tw.WriteHeader(entry.header); err != nil { 205 t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err) 206 continue testLoop 207 } 208 if _, err := io.WriteString(tw, entry.contents); err != nil { 209 t.Errorf("test %d, entry %d: Failed writing contents: %v", i, j, err) 210 continue testLoop 211 } 212 } 213 // Only interested in Close failures for the small tests. 214 if err := tw.Close(); err != nil && !big { 215 t.Errorf("test %d: Failed closing archive: %v", i, err) 216 continue testLoop 217 } 218 219 actual := buf.Bytes() 220 if !bytes.Equal(expected, actual) { 221 t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v", 222 i, bytediff(expected, actual)) 223 } 224 if testing.Short() { // The second test is expensive. 225 break 226 } 227 } 228 } 229 230 func TestPax(t *testing.T) { 231 // Create an archive with a large name 232 fileinfo, err := os.Stat("testdata/small.txt") 233 if err != nil { 234 t.Fatal(err) 235 } 236 hdr, err := FileInfoHeader(fileinfo, "") 237 if err != nil { 238 t.Fatalf("os.Stat: %v", err) 239 } 240 // Force a PAX long name to be written 241 longName := strings.Repeat("ab", 100) 242 contents := strings.Repeat(" ", int(hdr.Size)) 243 hdr.Name = longName 244 var buf bytes.Buffer 245 writer := NewWriter(&buf) 246 if err := writer.WriteHeader(hdr); err != nil { 247 t.Fatal(err) 248 } 249 if _, err = writer.Write([]byte(contents)); err != nil { 250 t.Fatal(err) 251 } 252 if err := writer.Close(); err != nil { 253 t.Fatal(err) 254 } 255 // Simple test to make sure PAX extensions are in effect 256 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { 257 t.Fatal("Expected at least one PAX header to be written.") 258 } 259 // Test that we can get a long name back out of the archive. 260 reader := NewReader(&buf) 261 hdr, err = reader.Next() 262 if err != nil { 263 t.Fatal(err) 264 } 265 if hdr.Name != longName { 266 t.Fatal("Couldn't recover long file name") 267 } 268 } 269 270 func TestPaxSymlink(t *testing.T) { 271 // Create an archive with a large linkname 272 fileinfo, err := os.Stat("testdata/small.txt") 273 if err != nil { 274 t.Fatal(err) 275 } 276 hdr, err := FileInfoHeader(fileinfo, "") 277 hdr.Typeflag = TypeSymlink 278 if err != nil { 279 t.Fatalf("os.Stat:1 %v", err) 280 } 281 // Force a PAX long linkname to be written 282 longLinkname := strings.Repeat("1234567890/1234567890", 10) 283 hdr.Linkname = longLinkname 284 285 hdr.Size = 0 286 var buf bytes.Buffer 287 writer := NewWriter(&buf) 288 if err := writer.WriteHeader(hdr); err != nil { 289 t.Fatal(err) 290 } 291 if err := writer.Close(); err != nil { 292 t.Fatal(err) 293 } 294 // Simple test to make sure PAX extensions are in effect 295 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { 296 t.Fatal("Expected at least one PAX header to be written.") 297 } 298 // Test that we can get a long name back out of the archive. 299 reader := NewReader(&buf) 300 hdr, err = reader.Next() 301 if err != nil { 302 t.Fatal(err) 303 } 304 if hdr.Linkname != longLinkname { 305 t.Fatal("Couldn't recover long link name") 306 } 307 } 308 309 func TestPaxNonAscii(t *testing.T) { 310 // Create an archive with non ascii. These should trigger a pax header 311 // because pax headers have a defined utf-8 encoding. 312 fileinfo, err := os.Stat("testdata/small.txt") 313 if err != nil { 314 t.Fatal(err) 315 } 316 317 hdr, err := FileInfoHeader(fileinfo, "") 318 if err != nil { 319 t.Fatalf("os.Stat:1 %v", err) 320 } 321 322 // some sample data 323 chineseFilename := "文件名" 324 chineseGroupname := "組" 325 chineseUsername := "用戶名" 326 327 hdr.Name = chineseFilename 328 hdr.Gname = chineseGroupname 329 hdr.Uname = chineseUsername 330 331 contents := strings.Repeat(" ", int(hdr.Size)) 332 333 var buf bytes.Buffer 334 writer := NewWriter(&buf) 335 if err := writer.WriteHeader(hdr); err != nil { 336 t.Fatal(err) 337 } 338 if _, err = writer.Write([]byte(contents)); err != nil { 339 t.Fatal(err) 340 } 341 if err := writer.Close(); err != nil { 342 t.Fatal(err) 343 } 344 // Simple test to make sure PAX extensions are in effect 345 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { 346 t.Fatal("Expected at least one PAX header to be written.") 347 } 348 // Test that we can get a long name back out of the archive. 349 reader := NewReader(&buf) 350 hdr, err = reader.Next() 351 if err != nil { 352 t.Fatal(err) 353 } 354 if hdr.Name != chineseFilename { 355 t.Fatal("Couldn't recover unicode name") 356 } 357 if hdr.Gname != chineseGroupname { 358 t.Fatal("Couldn't recover unicode group") 359 } 360 if hdr.Uname != chineseUsername { 361 t.Fatal("Couldn't recover unicode user") 362 } 363 } 364 365 func TestPaxXattrs(t *testing.T) { 366 xattrs := map[string]string{ 367 "user.key": "value", 368 } 369 370 // Create an archive with an xattr 371 fileinfo, err := os.Stat("testdata/small.txt") 372 if err != nil { 373 t.Fatal(err) 374 } 375 hdr, err := FileInfoHeader(fileinfo, "") 376 if err != nil { 377 t.Fatalf("os.Stat: %v", err) 378 } 379 contents := "Kilts" 380 hdr.Xattrs = xattrs 381 var buf bytes.Buffer 382 writer := NewWriter(&buf) 383 if err := writer.WriteHeader(hdr); err != nil { 384 t.Fatal(err) 385 } 386 if _, err = writer.Write([]byte(contents)); err != nil { 387 t.Fatal(err) 388 } 389 if err := writer.Close(); err != nil { 390 t.Fatal(err) 391 } 392 // Test that we can get the xattrs back out of the archive. 393 reader := NewReader(&buf) 394 hdr, err = reader.Next() 395 if err != nil { 396 t.Fatal(err) 397 } 398 if !reflect.DeepEqual(hdr.Xattrs, xattrs) { 399 t.Fatalf("xattrs did not survive round trip: got %+v, want %+v", 400 hdr.Xattrs, xattrs) 401 } 402 } 403 404 func TestPAXHeader(t *testing.T) { 405 medName := strings.Repeat("CD", 50) 406 longName := strings.Repeat("AB", 100) 407 paxTests := [][2]string{ 408 {paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"}, 409 {"a=b", "6 a=b\n"}, // Single digit length 410 {"a=names", "11 a=names\n"}, // Test case involving carries 411 {paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)}, 412 {paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}} 413 414 for _, test := range paxTests { 415 key, expected := test[0], test[1] 416 if result := paxHeader(key); result != expected { 417 t.Fatalf("paxHeader: got %s, expected %s", result, expected) 418 } 419 } 420 } 421 422 func TestUSTARLongName(t *testing.T) { 423 // Create an archive with a path that failed to split with USTAR extension in previous versions. 424 fileinfo, err := os.Stat("testdata/small.txt") 425 if err != nil { 426 t.Fatal(err) 427 } 428 hdr, err := FileInfoHeader(fileinfo, "") 429 hdr.Typeflag = TypeDir 430 if err != nil { 431 t.Fatalf("os.Stat:1 %v", err) 432 } 433 // Force a PAX long name to be written. The name was taken from a practical example 434 // that fails and replaced ever char through numbers to anonymize the sample. 435 longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/" 436 hdr.Name = longName 437 438 hdr.Size = 0 439 var buf bytes.Buffer 440 writer := NewWriter(&buf) 441 if err := writer.WriteHeader(hdr); err != nil { 442 t.Fatal(err) 443 } 444 if err := writer.Close(); err != nil { 445 t.Fatal(err) 446 } 447 // Test that we can get a long name back out of the archive. 448 reader := NewReader(&buf) 449 hdr, err = reader.Next() 450 if err != nil { 451 t.Fatal(err) 452 } 453 if hdr.Name != longName { 454 t.Fatal("Couldn't recover long name") 455 } 456 } 457 458 func TestValidTypeflagWithPAXHeader(t *testing.T) { 459 var buffer bytes.Buffer 460 tw := NewWriter(&buffer) 461 462 fileName := strings.Repeat("ab", 100) 463 464 hdr := &Header{ 465 Name: fileName, 466 Size: 4, 467 Typeflag: 0, 468 } 469 if err := tw.WriteHeader(hdr); err != nil { 470 t.Fatalf("Failed to write header: %s", err) 471 } 472 if _, err := tw.Write([]byte("fooo")); err != nil { 473 t.Fatalf("Failed to write the file's data: %s", err) 474 } 475 tw.Close() 476 477 tr := NewReader(&buffer) 478 479 for { 480 header, err := tr.Next() 481 if err == io.EOF { 482 break 483 } 484 if err != nil { 485 t.Fatalf("Failed to read header: %s", err) 486 } 487 if header.Typeflag != 0 { 488 t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag) 489 } 490 } 491 }