]> Cypherpunks repositories - gostls13.git/commitdiff
archive/tar: add FileInfoHeader function
authorBrad Fitzpatrick <bradfitz@golang.org>
Thu, 24 May 2012 21:10:54 +0000 (14:10 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 24 May 2012 21:10:54 +0000 (14:10 -0700)
Fixes #3295

R=adg, rsc, mike.rosset
CC=golang-dev
https://golang.org/cl/5796073

api/next.txt
src/pkg/archive/tar/common.go
src/pkg/archive/tar/stat_atim.go [new file with mode: 0644]
src/pkg/archive/tar/stat_atimespec.go [new file with mode: 0644]
src/pkg/archive/tar/stat_unix.go [new file with mode: 0644]
src/pkg/archive/tar/tar_test.go [new file with mode: 0644]
src/pkg/go/build/deps_test.go

index 1279a7ace902abf4a79738bbd4737b0ce01ea113..5147f995853b3a590cc4630a1131e5f527562460 100644 (file)
@@ -1,3 +1,4 @@
+pkg archive/tar, func FileInfoHeader(os.FileInfo, string) (*Header, error)
 pkg crypto/tls, const TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16
 pkg crypto/tls, const TLS_RSA_WITH_AES_256_CBC_SHA uint16
 pkg crypto/x509, const ECDSA PublicKeyAlgorithm
index fc7a40923cd2a918f22e116184597b96560d76b6..921b9fe9bdd1420186959d82e5383c578c860ec2 100644 (file)
 //   http://www.gnu.org/software/tar/manual/html_node/Standard.html
 package tar
 
-import "time"
+import (
+       "errors"
+       "fmt"
+       "os"
+       "time"
+)
 
 const (
        blockSize = 512
@@ -49,6 +54,62 @@ type Header struct {
        ChangeTime time.Time // status change time
 }
 
+// sysStat, if non-nil, populates h from system-dependent fields of fi.
+var sysStat func(fi os.FileInfo, h *Header) error
+
+// Mode constants from the tar spec.
+const (
+       c_ISDIR  = 040000
+       c_ISFIFO = 010000
+       c_ISREG  = 0100000
+       c_ISLNK  = 0120000
+       c_ISBLK  = 060000
+       c_ISCHR  = 020000
+       c_ISSOCK = 0140000
+)
+
+// FileInfoHeader creates a partially-populated Header from fi.
+// If fi describes a symlink, FileInfoHeader records link as the link target.
+func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
+       if fi == nil {
+               return nil, errors.New("tar: FileInfo is nil")
+       }
+       h := &Header{
+               Name:    fi.Name(),
+               ModTime: fi.ModTime(),
+               Mode:    int64(fi.Mode().Perm()), // or'd with c_IS* constants later
+       }
+       switch {
+       case fi.Mode()&os.ModeType == 0:
+               h.Mode |= c_ISREG
+               h.Typeflag = TypeReg
+               h.Size = fi.Size()
+       case fi.IsDir():
+               h.Typeflag = TypeDir
+               h.Mode |= c_ISDIR
+       case fi.Mode()&os.ModeSymlink != 0:
+               h.Typeflag = TypeSymlink
+               h.Mode |= c_ISLNK
+               h.Linkname = link
+       case fi.Mode()&os.ModeDevice != 0:
+               if fi.Mode()&os.ModeCharDevice != 0 {
+                       h.Mode |= c_ISCHR
+                       h.Typeflag = TypeChar
+               } else {
+                       h.Mode |= c_ISBLK
+                       h.Typeflag = TypeBlock
+               }
+       case fi.Mode()&os.ModeSocket != 0:
+               h.Mode |= c_ISSOCK
+       default:
+               return nil, fmt.Errorf("archive/tar: unknown file mode %v", fi.Mode())
+       }
+       if sysStat != nil {
+               return h, sysStat(fi, h)
+       }
+       return h, nil
+}
+
 var zeroBlock = make([]byte, blockSize)
 
 // POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
diff --git a/src/pkg/archive/tar/stat_atim.go b/src/pkg/archive/tar/stat_atim.go
new file mode 100644 (file)
index 0000000..6029b08
--- /dev/null
@@ -0,0 +1,20 @@
+// 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.
+
+// +build linux openbsd
+
+package tar
+
+import (
+       "syscall"
+       "time"
+)
+
+func statAtime(st *syscall.Stat_t) time.Time {
+       return time.Unix(st.Atim.Unix())
+}
+
+func statCtime(st *syscall.Stat_t) time.Time {
+       return time.Unix(st.Ctim.Unix())
+}
diff --git a/src/pkg/archive/tar/stat_atimespec.go b/src/pkg/archive/tar/stat_atimespec.go
new file mode 100644 (file)
index 0000000..6f17dbe
--- /dev/null
@@ -0,0 +1,20 @@
+// 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.
+
+// +build darwin freebsd netbsd
+
+package tar
+
+import (
+       "syscall"
+       "time"
+)
+
+func statAtime(st *syscall.Stat_t) time.Time {
+       return time.Unix(st.Atimespec.Unix())
+}
+
+func statCtime(st *syscall.Stat_t) time.Time {
+       return time.Unix(st.Ctimespec.Unix())
+}
diff --git a/src/pkg/archive/tar/stat_unix.go b/src/pkg/archive/tar/stat_unix.go
new file mode 100644 (file)
index 0000000..92bc924
--- /dev/null
@@ -0,0 +1,32 @@
+// 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.
+
+// +build linux darwin freebsd openbsd netbsd
+
+package tar
+
+import (
+       "os"
+       "syscall"
+)
+
+func init() {
+       sysStat = statUnix
+}
+
+func statUnix(fi os.FileInfo, h *Header) error {
+       sys, ok := fi.Sys().(*syscall.Stat_t)
+       if !ok {
+               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?
+       return nil
+}
diff --git a/src/pkg/archive/tar/tar_test.go b/src/pkg/archive/tar/tar_test.go
new file mode 100644 (file)
index 0000000..d3d2a62
--- /dev/null
@@ -0,0 +1,56 @@
+// 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 (
+       "os"
+       "testing"
+       "time"
+)
+
+func TestFileInfoHeader(t *testing.T) {
+       fi, err := os.Lstat("testdata/small.txt")
+       if err != nil {
+               t.Fatal(err)
+       }
+       h, err := FileInfoHeader(fi, "")
+       if err != nil {
+               t.Fatalf("on small.txt: %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(0644|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)
+       }
+}
+
+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 }
index 4e9f32a036ff15aa9749d6b214ac6105c191a6b6..0505a4304e848703c68d37337f9da00de63fc534 100644 (file)
@@ -177,7 +177,7 @@ var pkgDeps = map[string][]string{
        },
 
        // One of a kind.
-       "archive/tar":         {"L4", "OS"},
+       "archive/tar":         {"L4", "OS", "syscall"},
        "archive/zip":         {"L4", "OS", "compress/flate"},
        "compress/bzip2":      {"L4"},
        "compress/flate":      {"L4"},