Home | History | Annotate | Download | only in tar
      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 	"io/ioutil"
     10 	"os"
     11 	"path"
     12 	"reflect"
     13 	"strings"
     14 	"testing"
     15 	"time"
     16 )
     17 
     18 func TestFileInfoHeader(t *testing.T) {
     19 	fi, err := os.Stat("testdata/small.txt")
     20 	if err != nil {
     21 		t.Fatal(err)
     22 	}
     23 	h, err := FileInfoHeader(fi, "")
     24 	if err != nil {
     25 		t.Fatalf("FileInfoHeader: %v", err)
     26 	}
     27 	if g, e := h.Name, "small.txt"; g != e {
     28 		t.Errorf("Name = %q; want %q", g, e)
     29 	}
     30 	if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e {
     31 		t.Errorf("Mode = %#o; want %#o", g, e)
     32 	}
     33 	if g, e := h.Size, int64(5); g != e {
     34 		t.Errorf("Size = %v; want %v", g, e)
     35 	}
     36 	if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
     37 		t.Errorf("ModTime = %v; want %v", g, e)
     38 	}
     39 	// FileInfoHeader should error when passing nil FileInfo
     40 	if _, err := FileInfoHeader(nil, ""); err == nil {
     41 		t.Fatalf("Expected error when passing nil to FileInfoHeader")
     42 	}
     43 }
     44 
     45 func TestFileInfoHeaderDir(t *testing.T) {
     46 	fi, err := os.Stat("testdata")
     47 	if err != nil {
     48 		t.Fatal(err)
     49 	}
     50 	h, err := FileInfoHeader(fi, "")
     51 	if err != nil {
     52 		t.Fatalf("FileInfoHeader: %v", err)
     53 	}
     54 	if g, e := h.Name, "testdata/"; g != e {
     55 		t.Errorf("Name = %q; want %q", g, e)
     56 	}
     57 	// Ignoring c_ISGID for golang.org/issue/4867
     58 	if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm())|c_ISDIR; g != e {
     59 		t.Errorf("Mode = %#o; want %#o", g, e)
     60 	}
     61 	if g, e := h.Size, int64(0); g != e {
     62 		t.Errorf("Size = %v; want %v", g, e)
     63 	}
     64 	if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
     65 		t.Errorf("ModTime = %v; want %v", g, e)
     66 	}
     67 }
     68 
     69 func TestFileInfoHeaderSymlink(t *testing.T) {
     70 	h, err := FileInfoHeader(symlink{}, "some-target")
     71 	if err != nil {
     72 		t.Fatal(err)
     73 	}
     74 	if g, e := h.Name, "some-symlink"; g != e {
     75 		t.Errorf("Name = %q; want %q", g, e)
     76 	}
     77 	if g, e := h.Linkname, "some-target"; g != e {
     78 		t.Errorf("Linkname = %q; want %q", g, e)
     79 	}
     80 }
     81 
     82 type symlink struct{}
     83 
     84 func (symlink) Name() string       { return "some-symlink" }
     85 func (symlink) Size() int64        { return 0 }
     86 func (symlink) Mode() os.FileMode  { return os.ModeSymlink }
     87 func (symlink) ModTime() time.Time { return time.Time{} }
     88 func (symlink) IsDir() bool        { return false }
     89 func (symlink) Sys() interface{}   { return nil }
     90 
     91 func TestRoundTrip(t *testing.T) {
     92 	data := []byte("some file contents")
     93 
     94 	var b bytes.Buffer
     95 	tw := NewWriter(&b)
     96 	hdr := &Header{
     97 		Name:    "file.txt",
     98 		Uid:     1 << 21, // too big for 8 octal digits
     99 		Size:    int64(len(data)),
    100 		ModTime: time.Now(),
    101 	}
    102 	// tar only supports second precision.
    103 	hdr.ModTime = hdr.ModTime.Add(-time.Duration(hdr.ModTime.Nanosecond()) * time.Nanosecond)
    104 	if err := tw.WriteHeader(hdr); err != nil {
    105 		t.Fatalf("tw.WriteHeader: %v", err)
    106 	}
    107 	if _, err := tw.Write(data); err != nil {
    108 		t.Fatalf("tw.Write: %v", err)
    109 	}
    110 	if err := tw.Close(); err != nil {
    111 		t.Fatalf("tw.Close: %v", err)
    112 	}
    113 
    114 	// Read it back.
    115 	tr := NewReader(&b)
    116 	rHdr, err := tr.Next()
    117 	if err != nil {
    118 		t.Fatalf("tr.Next: %v", err)
    119 	}
    120 	if !reflect.DeepEqual(rHdr, hdr) {
    121 		t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr)
    122 	}
    123 	rData, err := ioutil.ReadAll(tr)
    124 	if err != nil {
    125 		t.Fatalf("Read: %v", err)
    126 	}
    127 	if !bytes.Equal(rData, data) {
    128 		t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data)
    129 	}
    130 }
    131 
    132 type headerRoundTripTest struct {
    133 	h  *Header
    134 	fm os.FileMode
    135 }
    136 
    137 func TestHeaderRoundTrip(t *testing.T) {
    138 	golden := []headerRoundTripTest{
    139 		// regular file.
    140 		{
    141 			h: &Header{
    142 				Name:     "test.txt",
    143 				Mode:     0644 | c_ISREG,
    144 				Size:     12,
    145 				ModTime:  time.Unix(1360600916, 0),
    146 				Typeflag: TypeReg,
    147 			},
    148 			fm: 0644,
    149 		},
    150 		// symbolic link.
    151 		{
    152 			h: &Header{
    153 				Name:     "link.txt",
    154 				Mode:     0777 | c_ISLNK,
    155 				Size:     0,
    156 				ModTime:  time.Unix(1360600852, 0),
    157 				Typeflag: TypeSymlink,
    158 			},
    159 			fm: 0777 | os.ModeSymlink,
    160 		},
    161 		// character device node.
    162 		{
    163 			h: &Header{
    164 				Name:     "dev/null",
    165 				Mode:     0666 | c_ISCHR,
    166 				Size:     0,
    167 				ModTime:  time.Unix(1360578951, 0),
    168 				Typeflag: TypeChar,
    169 			},
    170 			fm: 0666 | os.ModeDevice | os.ModeCharDevice,
    171 		},
    172 		// block device node.
    173 		{
    174 			h: &Header{
    175 				Name:     "dev/sda",
    176 				Mode:     0660 | c_ISBLK,
    177 				Size:     0,
    178 				ModTime:  time.Unix(1360578954, 0),
    179 				Typeflag: TypeBlock,
    180 			},
    181 			fm: 0660 | os.ModeDevice,
    182 		},
    183 		// directory.
    184 		{
    185 			h: &Header{
    186 				Name:     "dir/",
    187 				Mode:     0755 | c_ISDIR,
    188 				Size:     0,
    189 				ModTime:  time.Unix(1360601116, 0),
    190 				Typeflag: TypeDir,
    191 			},
    192 			fm: 0755 | os.ModeDir,
    193 		},
    194 		// fifo node.
    195 		{
    196 			h: &Header{
    197 				Name:     "dev/initctl",
    198 				Mode:     0600 | c_ISFIFO,
    199 				Size:     0,
    200 				ModTime:  time.Unix(1360578949, 0),
    201 				Typeflag: TypeFifo,
    202 			},
    203 			fm: 0600 | os.ModeNamedPipe,
    204 		},
    205 		// setuid.
    206 		{
    207 			h: &Header{
    208 				Name:     "bin/su",
    209 				Mode:     0755 | c_ISREG | c_ISUID,
    210 				Size:     23232,
    211 				ModTime:  time.Unix(1355405093, 0),
    212 				Typeflag: TypeReg,
    213 			},
    214 			fm: 0755 | os.ModeSetuid,
    215 		},
    216 		// setguid.
    217 		{
    218 			h: &Header{
    219 				Name:     "group.txt",
    220 				Mode:     0750 | c_ISREG | c_ISGID,
    221 				Size:     0,
    222 				ModTime:  time.Unix(1360602346, 0),
    223 				Typeflag: TypeReg,
    224 			},
    225 			fm: 0750 | os.ModeSetgid,
    226 		},
    227 		// sticky.
    228 		{
    229 			h: &Header{
    230 				Name:     "sticky.txt",
    231 				Mode:     0600 | c_ISREG | c_ISVTX,
    232 				Size:     7,
    233 				ModTime:  time.Unix(1360602540, 0),
    234 				Typeflag: TypeReg,
    235 			},
    236 			fm: 0600 | os.ModeSticky,
    237 		},
    238 		// hard link.
    239 		{
    240 			h: &Header{
    241 				Name:     "hard.txt",
    242 				Mode:     0644 | c_ISREG,
    243 				Size:     0,
    244 				Linkname: "file.txt",
    245 				ModTime:  time.Unix(1360600916, 0),
    246 				Typeflag: TypeLink,
    247 			},
    248 			fm: 0644,
    249 		},
    250 		// More information.
    251 		{
    252 			h: &Header{
    253 				Name:     "info.txt",
    254 				Mode:     0600 | c_ISREG,
    255 				Size:     0,
    256 				Uid:      1000,
    257 				Gid:      1000,
    258 				ModTime:  time.Unix(1360602540, 0),
    259 				Uname:    "slartibartfast",
    260 				Gname:    "users",
    261 				Typeflag: TypeReg,
    262 			},
    263 			fm: 0600,
    264 		},
    265 	}
    266 
    267 	for i, g := range golden {
    268 		fi := g.h.FileInfo()
    269 		h2, err := FileInfoHeader(fi, "")
    270 		if err != nil {
    271 			t.Error(err)
    272 			continue
    273 		}
    274 		if strings.Contains(fi.Name(), "/") {
    275 			t.Errorf("FileInfo of %q contains slash: %q", g.h.Name, fi.Name())
    276 		}
    277 		name := path.Base(g.h.Name)
    278 		if fi.IsDir() {
    279 			name += "/"
    280 		}
    281 		if got, want := h2.Name, name; got != want {
    282 			t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
    283 		}
    284 		if got, want := h2.Size, g.h.Size; got != want {
    285 			t.Errorf("i=%d: Size: got %v, want %v", i, got, want)
    286 		}
    287 		if got, want := h2.Uid, g.h.Uid; got != want {
    288 			t.Errorf("i=%d: Uid: got %d, want %d", i, got, want)
    289 		}
    290 		if got, want := h2.Gid, g.h.Gid; got != want {
    291 			t.Errorf("i=%d: Gid: got %d, want %d", i, got, want)
    292 		}
    293 		if got, want := h2.Uname, g.h.Uname; got != want {
    294 			t.Errorf("i=%d: Uname: got %q, want %q", i, got, want)
    295 		}
    296 		if got, want := h2.Gname, g.h.Gname; got != want {
    297 			t.Errorf("i=%d: Gname: got %q, want %q", i, got, want)
    298 		}
    299 		if got, want := h2.Linkname, g.h.Linkname; got != want {
    300 			t.Errorf("i=%d: Linkname: got %v, want %v", i, got, want)
    301 		}
    302 		if got, want := h2.Typeflag, g.h.Typeflag; got != want {
    303 			t.Logf("%#v %#v", g.h, fi.Sys())
    304 			t.Errorf("i=%d: Typeflag: got %q, want %q", i, got, want)
    305 		}
    306 		if got, want := h2.Mode, g.h.Mode; got != want {
    307 			t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
    308 		}
    309 		if got, want := fi.Mode(), g.fm; got != want {
    310 			t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want)
    311 		}
    312 		if got, want := h2.AccessTime, g.h.AccessTime; got != want {
    313 			t.Errorf("i=%d: AccessTime: got %v, want %v", i, got, want)
    314 		}
    315 		if got, want := h2.ChangeTime, g.h.ChangeTime; got != want {
    316 			t.Errorf("i=%d: ChangeTime: got %v, want %v", i, got, want)
    317 		}
    318 		if got, want := h2.ModTime, g.h.ModTime; got != want {
    319 			t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
    320 		}
    321 		if sysh, ok := fi.Sys().(*Header); !ok || sysh != g.h {
    322 			t.Errorf("i=%d: Sys didn't return original *Header", i)
    323 		}
    324 	}
    325 }
    326