forked from mirrors/tar-split
Vincent Batts
b48c28014e
The issue was identified while working with round trip FileInfo of the headers of hardlinks. Also, additional test cases for hard link handling. (review carried over from http://golang.org/cl/165860043) Fixes #9027 Change-Id: I9e3a724c8de72eb1b0fbe0751a7b488894911b76 Reviewed-on: https://go-review.googlesource.com/6790 Reviewed-by: Russ Cox <rsc@golang.org> Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
325 lines
7.8 KiB
Go
325 lines
7.8 KiB
Go
// Copyright 2012 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package tar
|
|
|
|
import (
|
|
"bytes"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestFileInfoHeader(t *testing.T) {
|
|
fi, err := os.Stat("testdata/small.txt")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
h, err := FileInfoHeader(fi, "")
|
|
if err != nil {
|
|
t.Fatalf("FileInfoHeader: %v", err)
|
|
}
|
|
if g, e := h.Name, "small.txt"; g != e {
|
|
t.Errorf("Name = %q; want %q", g, e)
|
|
}
|
|
if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e {
|
|
t.Errorf("Mode = %#o; want %#o", g, e)
|
|
}
|
|
if g, e := h.Size, int64(5); g != e {
|
|
t.Errorf("Size = %v; want %v", g, e)
|
|
}
|
|
if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
|
|
t.Errorf("ModTime = %v; want %v", g, e)
|
|
}
|
|
// FileInfoHeader should error when passing nil FileInfo
|
|
if _, err := FileInfoHeader(nil, ""); err == nil {
|
|
t.Fatalf("Expected error when passing nil to FileInfoHeader")
|
|
}
|
|
}
|
|
|
|
func TestFileInfoHeaderDir(t *testing.T) {
|
|
fi, err := os.Stat("testdata")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
h, err := FileInfoHeader(fi, "")
|
|
if err != nil {
|
|
t.Fatalf("FileInfoHeader: %v", err)
|
|
}
|
|
if g, e := h.Name, "testdata/"; g != e {
|
|
t.Errorf("Name = %q; want %q", g, e)
|
|
}
|
|
// Ignoring c_ISGID for golang.org/issue/4867
|
|
if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm())|c_ISDIR; g != e {
|
|
t.Errorf("Mode = %#o; want %#o", g, e)
|
|
}
|
|
if g, e := h.Size, int64(0); g != e {
|
|
t.Errorf("Size = %v; want %v", g, e)
|
|
}
|
|
if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
|
|
t.Errorf("ModTime = %v; want %v", g, e)
|
|
}
|
|
}
|
|
|
|
func TestFileInfoHeaderSymlink(t *testing.T) {
|
|
h, err := FileInfoHeader(symlink{}, "some-target")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if g, e := h.Name, "some-symlink"; g != e {
|
|
t.Errorf("Name = %q; want %q", g, e)
|
|
}
|
|
if g, e := h.Linkname, "some-target"; g != e {
|
|
t.Errorf("Linkname = %q; want %q", g, e)
|
|
}
|
|
}
|
|
|
|
type symlink struct{}
|
|
|
|
func (symlink) Name() string { return "some-symlink" }
|
|
func (symlink) Size() int64 { return 0 }
|
|
func (symlink) Mode() os.FileMode { return os.ModeSymlink }
|
|
func (symlink) ModTime() time.Time { return time.Time{} }
|
|
func (symlink) IsDir() bool { return false }
|
|
func (symlink) Sys() interface{} { return nil }
|
|
|
|
func TestRoundTrip(t *testing.T) {
|
|
data := []byte("some file contents")
|
|
|
|
var b bytes.Buffer
|
|
tw := NewWriter(&b)
|
|
hdr := &Header{
|
|
Name: "file.txt",
|
|
Uid: 1 << 21, // too big for 8 octal digits
|
|
Size: int64(len(data)),
|
|
ModTime: time.Now(),
|
|
}
|
|
// tar only supports second precision.
|
|
hdr.ModTime = hdr.ModTime.Add(-time.Duration(hdr.ModTime.Nanosecond()) * time.Nanosecond)
|
|
if err := tw.WriteHeader(hdr); err != nil {
|
|
t.Fatalf("tw.WriteHeader: %v", err)
|
|
}
|
|
if _, err := tw.Write(data); err != nil {
|
|
t.Fatalf("tw.Write: %v", err)
|
|
}
|
|
if err := tw.Close(); err != nil {
|
|
t.Fatalf("tw.Close: %v", err)
|
|
}
|
|
|
|
// Read it back.
|
|
tr := NewReader(&b)
|
|
rHdr, err := tr.Next()
|
|
if err != nil {
|
|
t.Fatalf("tr.Next: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(rHdr, hdr) {
|
|
t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr)
|
|
}
|
|
rData, err := ioutil.ReadAll(tr)
|
|
if err != nil {
|
|
t.Fatalf("Read: %v", err)
|
|
}
|
|
if !bytes.Equal(rData, data) {
|
|
t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data)
|
|
}
|
|
}
|
|
|
|
type headerRoundTripTest struct {
|
|
h *Header
|
|
fm os.FileMode
|
|
}
|
|
|
|
func TestHeaderRoundTrip(t *testing.T) {
|
|
golden := []headerRoundTripTest{
|
|
// regular file.
|
|
{
|
|
h: &Header{
|
|
Name: "test.txt",
|
|
Mode: 0644 | c_ISREG,
|
|
Size: 12,
|
|
ModTime: time.Unix(1360600916, 0),
|
|
Typeflag: TypeReg,
|
|
},
|
|
fm: 0644,
|
|
},
|
|
// symbolic link.
|
|
{
|
|
h: &Header{
|
|
Name: "link.txt",
|
|
Mode: 0777 | c_ISLNK,
|
|
Size: 0,
|
|
ModTime: time.Unix(1360600852, 0),
|
|
Typeflag: TypeSymlink,
|
|
},
|
|
fm: 0777 | os.ModeSymlink,
|
|
},
|
|
// character device node.
|
|
{
|
|
h: &Header{
|
|
Name: "dev/null",
|
|
Mode: 0666 | c_ISCHR,
|
|
Size: 0,
|
|
ModTime: time.Unix(1360578951, 0),
|
|
Typeflag: TypeChar,
|
|
},
|
|
fm: 0666 | os.ModeDevice | os.ModeCharDevice,
|
|
},
|
|
// block device node.
|
|
{
|
|
h: &Header{
|
|
Name: "dev/sda",
|
|
Mode: 0660 | c_ISBLK,
|
|
Size: 0,
|
|
ModTime: time.Unix(1360578954, 0),
|
|
Typeflag: TypeBlock,
|
|
},
|
|
fm: 0660 | os.ModeDevice,
|
|
},
|
|
// directory.
|
|
{
|
|
h: &Header{
|
|
Name: "dir/",
|
|
Mode: 0755 | c_ISDIR,
|
|
Size: 0,
|
|
ModTime: time.Unix(1360601116, 0),
|
|
Typeflag: TypeDir,
|
|
},
|
|
fm: 0755 | os.ModeDir,
|
|
},
|
|
// fifo node.
|
|
{
|
|
h: &Header{
|
|
Name: "dev/initctl",
|
|
Mode: 0600 | c_ISFIFO,
|
|
Size: 0,
|
|
ModTime: time.Unix(1360578949, 0),
|
|
Typeflag: TypeFifo,
|
|
},
|
|
fm: 0600 | os.ModeNamedPipe,
|
|
},
|
|
// setuid.
|
|
{
|
|
h: &Header{
|
|
Name: "bin/su",
|
|
Mode: 0755 | c_ISREG | c_ISUID,
|
|
Size: 23232,
|
|
ModTime: time.Unix(1355405093, 0),
|
|
Typeflag: TypeReg,
|
|
},
|
|
fm: 0755 | os.ModeSetuid,
|
|
},
|
|
// setguid.
|
|
{
|
|
h: &Header{
|
|
Name: "group.txt",
|
|
Mode: 0750 | c_ISREG | c_ISGID,
|
|
Size: 0,
|
|
ModTime: time.Unix(1360602346, 0),
|
|
Typeflag: TypeReg,
|
|
},
|
|
fm: 0750 | os.ModeSetgid,
|
|
},
|
|
// sticky.
|
|
{
|
|
h: &Header{
|
|
Name: "sticky.txt",
|
|
Mode: 0600 | c_ISREG | c_ISVTX,
|
|
Size: 7,
|
|
ModTime: time.Unix(1360602540, 0),
|
|
Typeflag: TypeReg,
|
|
},
|
|
fm: 0600 | os.ModeSticky,
|
|
},
|
|
// hard link.
|
|
{
|
|
h: &Header{
|
|
Name: "hard.txt",
|
|
Mode: 0644 | c_ISREG,
|
|
Size: 0,
|
|
Linkname: "file.txt",
|
|
ModTime: time.Unix(1360600916, 0),
|
|
Typeflag: TypeLink,
|
|
},
|
|
fm: 0644,
|
|
},
|
|
// More information.
|
|
{
|
|
h: &Header{
|
|
Name: "info.txt",
|
|
Mode: 0600 | c_ISREG,
|
|
Size: 0,
|
|
Uid: 1000,
|
|
Gid: 1000,
|
|
ModTime: time.Unix(1360602540, 0),
|
|
Uname: "slartibartfast",
|
|
Gname: "users",
|
|
Typeflag: TypeReg,
|
|
},
|
|
fm: 0600,
|
|
},
|
|
}
|
|
|
|
for i, g := range golden {
|
|
fi := g.h.FileInfo()
|
|
h2, err := FileInfoHeader(fi, "")
|
|
if err != nil {
|
|
t.Error(err)
|
|
continue
|
|
}
|
|
if strings.Contains(fi.Name(), "/") {
|
|
t.Errorf("FileInfo of %q contains slash: %q", g.h.Name, fi.Name())
|
|
}
|
|
name := path.Base(g.h.Name)
|
|
if fi.IsDir() {
|
|
name += "/"
|
|
}
|
|
if got, want := h2.Name, name; got != want {
|
|
t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
|
|
}
|
|
if got, want := h2.Size, g.h.Size; got != want {
|
|
t.Errorf("i=%d: Size: got %v, want %v", i, got, want)
|
|
}
|
|
if got, want := h2.Uid, g.h.Uid; got != want {
|
|
t.Errorf("i=%d: Uid: got %d, want %d", i, got, want)
|
|
}
|
|
if got, want := h2.Gid, g.h.Gid; got != want {
|
|
t.Errorf("i=%d: Gid: got %d, want %d", i, got, want)
|
|
}
|
|
if got, want := h2.Uname, g.h.Uname; got != want {
|
|
t.Errorf("i=%d: Uname: got %q, want %q", i, got, want)
|
|
}
|
|
if got, want := h2.Gname, g.h.Gname; got != want {
|
|
t.Errorf("i=%d: Gname: got %q, want %q", i, got, want)
|
|
}
|
|
if got, want := h2.Linkname, g.h.Linkname; got != want {
|
|
t.Errorf("i=%d: Linkname: got %v, want %v", i, got, want)
|
|
}
|
|
if got, want := h2.Typeflag, g.h.Typeflag; got != want {
|
|
t.Logf("%#v %#v", g.h, fi.Sys())
|
|
t.Errorf("i=%d: Typeflag: got %q, want %q", i, got, want)
|
|
}
|
|
if got, want := h2.Mode, g.h.Mode; got != want {
|
|
t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
|
|
}
|
|
if got, want := fi.Mode(), g.fm; got != want {
|
|
t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want)
|
|
}
|
|
if got, want := h2.AccessTime, g.h.AccessTime; got != want {
|
|
t.Errorf("i=%d: AccessTime: got %v, want %v", i, got, want)
|
|
}
|
|
if got, want := h2.ChangeTime, g.h.ChangeTime; got != want {
|
|
t.Errorf("i=%d: ChangeTime: got %v, want %v", i, got, want)
|
|
}
|
|
if got, want := h2.ModTime, g.h.ModTime; got != want {
|
|
t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
|
|
}
|
|
if sysh, ok := fi.Sys().(*Header); !ok || sysh != g.h {
|
|
t.Errorf("i=%d: Sys didn't return original *Header", i)
|
|
}
|
|
}
|
|
}
|