mirror of
https://github.com/vbatts/tar-split.git
synced 2024-11-15 04:58:36 +00:00
archive/tar: fix round-trip attributes
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>
This commit is contained in:
parent
2e5698249c
commit
b48c28014e
5 changed files with 127 additions and 24 deletions
|
@ -139,8 +139,8 @@ func (fi headerFileInfo) Mode() (mode os.FileMode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch fi.h.Typeflag {
|
switch fi.h.Typeflag {
|
||||||
case TypeLink, TypeSymlink:
|
case TypeSymlink:
|
||||||
// hard link, symbolic link
|
// symbolic link
|
||||||
mode |= os.ModeSymlink
|
mode |= os.ModeSymlink
|
||||||
case TypeChar:
|
case TypeChar:
|
||||||
// character device node
|
// character device node
|
||||||
|
|
|
@ -16,10 +16,8 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func statUnix(fi os.FileInfo, h *Header) error {
|
func statUnix(fi os.FileInfo, h *Header) error {
|
||||||
sys, ok := fi.Sys().(*syscall.Stat_t)
|
switch sys := fi.Sys().(type) {
|
||||||
if !ok {
|
case *syscall.Stat_t:
|
||||||
return nil
|
|
||||||
}
|
|
||||||
h.Uid = int(sys.Uid)
|
h.Uid = int(sys.Uid)
|
||||||
h.Gid = int(sys.Gid)
|
h.Gid = int(sys.Gid)
|
||||||
// TODO(bradfitz): populate username & group. os/user
|
// TODO(bradfitz): populate username & group. os/user
|
||||||
|
@ -28,5 +26,31 @@ func statUnix(fi os.FileInfo, h *Header) error {
|
||||||
h.AccessTime = statAtime(sys)
|
h.AccessTime = statAtime(sys)
|
||||||
h.ChangeTime = statCtime(sys)
|
h.ChangeTime = statCtime(sys)
|
||||||
// TODO(bradfitz): major/minor device numbers?
|
// TODO(bradfitz): major/minor device numbers?
|
||||||
|
if fi.Mode().IsRegular() && sys.Nlink > 1 {
|
||||||
|
h.Typeflag = TypeLink
|
||||||
|
h.Size = 0
|
||||||
|
// TODO(vbatts): Linkname?
|
||||||
|
}
|
||||||
|
case *Header:
|
||||||
|
// for the roundtrip logic
|
||||||
|
h.Uid = sys.Uid
|
||||||
|
h.Gid = sys.Gid
|
||||||
|
h.Uname = sys.Uname
|
||||||
|
h.Gname = sys.Gname
|
||||||
|
h.AccessTime = sys.AccessTime
|
||||||
|
h.ChangeTime = sys.ChangeTime
|
||||||
|
if sys.Xattrs != nil {
|
||||||
|
h.Xattrs = make(map[string]string)
|
||||||
|
for k, v := range sys.Xattrs {
|
||||||
|
h.Xattrs[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sys.Typeflag == TypeLink {
|
||||||
|
// hard link
|
||||||
|
h.Typeflag = TypeLink
|
||||||
|
h.Size = 0
|
||||||
|
h.Linkname = sys.Linkname
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,17 +147,6 @@ func TestHeaderRoundTrip(t *testing.T) {
|
||||||
},
|
},
|
||||||
fm: 0644,
|
fm: 0644,
|
||||||
},
|
},
|
||||||
// hard link.
|
|
||||||
{
|
|
||||||
h: &Header{
|
|
||||||
Name: "hard.txt",
|
|
||||||
Mode: 0644 | c_ISLNK,
|
|
||||||
Size: 0,
|
|
||||||
ModTime: time.Unix(1360600916, 0),
|
|
||||||
Typeflag: TypeLink,
|
|
||||||
},
|
|
||||||
fm: 0644 | os.ModeSymlink,
|
|
||||||
},
|
|
||||||
// symbolic link.
|
// symbolic link.
|
||||||
{
|
{
|
||||||
h: &Header{
|
h: &Header{
|
||||||
|
@ -246,6 +235,33 @@ func TestHeaderRoundTrip(t *testing.T) {
|
||||||
},
|
},
|
||||||
fm: 0600 | os.ModeSticky,
|
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 {
|
for i, g := range golden {
|
||||||
|
@ -268,12 +284,37 @@ func TestHeaderRoundTrip(t *testing.T) {
|
||||||
if got, want := h2.Size, g.h.Size; got != want {
|
if got, want := h2.Size, g.h.Size; got != want {
|
||||||
t.Errorf("i=%d: Size: got %v, want %v", i, 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 {
|
if got, want := h2.Mode, g.h.Mode; got != want {
|
||||||
t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
|
t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
|
||||||
}
|
}
|
||||||
if got, want := fi.Mode(), g.fm; got != want {
|
if got, want := fi.Mode(), g.fm; got != want {
|
||||||
t.Errorf("i=%d: fi.Mode: got %o, want %o", i, 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 {
|
if got, want := h2.ModTime, g.h.ModTime; got != want {
|
||||||
t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
|
t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
|
||||||
}
|
}
|
||||||
|
|
BIN
archive/tar/testdata/hardlink.tar
vendored
Normal file
BIN
archive/tar/testdata/hardlink.tar
vendored
Normal file
Binary file not shown.
|
@ -147,6 +147,44 @@ var writerTests = []*writerTest{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// This file was produced using gnu tar 1.26
|
||||||
|
// echo "Slartibartfast" > file.txt
|
||||||
|
// ln file.txt hard.txt
|
||||||
|
// tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt
|
||||||
|
{
|
||||||
|
file: "testdata/hardlink.tar",
|
||||||
|
entries: []*writerTestEntry{
|
||||||
|
{
|
||||||
|
header: &Header{
|
||||||
|
Name: "file.txt",
|
||||||
|
Mode: 0644,
|
||||||
|
Uid: 1000,
|
||||||
|
Gid: 100,
|
||||||
|
Size: 15,
|
||||||
|
ModTime: time.Unix(1425484303, 0),
|
||||||
|
Typeflag: '0',
|
||||||
|
Uname: "vbatts",
|
||||||
|
Gname: "users",
|
||||||
|
},
|
||||||
|
contents: "Slartibartfast\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: &Header{
|
||||||
|
Name: "hard.txt",
|
||||||
|
Mode: 0644,
|
||||||
|
Uid: 1000,
|
||||||
|
Gid: 100,
|
||||||
|
Size: 0,
|
||||||
|
ModTime: time.Unix(1425484303, 0),
|
||||||
|
Typeflag: '1',
|
||||||
|
Linkname: "file.txt",
|
||||||
|
Uname: "vbatts",
|
||||||
|
Gname: "users",
|
||||||
|
},
|
||||||
|
// no contents
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render byte array in a two-character hexadecimal string, spaced for easy visual inspection.
|
// Render byte array in a two-character hexadecimal string, spaced for easy visual inspection.
|
||||||
|
|
Loading…
Reference in a new issue