summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2017-08-28 16:25:56 -0700
committerJoe Tsai <thebrokentoaster@gmail.com>2017-08-30 00:52:31 +0000
commit0564e304a6ea394a42929060c588469dbd6f32af (patch)
tree0a06f26deea10ff5ac8388a016ca9eb508c8e419
parent93da0b6e66f24c4c307e0df37ceb102a33306174 (diff)
downloadgo-git-0564e304a6ea394a42929060c588469dbd6f32af.tar.gz
archive/tar: populate uname/gname/devmajor/devminor in FileInfoHeader
We take a best-effort approach since information for these fields are not well supported on all platforms. user.LookupId+user.LookupGroupId is currently 15x slower than os.Stat. For performance reasons, we perpetually cache username and groupname with a sync.Map. As a result, this function will not be updated whenever the user or group names are renamed in the OS. However, this is a better situation than before, where those fields were not populated at all. Change-Id: I3cec8291aed7675dea89ee1cbda92bd493c8831f Reviewed-on: https://go-review.googlesource.com/59531 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
-rw-r--r--src/archive/tar/stat_unix.go45
-rw-r--r--src/go/build/deps_test.go2
2 files changed, 42 insertions, 5 deletions
diff --git a/src/archive/tar/stat_unix.go b/src/archive/tar/stat_unix.go
index cb843db4cf..aa6d82b1c6 100644
--- a/src/archive/tar/stat_unix.go
+++ b/src/archive/tar/stat_unix.go
@@ -8,6 +8,10 @@ package tar
import (
"os"
+ "os/user"
+ "runtime"
+ "strconv"
+ "sync"
"syscall"
)
@@ -15,6 +19,10 @@ func init() {
sysStat = statUnix
}
+// userMap and groupMap caches UID and GID lookups for performance reasons.
+// The downside is that renaming uname or gname by the OS never takes effect.
+var userMap, groupMap sync.Map // map[int]string
+
func statUnix(fi os.FileInfo, h *Header) error {
sys, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
@@ -22,11 +30,40 @@ func statUnix(fi os.FileInfo, h *Header) error {
}
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.
+
+ // Best effort at populating Uname and Gname.
+ // The os/user functions may fail for any number of reasons
+ // (not implemented on that platform, cgo not enabled, etc).
+ if u, ok := userMap.Load(h.Uid); ok {
+ h.Uname = u.(string)
+ } else if u, err := user.LookupId(strconv.Itoa(h.Uid)); err == nil {
+ h.Uname = u.Username
+ userMap.Store(h.Uid, h.Uname)
+ }
+ if g, ok := groupMap.Load(h.Gid); ok {
+ h.Gname = g.(string)
+ } else if g, err := user.LookupGroupId(strconv.Itoa(h.Gid)); err == nil {
+ h.Gname = g.Name
+ groupMap.Store(h.Gid, h.Gname)
+ }
+
h.AccessTime = statAtime(sys)
h.ChangeTime = statCtime(sys)
- // TODO(bradfitz): major/minor device numbers?
+
+ // Best effort at populating Devmajor and Devminor.
+ if h.Typeflag == TypeChar || h.Typeflag == TypeBlock {
+ dev := uint64(sys.Rdev) // May be int32 or uint32
+ switch runtime.GOOS {
+ case "linux":
+ // Copied from golang.org/x/sys/unix/dev_linux.go.
+ major := uint32((dev & 0x00000000000fff00) >> 8)
+ major |= uint32((dev & 0xfffff00000000000) >> 32)
+ minor := uint32((dev & 0x00000000000000ff) >> 0)
+ minor |= uint32((dev & 0x00000ffffff00000) >> 12)
+ h.Devmajor, h.Devminor = int64(major), int64(minor)
+ default:
+ // TODO: Implement others (see https://golang.org/issue/8106)
+ }
+ }
return nil
}
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index 87abfba921..041c52310a 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -226,7 +226,7 @@ var pkgDeps = map[string][]string{
"go/types": {"L4", "GOPARSER", "container/heap", "go/constant"},
// One of a kind.
- "archive/tar": {"L4", "OS", "syscall"},
+ "archive/tar": {"L4", "OS", "syscall", "os/user"},
"archive/zip": {"L4", "OS", "compress/flate"},
"container/heap": {"sort"},
"compress/bzip2": {"L4"},