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  }