github.com/eun/go@v0.0.0-20170811110501-92cfd07a6cfd/src/archive/tar/tar_test.go (about) 1 // Copyright 2012 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 "internal/testenv" 10 "io/ioutil" 11 "math" 12 "os" 13 "path" 14 "path/filepath" 15 "reflect" 16 "strings" 17 "testing" 18 "time" 19 ) 20 21 func TestFileInfoHeader(t *testing.T) { 22 fi, err := os.Stat("testdata/small.txt") 23 if err != nil { 24 t.Fatal(err) 25 } 26 h, err := FileInfoHeader(fi, "") 27 if err != nil { 28 t.Fatalf("FileInfoHeader: %v", err) 29 } 30 if g, e := h.Name, "small.txt"; g != e { 31 t.Errorf("Name = %q; want %q", g, e) 32 } 33 if g, e := h.Mode, int64(fi.Mode().Perm()); g != e { 34 t.Errorf("Mode = %#o; want %#o", g, e) 35 } 36 if g, e := h.Size, int64(5); g != e { 37 t.Errorf("Size = %v; want %v", g, e) 38 } 39 if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) { 40 t.Errorf("ModTime = %v; want %v", g, e) 41 } 42 // FileInfoHeader should error when passing nil FileInfo 43 if _, err := FileInfoHeader(nil, ""); err == nil { 44 t.Fatalf("Expected error when passing nil to FileInfoHeader") 45 } 46 } 47 48 func TestFileInfoHeaderDir(t *testing.T) { 49 fi, err := os.Stat("testdata") 50 if err != nil { 51 t.Fatal(err) 52 } 53 h, err := FileInfoHeader(fi, "") 54 if err != nil { 55 t.Fatalf("FileInfoHeader: %v", err) 56 } 57 if g, e := h.Name, "testdata/"; g != e { 58 t.Errorf("Name = %q; want %q", g, e) 59 } 60 // Ignoring c_ISGID for golang.org/issue/4867 61 if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm()); g != e { 62 t.Errorf("Mode = %#o; want %#o", g, e) 63 } 64 if g, e := h.Size, int64(0); g != e { 65 t.Errorf("Size = %v; want %v", g, e) 66 } 67 if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) { 68 t.Errorf("ModTime = %v; want %v", g, e) 69 } 70 } 71 72 func TestFileInfoHeaderSymlink(t *testing.T) { 73 testenv.MustHaveSymlink(t) 74 75 tmpdir, err := ioutil.TempDir("", "TestFileInfoHeaderSymlink") 76 if err != nil { 77 t.Fatal(err) 78 } 79 defer os.RemoveAll(tmpdir) 80 81 link := filepath.Join(tmpdir, "link") 82 target := tmpdir 83 err = os.Symlink(target, link) 84 if err != nil { 85 t.Fatal(err) 86 } 87 fi, err := os.Lstat(link) 88 if err != nil { 89 t.Fatal(err) 90 } 91 92 h, err := FileInfoHeader(fi, target) 93 if err != nil { 94 t.Fatal(err) 95 } 96 if g, e := h.Name, fi.Name(); g != e { 97 t.Errorf("Name = %q; want %q", g, e) 98 } 99 if g, e := h.Linkname, target; g != e { 100 t.Errorf("Linkname = %q; want %q", g, e) 101 } 102 if g, e := h.Typeflag, byte(TypeSymlink); g != e { 103 t.Errorf("Typeflag = %v; want %v", g, e) 104 } 105 } 106 107 func TestRoundTrip(t *testing.T) { 108 data := []byte("some file contents") 109 110 var b bytes.Buffer 111 tw := NewWriter(&b) 112 hdr := &Header{ 113 Name: "file.txt", 114 Uid: 1 << 21, // too big for 8 octal digits 115 Size: int64(len(data)), 116 // AddDate to strip monotonic clock reading, 117 // and Round to discard sub-second precision, 118 // both of which are not included in the tar header 119 // and would otherwise break the round-trip check 120 // below. 121 ModTime: time.Now().AddDate(0, 0, 0).Round(1 * time.Second), 122 } 123 if err := tw.WriteHeader(hdr); err != nil { 124 t.Fatalf("tw.WriteHeader: %v", err) 125 } 126 if _, err := tw.Write(data); err != nil { 127 t.Fatalf("tw.Write: %v", err) 128 } 129 if err := tw.Close(); err != nil { 130 t.Fatalf("tw.Close: %v", err) 131 } 132 133 // Read it back. 134 tr := NewReader(&b) 135 rHdr, err := tr.Next() 136 if err != nil { 137 t.Fatalf("tr.Next: %v", err) 138 } 139 if !reflect.DeepEqual(rHdr, hdr) { 140 t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr) 141 } 142 rData, err := ioutil.ReadAll(tr) 143 if err != nil { 144 t.Fatalf("Read: %v", err) 145 } 146 if !bytes.Equal(rData, data) { 147 t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data) 148 } 149 } 150 151 type headerRoundTripTest struct { 152 h *Header 153 fm os.FileMode 154 } 155 156 func TestHeaderRoundTrip(t *testing.T) { 157 vectors := []headerRoundTripTest{{ 158 // regular file. 159 h: &Header{ 160 Name: "test.txt", 161 Mode: 0644, 162 Size: 12, 163 ModTime: time.Unix(1360600916, 0), 164 Typeflag: TypeReg, 165 }, 166 fm: 0644, 167 }, { 168 // symbolic link. 169 h: &Header{ 170 Name: "link.txt", 171 Mode: 0777, 172 Size: 0, 173 ModTime: time.Unix(1360600852, 0), 174 Typeflag: TypeSymlink, 175 }, 176 fm: 0777 | os.ModeSymlink, 177 }, { 178 // character device node. 179 h: &Header{ 180 Name: "dev/null", 181 Mode: 0666, 182 Size: 0, 183 ModTime: time.Unix(1360578951, 0), 184 Typeflag: TypeChar, 185 }, 186 fm: 0666 | os.ModeDevice | os.ModeCharDevice, 187 }, { 188 // block device node. 189 h: &Header{ 190 Name: "dev/sda", 191 Mode: 0660, 192 Size: 0, 193 ModTime: time.Unix(1360578954, 0), 194 Typeflag: TypeBlock, 195 }, 196 fm: 0660 | os.ModeDevice, 197 }, { 198 // directory. 199 h: &Header{ 200 Name: "dir/", 201 Mode: 0755, 202 Size: 0, 203 ModTime: time.Unix(1360601116, 0), 204 Typeflag: TypeDir, 205 }, 206 fm: 0755 | os.ModeDir, 207 }, { 208 // fifo node. 209 h: &Header{ 210 Name: "dev/initctl", 211 Mode: 0600, 212 Size: 0, 213 ModTime: time.Unix(1360578949, 0), 214 Typeflag: TypeFifo, 215 }, 216 fm: 0600 | os.ModeNamedPipe, 217 }, { 218 // setuid. 219 h: &Header{ 220 Name: "bin/su", 221 Mode: 0755 | c_ISUID, 222 Size: 23232, 223 ModTime: time.Unix(1355405093, 0), 224 Typeflag: TypeReg, 225 }, 226 fm: 0755 | os.ModeSetuid, 227 }, { 228 // setguid. 229 h: &Header{ 230 Name: "group.txt", 231 Mode: 0750 | c_ISGID, 232 Size: 0, 233 ModTime: time.Unix(1360602346, 0), 234 Typeflag: TypeReg, 235 }, 236 fm: 0750 | os.ModeSetgid, 237 }, { 238 // sticky. 239 h: &Header{ 240 Name: "sticky.txt", 241 Mode: 0600 | c_ISVTX, 242 Size: 7, 243 ModTime: time.Unix(1360602540, 0), 244 Typeflag: TypeReg, 245 }, 246 fm: 0600 | os.ModeSticky, 247 }, { 248 // hard link. 249 h: &Header{ 250 Name: "hard.txt", 251 Mode: 0644, 252 Size: 0, 253 Linkname: "file.txt", 254 ModTime: time.Unix(1360600916, 0), 255 Typeflag: TypeLink, 256 }, 257 fm: 0644, 258 }, { 259 // More information. 260 h: &Header{ 261 Name: "info.txt", 262 Mode: 0600, 263 Size: 0, 264 Uid: 1000, 265 Gid: 1000, 266 ModTime: time.Unix(1360602540, 0), 267 Uname: "slartibartfast", 268 Gname: "users", 269 Typeflag: TypeReg, 270 }, 271 fm: 0600, 272 }} 273 274 for i, v := range vectors { 275 fi := v.h.FileInfo() 276 h2, err := FileInfoHeader(fi, "") 277 if err != nil { 278 t.Error(err) 279 continue 280 } 281 if strings.Contains(fi.Name(), "/") { 282 t.Errorf("FileInfo of %q contains slash: %q", v.h.Name, fi.Name()) 283 } 284 name := path.Base(v.h.Name) 285 if fi.IsDir() { 286 name += "/" 287 } 288 if got, want := h2.Name, name; got != want { 289 t.Errorf("i=%d: Name: got %v, want %v", i, got, want) 290 } 291 if got, want := h2.Size, v.h.Size; got != want { 292 t.Errorf("i=%d: Size: got %v, want %v", i, got, want) 293 } 294 if got, want := h2.Uid, v.h.Uid; got != want { 295 t.Errorf("i=%d: Uid: got %d, want %d", i, got, want) 296 } 297 if got, want := h2.Gid, v.h.Gid; got != want { 298 t.Errorf("i=%d: Gid: got %d, want %d", i, got, want) 299 } 300 if got, want := h2.Uname, v.h.Uname; got != want { 301 t.Errorf("i=%d: Uname: got %q, want %q", i, got, want) 302 } 303 if got, want := h2.Gname, v.h.Gname; got != want { 304 t.Errorf("i=%d: Gname: got %q, want %q", i, got, want) 305 } 306 if got, want := h2.Linkname, v.h.Linkname; got != want { 307 t.Errorf("i=%d: Linkname: got %v, want %v", i, got, want) 308 } 309 if got, want := h2.Typeflag, v.h.Typeflag; got != want { 310 t.Logf("%#v %#v", v.h, fi.Sys()) 311 t.Errorf("i=%d: Typeflag: got %q, want %q", i, got, want) 312 } 313 if got, want := h2.Mode, v.h.Mode; got != want { 314 t.Errorf("i=%d: Mode: got %o, want %o", i, got, want) 315 } 316 if got, want := fi.Mode(), v.fm; got != want { 317 t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want) 318 } 319 if got, want := h2.AccessTime, v.h.AccessTime; got != want { 320 t.Errorf("i=%d: AccessTime: got %v, want %v", i, got, want) 321 } 322 if got, want := h2.ChangeTime, v.h.ChangeTime; got != want { 323 t.Errorf("i=%d: ChangeTime: got %v, want %v", i, got, want) 324 } 325 if got, want := h2.ModTime, v.h.ModTime; got != want { 326 t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want) 327 } 328 if sysh, ok := fi.Sys().(*Header); !ok || sysh != v.h { 329 t.Errorf("i=%d: Sys didn't return original *Header", i) 330 } 331 } 332 } 333 334 func TestHeaderAllowedFormats(t *testing.T) { 335 prettyFormat := func(f int) string { 336 if f == formatUnknown { 337 return "(formatUnknown)" 338 } 339 var fs []string 340 if f&formatUSTAR > 0 { 341 fs = append(fs, "formatUSTAR") 342 } 343 if f&formatPAX > 0 { 344 fs = append(fs, "formatPAX") 345 } 346 if f&formatGNU > 0 { 347 fs = append(fs, "formatGNU") 348 } 349 return "(" + strings.Join(fs, " | ") + ")" 350 } 351 352 vectors := []struct { 353 header *Header // Input header 354 paxHdrs map[string]string // Expected PAX headers that may be needed 355 formats int // Expected formats that can encode the header 356 }{{ 357 header: &Header{}, 358 formats: formatUSTAR | formatPAX | formatGNU, 359 }, { 360 header: &Header{Size: 077777777777}, 361 formats: formatUSTAR | formatPAX | formatGNU, 362 }, { 363 header: &Header{Size: 077777777777 + 1}, 364 paxHdrs: map[string]string{paxSize: "8589934592"}, 365 formats: formatPAX | formatGNU, 366 }, { 367 header: &Header{Mode: 07777777}, 368 formats: formatUSTAR | formatPAX | formatGNU, 369 }, { 370 header: &Header{Mode: 07777777 + 1}, 371 formats: formatGNU, 372 }, { 373 header: &Header{Devmajor: -123}, 374 formats: formatGNU, 375 }, { 376 header: &Header{Devmajor: 1<<56 - 1}, 377 formats: formatGNU, 378 }, { 379 header: &Header{Devmajor: 1 << 56}, 380 formats: formatUnknown, 381 }, { 382 header: &Header{Devmajor: -1 << 56}, 383 formats: formatGNU, 384 }, { 385 header: &Header{Devmajor: -1<<56 - 1}, 386 formats: formatUnknown, 387 }, { 388 header: &Header{Name: "用戶名", Devmajor: -1 << 56}, 389 formats: formatUnknown, 390 }, { 391 header: &Header{Size: math.MaxInt64}, 392 paxHdrs: map[string]string{paxSize: "9223372036854775807"}, 393 formats: formatPAX | formatGNU, 394 }, { 395 header: &Header{Size: math.MinInt64}, 396 paxHdrs: map[string]string{paxSize: "-9223372036854775808"}, 397 formats: formatUnknown, 398 }, { 399 header: &Header{Uname: "0123456789abcdef0123456789abcdef"}, 400 formats: formatUSTAR | formatPAX | formatGNU, 401 }, { 402 header: &Header{Uname: "0123456789abcdef0123456789abcdefx"}, 403 paxHdrs: map[string]string{paxUname: "0123456789abcdef0123456789abcdefx"}, 404 formats: formatPAX, 405 }, { 406 header: &Header{Name: "foobar"}, 407 formats: formatUSTAR | formatPAX | formatGNU, 408 }, { 409 header: &Header{Name: strings.Repeat("a", nameSize)}, 410 formats: formatUSTAR | formatPAX | formatGNU, 411 }, { 412 header: &Header{Linkname: "用戶名"}, 413 paxHdrs: map[string]string{paxLinkpath: "用戶名"}, 414 formats: formatPAX, 415 }, { 416 header: &Header{Linkname: strings.Repeat("用戶名\x00", nameSize)}, 417 paxHdrs: map[string]string{paxLinkpath: strings.Repeat("用戶名\x00", nameSize)}, 418 formats: formatUnknown, 419 }, { 420 header: &Header{Linkname: "\x00hello"}, 421 paxHdrs: map[string]string{paxLinkpath: "\x00hello"}, 422 formats: formatUnknown, 423 }, { 424 header: &Header{Uid: 07777777}, 425 formats: formatUSTAR | formatPAX | formatGNU, 426 }, { 427 header: &Header{Uid: 07777777 + 1}, 428 paxHdrs: map[string]string{paxUid: "2097152"}, 429 formats: formatPAX | formatGNU, 430 }, { 431 header: &Header{Xattrs: nil}, 432 formats: formatUSTAR | formatPAX | formatGNU, 433 }, { 434 header: &Header{Xattrs: map[string]string{"foo": "bar"}}, 435 paxHdrs: map[string]string{paxXattr + "foo": "bar"}, 436 formats: formatPAX, 437 }, { 438 header: &Header{Xattrs: map[string]string{"用戶名": "\x00hello"}}, 439 paxHdrs: map[string]string{paxXattr + "用戶名": "\x00hello"}, 440 formats: formatPAX, 441 }, { 442 header: &Header{ModTime: time.Unix(0, 0)}, 443 formats: formatUSTAR | formatPAX | formatGNU, 444 }, { 445 header: &Header{ModTime: time.Unix(077777777777, 0)}, 446 formats: formatUSTAR | formatPAX | formatGNU, 447 }} 448 449 for i, v := range vectors { 450 formats, paxHdrs := v.header.allowedFormats() 451 if formats != v.formats { 452 t.Errorf("test %d, allowedFormats(...): got %v, want %v", i, prettyFormat(formats), prettyFormat(v.formats)) 453 } 454 if formats&formatPAX > 0 && !reflect.DeepEqual(paxHdrs, v.paxHdrs) && !(len(paxHdrs) == 0 && len(v.paxHdrs) == 0) { 455 t.Errorf("test %d, allowedFormats(...):\ngot %v\nwant %s", i, paxHdrs, v.paxHdrs) 456 } 457 } 458 }