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 	vectors := []headerRoundTripTest{{
    139 		// regular file.
    140 		h: &Header{
    141 			Name:     "test.txt",
    142 			Mode:     0644 | c_ISREG,
    143 			Size:     12,
    144 			ModTime:  time.Unix(1360600916, 0),
    145 			Typeflag: TypeReg,
    146 		},
    147 		fm: 0644,
    148 	}, {
    149 		// symbolic link.
    150 		h: &Header{
    151 			Name:     "link.txt",
    152 			Mode:     0777 | c_ISLNK,
    153 			Size:     0,
    154 			ModTime:  time.Unix(1360600852, 0),
    155 			Typeflag: TypeSymlink,
    156 		},
    157 		fm: 0777 | os.ModeSymlink,
    158 	}, {
    159 		// character device node.
    160 		h: &Header{
    161 			Name:     "dev/null",
    162 			Mode:     0666 | c_ISCHR,
    163 			Size:     0,
    164 			ModTime:  time.Unix(1360578951, 0),
    165 			Typeflag: TypeChar,
    166 		},
    167 		fm: 0666 | os.ModeDevice | os.ModeCharDevice,
    168 	}, {
    169 		// block device node.
    170 		h: &Header{
    171 			Name:     "dev/sda",
    172 			Mode:     0660 | c_ISBLK,
    173 			Size:     0,
    174 			ModTime:  time.Unix(1360578954, 0),
    175 			Typeflag: TypeBlock,
    176 		},
    177 		fm: 0660 | os.ModeDevice,
    178 	}, {
    179 		// directory.
    180 		h: &Header{
    181 			Name:     "dir/",
    182 			Mode:     0755 | c_ISDIR,
    183 			Size:     0,
    184 			ModTime:  time.Unix(1360601116, 0),
    185 			Typeflag: TypeDir,
    186 		},
    187 		fm: 0755 | os.ModeDir,
    188 	}, {
    189 		// fifo node.
    190 		h: &Header{
    191 			Name:     "dev/initctl",
    192 			Mode:     0600 | c_ISFIFO,
    193 			Size:     0,
    194 			ModTime:  time.Unix(1360578949, 0),
    195 			Typeflag: TypeFifo,
    196 		},
    197 		fm: 0600 | os.ModeNamedPipe,
    198 	}, {
    199 		// setuid.
    200 		h: &Header{
    201 			Name:     "bin/su",
    202 			Mode:     0755 | c_ISREG | c_ISUID,
    203 			Size:     23232,
    204 			ModTime:  time.Unix(1355405093, 0),
    205 			Typeflag: TypeReg,
    206 		},
    207 		fm: 0755 | os.ModeSetuid,
    208 	}, {
    209 		// setguid.
    210 		h: &Header{
    211 			Name:     "group.txt",
    212 			Mode:     0750 | c_ISREG | c_ISGID,
    213 			Size:     0,
    214 			ModTime:  time.Unix(1360602346, 0),
    215 			Typeflag: TypeReg,
    216 		},
    217 		fm: 0750 | os.ModeSetgid,
    218 	}, {
    219 		// sticky.
    220 		h: &Header{
    221 			Name:     "sticky.txt",
    222 			Mode:     0600 | c_ISREG | c_ISVTX,
    223 			Size:     7,
    224 			ModTime:  time.Unix(1360602540, 0),
    225 			Typeflag: TypeReg,
    226 		},
    227 		fm: 0600 | os.ModeSticky,
    228 	}, {
    229 		// hard link.
    230 		h: &Header{
    231 			Name:     "hard.txt",
    232 			Mode:     0644 | c_ISREG,
    233 			Size:     0,
    234 			Linkname: "file.txt",
    235 			ModTime:  time.Unix(1360600916, 0),
    236 			Typeflag: TypeLink,
    237 		},
    238 		fm: 0644,
    239 	}, {
    240 		// More information.
    241 		h: &Header{
    242 			Name:     "info.txt",
    243 			Mode:     0600 | c_ISREG,
    244 			Size:     0,
    245 			Uid:      1000,
    246 			Gid:      1000,
    247 			ModTime:  time.Unix(1360602540, 0),
    248 			Uname:    "slartibartfast",
    249 			Gname:    "users",
    250 			Typeflag: TypeReg,
    251 		},
    252 		fm: 0600,
    253 	}}
    254 
    255 	for i, v := range vectors {
    256 		fi := v.h.FileInfo()
    257 		h2, err := FileInfoHeader(fi, "")
    258 		if err != nil {
    259 			t.Error(err)
    260 			continue
    261 		}
    262 		if strings.Contains(fi.Name(), "/") {
    263 			t.Errorf("FileInfo of %q contains slash: %q", v.h.Name, fi.Name())
    264 		}
    265 		name := path.Base(v.h.Name)
    266 		if fi.IsDir() {
    267 			name += "/"
    268 		}
    269 		if got, want := h2.Name, name; got != want {
    270 			t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
    271 		}
    272 		if got, want := h2.Size, v.h.Size; got != want {
    273 			t.Errorf("i=%d: Size: got %v, want %v", i, got, want)
    274 		}
    275 		if got, want := h2.Uid, v.h.Uid; got != want {
    276 			t.Errorf("i=%d: Uid: got %d, want %d", i, got, want)
    277 		}
    278 		if got, want := h2.Gid, v.h.Gid; got != want {
    279 			t.Errorf("i=%d: Gid: got %d, want %d", i, got, want)
    280 		}
    281 		if got, want := h2.Uname, v.h.Uname; got != want {
    282 			t.Errorf("i=%d: Uname: got %q, want %q", i, got, want)
    283 		}
    284 		if got, want := h2.Gname, v.h.Gname; got != want {
    285 			t.Errorf("i=%d: Gname: got %q, want %q", i, got, want)
    286 		}
    287 		if got, want := h2.Linkname, v.h.Linkname; got != want {
    288 			t.Errorf("i=%d: Linkname: got %v, want %v", i, got, want)
    289 		}
    290 		if got, want := h2.Typeflag, v.h.Typeflag; got != want {
    291 			t.Logf("%#v %#v", v.h, fi.Sys())
    292 			t.Errorf("i=%d: Typeflag: got %q, want %q", i, got, want)
    293 		}
    294 		if got, want := h2.Mode, v.h.Mode; got != want {
    295 			t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
    296 		}
    297 		if got, want := fi.Mode(), v.fm; got != want {
    298 			t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want)
    299 		}
    300 		if got, want := h2.AccessTime, v.h.AccessTime; got != want {
    301 			t.Errorf("i=%d: AccessTime: got %v, want %v", i, got, want)
    302 		}
    303 		if got, want := h2.ChangeTime, v.h.ChangeTime; got != want {
    304 			t.Errorf("i=%d: ChangeTime: got %v, want %v", i, got, want)
    305 		}
    306 		if got, want := h2.ModTime, v.h.ModTime; got != want {
    307 			t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
    308 		}
    309 		if sysh, ok := fi.Sys().(*Header); !ok || sysh != v.h {
    310 			t.Errorf("i=%d: Sys didn't return original *Header", i)
    311 		}
    312 	}
    313 }
    314