github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/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 "encoding/hex" 10 "io" 11 "io/ioutil" 12 "os" 13 "path" 14 "reflect" 15 "sort" 16 "strings" 17 "testing" 18 "testing/iotest" 19 "time" 20 ) 21 22 func bytediff(a, b []byte) string { 23 const ( 24 uniqueA = "- " 25 uniqueB = "+ " 26 identity = " " 27 ) 28 var ss []string 29 sa := strings.Split(strings.TrimSpace(hex.Dump(a)), "\n") 30 sb := strings.Split(strings.TrimSpace(hex.Dump(b)), "\n") 31 for len(sa) > 0 && len(sb) > 0 { 32 if sa[0] == sb[0] { 33 ss = append(ss, identity+sa[0]) 34 } else { 35 ss = append(ss, uniqueA+sa[0]) 36 ss = append(ss, uniqueB+sb[0]) 37 } 38 sa, sb = sa[1:], sb[1:] 39 } 40 for len(sa) > 0 { 41 ss = append(ss, uniqueA+sa[0]) 42 sa = sa[1:] 43 } 44 for len(sb) > 0 { 45 ss = append(ss, uniqueB+sb[0]) 46 sb = sb[1:] 47 } 48 return strings.Join(ss, "\n") 49 } 50 51 func TestWriter(t *testing.T) { 52 type entry struct { 53 header *Header 54 contents string 55 } 56 57 vectors := []struct { 58 file string // filename of expected output 59 entries []*entry 60 err error // expected error on WriteHeader 61 }{{ 62 // The writer test file was produced with this command: 63 // tar (GNU tar) 1.26 64 // ln -s small.txt link.txt 65 // tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt 66 file: "testdata/writer.tar", 67 entries: []*entry{{ 68 header: &Header{ 69 Name: "small.txt", 70 Mode: 0640, 71 Uid: 73025, 72 Gid: 5000, 73 Size: 5, 74 ModTime: time.Unix(1246508266, 0), 75 Typeflag: '0', 76 Uname: "dsymonds", 77 Gname: "eng", 78 }, 79 contents: "Kilts", 80 }, { 81 header: &Header{ 82 Name: "small2.txt", 83 Mode: 0640, 84 Uid: 73025, 85 Gid: 5000, 86 Size: 11, 87 ModTime: time.Unix(1245217492, 0), 88 Typeflag: '0', 89 Uname: "dsymonds", 90 Gname: "eng", 91 }, 92 contents: "Google.com\n", 93 }, { 94 header: &Header{ 95 Name: "link.txt", 96 Mode: 0777, 97 Uid: 1000, 98 Gid: 1000, 99 Size: 0, 100 ModTime: time.Unix(1314603082, 0), 101 Typeflag: '2', 102 Linkname: "small.txt", 103 Uname: "strings", 104 Gname: "strings", 105 }, 106 // no contents 107 }}, 108 }, { 109 // The truncated test file was produced using these commands: 110 // dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt 111 // tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar 112 file: "testdata/writer-big.tar", 113 entries: []*entry{{ 114 header: &Header{ 115 Name: "tmp/16gig.txt", 116 Mode: 0640, 117 Uid: 73025, 118 Gid: 5000, 119 Size: 16 << 30, 120 ModTime: time.Unix(1254699560, 0), 121 Typeflag: '0', 122 Uname: "dsymonds", 123 Gname: "eng", 124 Devminor: -1, // Force use of GNU format 125 }, 126 // fake contents 127 contents: strings.Repeat("\x00", 4<<10), 128 }}, 129 }, { 130 // This truncated file was produced using this library. 131 // It was verified to work with GNU tar 1.27.1 and BSD tar 3.1.2. 132 // dd if=/dev/zero bs=1G count=16 >> writer-big-long.tar 133 // gnutar -xvf writer-big-long.tar 134 // bsdtar -xvf writer-big-long.tar 135 // 136 // This file is in PAX format. 137 file: "testdata/writer-big-long.tar", 138 entries: []*entry{{ 139 header: &Header{ 140 Name: strings.Repeat("longname/", 15) + "16gig.txt", 141 Mode: 0644, 142 Uid: 1000, 143 Gid: 1000, 144 Size: 16 << 30, 145 ModTime: time.Unix(1399583047, 0), 146 Typeflag: '0', 147 Uname: "guillaume", 148 Gname: "guillaume", 149 }, 150 // fake contents 151 contents: strings.Repeat("\x00", 4<<10), 152 }}, 153 }, { 154 // This file was produced using GNU tar v1.17. 155 // gnutar -b 4 --format=ustar (longname/)*15 + file.txt 156 file: "testdata/ustar.tar", 157 entries: []*entry{{ 158 header: &Header{ 159 Name: strings.Repeat("longname/", 15) + "file.txt", 160 Mode: 0644, 161 Uid: 0765, 162 Gid: 024, 163 Size: 06, 164 ModTime: time.Unix(1360135598, 0), 165 Typeflag: '0', 166 Uname: "shane", 167 Gname: "staff", 168 }, 169 contents: "hello\n", 170 }}, 171 }, { 172 // This file was produced using gnu tar 1.26 173 // echo "Slartibartfast" > file.txt 174 // ln file.txt hard.txt 175 // tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt 176 file: "testdata/hardlink.tar", 177 entries: []*entry{{ 178 header: &Header{ 179 Name: "file.txt", 180 Mode: 0644, 181 Uid: 1000, 182 Gid: 100, 183 Size: 15, 184 ModTime: time.Unix(1425484303, 0), 185 Typeflag: '0', 186 Uname: "vbatts", 187 Gname: "users", 188 }, 189 contents: "Slartibartfast\n", 190 }, { 191 header: &Header{ 192 Name: "hard.txt", 193 Mode: 0644, 194 Uid: 1000, 195 Gid: 100, 196 Size: 0, 197 ModTime: time.Unix(1425484303, 0), 198 Typeflag: '1', 199 Linkname: "file.txt", 200 Uname: "vbatts", 201 Gname: "users", 202 }, 203 // no contents 204 }}, 205 }, { 206 entries: []*entry{{ 207 header: &Header{ 208 Name: "bad-null.txt", 209 Typeflag: '0', 210 Xattrs: map[string]string{"null\x00null\x00": "fizzbuzz"}, 211 }, 212 }}, 213 err: ErrHeader, 214 }, { 215 entries: []*entry{{ 216 header: &Header{ 217 Name: "null\x00.txt", 218 Typeflag: '0', 219 }, 220 }}, 221 err: ErrHeader, 222 }, { 223 file: "testdata/gnu-utf8.tar", 224 entries: []*entry{{ 225 header: &Header{ 226 Name: "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹", 227 Mode: 0644, 228 Uid: 1000, Gid: 1000, 229 ModTime: time.Unix(0, 0), 230 Typeflag: '0', 231 Uname: "☺", 232 Gname: "⚹", 233 Devminor: -1, // Force use of GNU format 234 }, 235 }}, 236 }, { 237 file: "testdata/gnu-not-utf8.tar", 238 entries: []*entry{{ 239 header: &Header{ 240 Name: "hi\x80\x81\x82\x83bye", 241 Mode: 0644, 242 Uid: 1000, 243 Gid: 1000, 244 ModTime: time.Unix(0, 0), 245 Typeflag: '0', 246 Uname: "rawr", 247 Gname: "dsnet", 248 Devminor: -1, // Force use of GNU format 249 }, 250 }}, 251 }} 252 253 for _, v := range vectors { 254 t.Run(path.Base(v.file), func(t *testing.T) { 255 buf := new(bytes.Buffer) 256 tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB 257 canFail := false 258 for i, entry := range v.entries { 259 canFail = canFail || entry.header.Size > 1<<10 || v.err != nil 260 261 err := tw.WriteHeader(entry.header) 262 if err != v.err { 263 t.Fatalf("entry %d: WriteHeader() = %v, want %v", i, err, v.err) 264 } 265 if _, err := io.WriteString(tw, entry.contents); err != nil { 266 t.Fatalf("entry %d: WriteString() = %v, want nil", i, err) 267 } 268 } 269 // Only interested in Close failures for the small tests. 270 if err := tw.Close(); err != nil && !canFail { 271 t.Fatalf("Close() = %v, want nil", err) 272 } 273 274 if v.file != "" { 275 want, err := ioutil.ReadFile(v.file) 276 if err != nil { 277 t.Fatalf("ReadFile() = %v, want nil", err) 278 } 279 got := buf.Bytes() 280 if !bytes.Equal(want, got) { 281 t.Fatalf("incorrect result: (-got +want)\n%v", bytediff(got, want)) 282 } 283 } 284 }) 285 } 286 } 287 288 func TestPax(t *testing.T) { 289 // Create an archive with a large name 290 fileinfo, err := os.Stat("testdata/small.txt") 291 if err != nil { 292 t.Fatal(err) 293 } 294 hdr, err := FileInfoHeader(fileinfo, "") 295 if err != nil { 296 t.Fatalf("os.Stat: %v", err) 297 } 298 // Force a PAX long name to be written 299 longName := strings.Repeat("ab", 100) 300 contents := strings.Repeat(" ", int(hdr.Size)) 301 hdr.Name = longName 302 var buf bytes.Buffer 303 writer := NewWriter(&buf) 304 if err := writer.WriteHeader(hdr); err != nil { 305 t.Fatal(err) 306 } 307 if _, err = writer.Write([]byte(contents)); err != nil { 308 t.Fatal(err) 309 } 310 if err := writer.Close(); err != nil { 311 t.Fatal(err) 312 } 313 // Simple test to make sure PAX extensions are in effect 314 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { 315 t.Fatal("Expected at least one PAX header to be written.") 316 } 317 // Test that we can get a long name back out of the archive. 318 reader := NewReader(&buf) 319 hdr, err = reader.Next() 320 if err != nil { 321 t.Fatal(err) 322 } 323 if hdr.Name != longName { 324 t.Fatal("Couldn't recover long file name") 325 } 326 } 327 328 func TestPaxSymlink(t *testing.T) { 329 // Create an archive with a large linkname 330 fileinfo, err := os.Stat("testdata/small.txt") 331 if err != nil { 332 t.Fatal(err) 333 } 334 hdr, err := FileInfoHeader(fileinfo, "") 335 hdr.Typeflag = TypeSymlink 336 if err != nil { 337 t.Fatalf("os.Stat:1 %v", err) 338 } 339 // Force a PAX long linkname to be written 340 longLinkname := strings.Repeat("1234567890/1234567890", 10) 341 hdr.Linkname = longLinkname 342 343 hdr.Size = 0 344 var buf bytes.Buffer 345 writer := NewWriter(&buf) 346 if err := writer.WriteHeader(hdr); err != nil { 347 t.Fatal(err) 348 } 349 if err := writer.Close(); err != nil { 350 t.Fatal(err) 351 } 352 // Simple test to make sure PAX extensions are in effect 353 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { 354 t.Fatal("Expected at least one PAX header to be written.") 355 } 356 // Test that we can get a long name back out of the archive. 357 reader := NewReader(&buf) 358 hdr, err = reader.Next() 359 if err != nil { 360 t.Fatal(err) 361 } 362 if hdr.Linkname != longLinkname { 363 t.Fatal("Couldn't recover long link name") 364 } 365 } 366 367 func TestPaxNonAscii(t *testing.T) { 368 // Create an archive with non ascii. These should trigger a pax header 369 // because pax headers have a defined utf-8 encoding. 370 fileinfo, err := os.Stat("testdata/small.txt") 371 if err != nil { 372 t.Fatal(err) 373 } 374 375 hdr, err := FileInfoHeader(fileinfo, "") 376 if err != nil { 377 t.Fatalf("os.Stat:1 %v", err) 378 } 379 380 // some sample data 381 chineseFilename := "文件名" 382 chineseGroupname := "組" 383 chineseUsername := "用戶名" 384 385 hdr.Name = chineseFilename 386 hdr.Gname = chineseGroupname 387 hdr.Uname = chineseUsername 388 389 contents := strings.Repeat(" ", int(hdr.Size)) 390 391 var buf bytes.Buffer 392 writer := NewWriter(&buf) 393 if err := writer.WriteHeader(hdr); err != nil { 394 t.Fatal(err) 395 } 396 if _, err = writer.Write([]byte(contents)); err != nil { 397 t.Fatal(err) 398 } 399 if err := writer.Close(); err != nil { 400 t.Fatal(err) 401 } 402 // Simple test to make sure PAX extensions are in effect 403 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { 404 t.Fatal("Expected at least one PAX header to be written.") 405 } 406 // Test that we can get a long name back out of the archive. 407 reader := NewReader(&buf) 408 hdr, err = reader.Next() 409 if err != nil { 410 t.Fatal(err) 411 } 412 if hdr.Name != chineseFilename { 413 t.Fatal("Couldn't recover unicode name") 414 } 415 if hdr.Gname != chineseGroupname { 416 t.Fatal("Couldn't recover unicode group") 417 } 418 if hdr.Uname != chineseUsername { 419 t.Fatal("Couldn't recover unicode user") 420 } 421 } 422 423 func TestPaxXattrs(t *testing.T) { 424 xattrs := map[string]string{ 425 "user.key": "value", 426 } 427 428 // Create an archive with an xattr 429 fileinfo, err := os.Stat("testdata/small.txt") 430 if err != nil { 431 t.Fatal(err) 432 } 433 hdr, err := FileInfoHeader(fileinfo, "") 434 if err != nil { 435 t.Fatalf("os.Stat: %v", err) 436 } 437 contents := "Kilts" 438 hdr.Xattrs = xattrs 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.Write([]byte(contents)); err != nil { 445 t.Fatal(err) 446 } 447 if err := writer.Close(); err != nil { 448 t.Fatal(err) 449 } 450 // Test that we can get the xattrs back out of the archive. 451 reader := NewReader(&buf) 452 hdr, err = reader.Next() 453 if err != nil { 454 t.Fatal(err) 455 } 456 if !reflect.DeepEqual(hdr.Xattrs, xattrs) { 457 t.Fatalf("xattrs did not survive round trip: got %+v, want %+v", 458 hdr.Xattrs, xattrs) 459 } 460 } 461 462 func TestPaxHeadersSorted(t *testing.T) { 463 fileinfo, err := os.Stat("testdata/small.txt") 464 if err != nil { 465 t.Fatal(err) 466 } 467 hdr, err := FileInfoHeader(fileinfo, "") 468 if err != nil { 469 t.Fatalf("os.Stat: %v", err) 470 } 471 contents := strings.Repeat(" ", int(hdr.Size)) 472 473 hdr.Xattrs = map[string]string{ 474 "foo": "foo", 475 "bar": "bar", 476 "baz": "baz", 477 "qux": "qux", 478 } 479 480 var buf bytes.Buffer 481 writer := NewWriter(&buf) 482 if err := writer.WriteHeader(hdr); err != nil { 483 t.Fatal(err) 484 } 485 if _, err = writer.Write([]byte(contents)); err != nil { 486 t.Fatal(err) 487 } 488 if err := writer.Close(); err != nil { 489 t.Fatal(err) 490 } 491 // Simple test to make sure PAX extensions are in effect 492 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { 493 t.Fatal("Expected at least one PAX header to be written.") 494 } 495 496 // xattr bar should always appear before others 497 indices := []int{ 498 bytes.Index(buf.Bytes(), []byte("bar=bar")), 499 bytes.Index(buf.Bytes(), []byte("baz=baz")), 500 bytes.Index(buf.Bytes(), []byte("foo=foo")), 501 bytes.Index(buf.Bytes(), []byte("qux=qux")), 502 } 503 if !sort.IntsAreSorted(indices) { 504 t.Fatal("PAX headers are not sorted") 505 } 506 } 507 508 func TestUSTARLongName(t *testing.T) { 509 // Create an archive with a path that failed to split with USTAR extension in previous versions. 510 fileinfo, err := os.Stat("testdata/small.txt") 511 if err != nil { 512 t.Fatal(err) 513 } 514 hdr, err := FileInfoHeader(fileinfo, "") 515 hdr.Typeflag = TypeDir 516 if err != nil { 517 t.Fatalf("os.Stat:1 %v", err) 518 } 519 // Force a PAX long name to be written. The name was taken from a practical example 520 // that fails and replaced ever char through numbers to anonymize the sample. 521 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/" 522 hdr.Name = longName 523 524 hdr.Size = 0 525 var buf bytes.Buffer 526 writer := NewWriter(&buf) 527 if err := writer.WriteHeader(hdr); err != nil { 528 t.Fatal(err) 529 } 530 if err := writer.Close(); err != nil { 531 t.Fatal(err) 532 } 533 // Test that we can get a long name back out of the archive. 534 reader := NewReader(&buf) 535 hdr, err = reader.Next() 536 if err != nil { 537 t.Fatal(err) 538 } 539 if hdr.Name != longName { 540 t.Fatal("Couldn't recover long name") 541 } 542 } 543 544 func TestValidTypeflagWithPAXHeader(t *testing.T) { 545 var buffer bytes.Buffer 546 tw := NewWriter(&buffer) 547 548 fileName := strings.Repeat("ab", 100) 549 550 hdr := &Header{ 551 Name: fileName, 552 Size: 4, 553 Typeflag: 0, 554 } 555 if err := tw.WriteHeader(hdr); err != nil { 556 t.Fatalf("Failed to write header: %s", err) 557 } 558 if _, err := tw.Write([]byte("fooo")); err != nil { 559 t.Fatalf("Failed to write the file's data: %s", err) 560 } 561 tw.Close() 562 563 tr := NewReader(&buffer) 564 565 for { 566 header, err := tr.Next() 567 if err == io.EOF { 568 break 569 } 570 if err != nil { 571 t.Fatalf("Failed to read header: %s", err) 572 } 573 if header.Typeflag != 0 { 574 t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag) 575 } 576 } 577 } 578 579 // failOnceWriter fails exactly once and then always reports success. 580 type failOnceWriter bool 581 582 func (w *failOnceWriter) Write(b []byte) (int, error) { 583 if !*w { 584 return 0, io.ErrShortWrite 585 } 586 *w = true 587 return len(b), nil 588 } 589 590 func TestWriterErrors(t *testing.T) { 591 t.Run("HeaderOnly", func(t *testing.T) { 592 tw := NewWriter(new(bytes.Buffer)) 593 hdr := &Header{Name: "dir/", Typeflag: TypeDir} 594 if err := tw.WriteHeader(hdr); err != nil { 595 t.Fatalf("WriteHeader() = %v, want nil", err) 596 } 597 if _, err := tw.Write([]byte{0x00}); err != ErrWriteTooLong { 598 t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong) 599 } 600 }) 601 602 t.Run("NegativeSize", func(t *testing.T) { 603 tw := NewWriter(new(bytes.Buffer)) 604 hdr := &Header{Name: "small.txt", Size: -1} 605 if err := tw.WriteHeader(hdr); err != ErrHeader { 606 t.Fatalf("WriteHeader() = nil, want %v", ErrHeader) 607 } 608 }) 609 610 t.Run("BeforeHeader", func(t *testing.T) { 611 tw := NewWriter(new(bytes.Buffer)) 612 if _, err := tw.Write([]byte("Kilts")); err != ErrWriteTooLong { 613 t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong) 614 } 615 }) 616 617 t.Run("AfterClose", func(t *testing.T) { 618 tw := NewWriter(new(bytes.Buffer)) 619 hdr := &Header{Name: "small.txt"} 620 if err := tw.WriteHeader(hdr); err != nil { 621 t.Fatalf("WriteHeader() = %v, want nil", err) 622 } 623 if err := tw.Close(); err != nil { 624 t.Fatalf("Close() = %v, want nil", err) 625 } 626 if _, err := tw.Write([]byte("Kilts")); err != ErrWriteAfterClose { 627 t.Fatalf("Write() = %v, want %v", err, ErrWriteAfterClose) 628 } 629 if err := tw.Flush(); err != ErrWriteAfterClose { 630 t.Fatalf("Flush() = %v, want %v", err, ErrWriteAfterClose) 631 } 632 if err := tw.Close(); err != nil { 633 t.Fatalf("Close() = %v, want nil", err) 634 } 635 }) 636 637 t.Run("PrematureFlush", func(t *testing.T) { 638 tw := NewWriter(new(bytes.Buffer)) 639 hdr := &Header{Name: "small.txt", Size: 5} 640 if err := tw.WriteHeader(hdr); err != nil { 641 t.Fatalf("WriteHeader() = %v, want nil", err) 642 } 643 if err := tw.Flush(); err == nil { 644 t.Fatalf("Flush() = %v, want non-nil error", err) 645 } 646 }) 647 648 t.Run("PrematureClose", func(t *testing.T) { 649 tw := NewWriter(new(bytes.Buffer)) 650 hdr := &Header{Name: "small.txt", Size: 5} 651 if err := tw.WriteHeader(hdr); err != nil { 652 t.Fatalf("WriteHeader() = %v, want nil", err) 653 } 654 if err := tw.Close(); err == nil { 655 t.Fatalf("Close() = %v, want non-nil error", err) 656 } 657 }) 658 659 t.Run("Persistence", func(t *testing.T) { 660 tw := NewWriter(new(failOnceWriter)) 661 if err := tw.WriteHeader(&Header{}); err != io.ErrShortWrite { 662 t.Fatalf("WriteHeader() = %v, want %v", err, io.ErrShortWrite) 663 } 664 if err := tw.WriteHeader(&Header{Name: "small.txt"}); err == nil { 665 t.Errorf("WriteHeader() = got %v, want non-nil error", err) 666 } 667 if _, err := tw.Write(nil); err == nil { 668 t.Errorf("Write() = %v, want non-nil error", err) 669 } 670 if err := tw.Flush(); err == nil { 671 t.Errorf("Flush() = %v, want non-nil error", err) 672 } 673 if err := tw.Close(); err == nil { 674 t.Errorf("Close() = %v, want non-nil error", err) 675 } 676 }) 677 } 678 679 func TestSplitUSTARPath(t *testing.T) { 680 sr := strings.Repeat 681 682 vectors := []struct { 683 input string // Input path 684 prefix string // Expected output prefix 685 suffix string // Expected output suffix 686 ok bool // Split success? 687 }{ 688 {"", "", "", false}, 689 {"abc", "", "", false}, 690 {"用戶名", "", "", false}, 691 {sr("a", nameSize), "", "", false}, 692 {sr("a", nameSize) + "/", "", "", false}, 693 {sr("a", nameSize) + "/a", sr("a", nameSize), "a", true}, 694 {sr("a", prefixSize) + "/", "", "", false}, 695 {sr("a", prefixSize) + "/a", sr("a", prefixSize), "a", true}, 696 {sr("a", nameSize+1), "", "", false}, 697 {sr("/", nameSize+1), sr("/", nameSize-1), "/", true}, 698 {sr("a", prefixSize) + "/" + sr("b", nameSize), 699 sr("a", prefixSize), sr("b", nameSize), true}, 700 {sr("a", prefixSize) + "//" + sr("b", nameSize), "", "", false}, 701 {sr("a/", nameSize), sr("a/", 77) + "a", sr("a/", 22), true}, 702 } 703 704 for _, v := range vectors { 705 prefix, suffix, ok := splitUSTARPath(v.input) 706 if prefix != v.prefix || suffix != v.suffix || ok != v.ok { 707 t.Errorf("splitUSTARPath(%q):\ngot (%q, %q, %v)\nwant (%q, %q, %v)", 708 v.input, prefix, suffix, ok, v.prefix, v.suffix, v.ok) 709 } 710 } 711 } 712 713 // TestIssue12594 tests that the Writer does not attempt to populate the prefix 714 // field when encoding a header in the GNU format. The prefix field is valid 715 // in USTAR and PAX, but not GNU. 716 func TestIssue12594(t *testing.T) { 717 names := []string{ 718 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/file.txt", 719 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/file.txt", 720 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/333/file.txt", 721 "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/file.txt", 722 "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/file.txt", 723 "/home/support/.openoffice.org/3/user/uno_packages/cache/registry/com.sun.star.comp.deployment.executable.PackageRegistryBackend", 724 } 725 726 for i, name := range names { 727 var b bytes.Buffer 728 729 tw := NewWriter(&b) 730 if err := tw.WriteHeader(&Header{ 731 Name: name, 732 Uid: 1 << 25, // Prevent USTAR format 733 }); err != nil { 734 t.Errorf("test %d, unexpected WriteHeader error: %v", i, err) 735 } 736 if err := tw.Close(); err != nil { 737 t.Errorf("test %d, unexpected Close error: %v", i, err) 738 } 739 740 // The prefix field should never appear in the GNU format. 741 var blk block 742 copy(blk[:], b.Bytes()) 743 prefix := string(blk.USTAR().Prefix()) 744 if i := strings.IndexByte(prefix, 0); i >= 0 { 745 prefix = prefix[:i] // Truncate at the NUL terminator 746 } 747 if blk.GetFormat() == formatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) { 748 t.Errorf("test %d, found prefix in GNU format: %s", i, prefix) 749 } 750 751 tr := NewReader(&b) 752 hdr, err := tr.Next() 753 if err != nil { 754 t.Errorf("test %d, unexpected Next error: %v", i, err) 755 } 756 if hdr.Name != name { 757 t.Errorf("test %d, hdr.Name = %s, want %s", i, hdr.Name, name) 758 } 759 } 760 }