]> Cypherpunks repositories - gostls13.git/commitdiff
archive/tar: fix round-trip attributes
authorVincent Batts <vbatts@hashbangbash.com>
Wed, 4 Mar 2015 17:29:16 +0000 (12:29 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 26 Jun 2015 15:51:06 +0000 (15:51 +0000)
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>
src/archive/tar/common.go
src/archive/tar/stat_unix.go
src/archive/tar/tar_test.go
src/archive/tar/testdata/hardlink.tar [new file with mode: 0644]
src/archive/tar/writer_test.go

index e363aa793e0f32410366a702b46c75ec621b2059..855e5fc4e6a850f0da63a4648589a6daec151d3a 100644 (file)
@@ -139,8 +139,8 @@ func (fi headerFileInfo) Mode() (mode os.FileMode) {
        }
 
        switch fi.h.Typeflag {
-       case TypeLink, TypeSymlink:
-               // hard link, symbolic link
+       case TypeSymlink:
+               // symbolic link
                mode |= os.ModeSymlink
        case TypeChar:
                // character device node
index cb843db4cfd65815830128cc0aa285dee36f7123..24b93111dc201931240e8310bee16b984588bddc 100644 (file)
@@ -16,17 +16,41 @@ func init() {
 }
 
 func statUnix(fi os.FileInfo, h *Header) error {
-       sys, ok := fi.Sys().(*syscall.Stat_t)
-       if !ok {
-               return nil
+       switch sys := fi.Sys().(type) {
+       case *syscall.Stat_t:
+               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
 }
index ed333f3ea4f146318c63547d8f8c5efc15749c77..d63c072eb9aaa489c5ec76552db060fa50f2a3ef 100644 (file)
@@ -147,17 +147,6 @@ func TestHeaderRoundTrip(t *testing.T) {
                        },
                        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.
                {
                        h: &Header{
@@ -246,6 +235,33 @@ func TestHeaderRoundTrip(t *testing.T) {
                        },
                        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 {
@@ -268,12 +284,37 @@ func TestHeaderRoundTrip(t *testing.T) {
                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)
                }
diff --git a/src/archive/tar/testdata/hardlink.tar b/src/archive/tar/testdata/hardlink.tar
new file mode 100644 (file)
index 0000000..9cd1a26
Binary files /dev/null and b/src/archive/tar/testdata/hardlink.tar differ
index 650899a10e85f2e189713a69442a3d52358a1c16..fe46a67ce38b1ce4c5943553b146ffb3b2e5e395 100644 (file)
@@ -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.