Commit f271f928 authored by Vincent Batts's avatar Vincent Batts Committed by Russ Cox

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/6790Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent a1cc84fa
...@@ -139,8 +139,8 @@ func (fi headerFileInfo) Mode() (mode os.FileMode) { ...@@ -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,17 +16,41 @@ func init() { ...@@ -16,17 +16,41 @@ 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.Gid = int(sys.Gid)
// TODO(bradfitz): populate username & group. os/user
// doesn't cache LookupId lookups, and lacks group
// lookup functions.
h.AccessTime = statAtime(sys)
h.ChangeTime = statCtime(sys)
// 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
}
} }
h.Uid = int(sys.Uid)
h.Gid = int(sys.Gid)
// TODO(bradfitz): populate username & group. os/user
// doesn't cache LookupId lookups, and lacks group
// lookup functions.
h.AccessTime = statAtime(sys)
h.ChangeTime = statCtime(sys)
// TODO(bradfitz): major/minor device numbers?
return nil return nil
} }
...@@ -147,17 +147,6 @@ func TestHeaderRoundTrip(t *testing.T) { ...@@ -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) { ...@@ -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) { ...@@ -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)
} }
......
...@@ -147,6 +147,44 @@ var writerTests = []*writerTest{ ...@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment