2017-09-22 09:51:05 -04:00
|
|
|
// 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 dragonfly freebsd openbsd netbsd solaris
|
|
|
|
|
|
|
|
package tar
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
2017-11-29 18:52:16 -05:00
|
|
|
"os/user"
|
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"sync"
|
2017-09-22 09:51:05 -04:00
|
|
|
"syscall"
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
sysStat = statUnix
|
|
|
|
}
|
|
|
|
|
2017-11-29 18:52:16 -05:00
|
|
|
// 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
|
|
|
|
|
2017-09-22 09:51:05 -04:00
|
|
|
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)
|
2017-11-29 18:52:16 -05:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2017-09-22 09:51:05 -04:00
|
|
|
h.AccessTime = statAtime(sys)
|
|
|
|
h.ChangeTime = statCtime(sys)
|
2017-11-29 18:52:16 -05:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
case "darwin":
|
|
|
|
// Copied from golang.org/x/sys/unix/dev_darwin.go.
|
|
|
|
major := uint32((dev >> 24) & 0xff)
|
|
|
|
minor := uint32(dev & 0xffffff)
|
|
|
|
h.Devmajor, h.Devminor = int64(major), int64(minor)
|
|
|
|
case "dragonfly":
|
|
|
|
// Copied from golang.org/x/sys/unix/dev_dragonfly.go.
|
|
|
|
major := uint32((dev >> 8) & 0xff)
|
|
|
|
minor := uint32(dev & 0xffff00ff)
|
|
|
|
h.Devmajor, h.Devminor = int64(major), int64(minor)
|
|
|
|
case "freebsd":
|
|
|
|
// Copied from golang.org/x/sys/unix/dev_freebsd.go.
|
|
|
|
major := uint32((dev >> 8) & 0xff)
|
|
|
|
minor := uint32(dev & 0xffff00ff)
|
|
|
|
h.Devmajor, h.Devminor = int64(major), int64(minor)
|
|
|
|
case "netbsd":
|
|
|
|
// Copied from golang.org/x/sys/unix/dev_netbsd.go.
|
|
|
|
major := uint32((dev & 0x000fff00) >> 8)
|
|
|
|
minor := uint32((dev & 0x000000ff) >> 0)
|
|
|
|
minor |= uint32((dev & 0xfff00000) >> 12)
|
|
|
|
h.Devmajor, h.Devminor = int64(major), int64(minor)
|
|
|
|
case "openbsd":
|
|
|
|
// Copied from golang.org/x/sys/unix/dev_openbsd.go.
|
|
|
|
major := uint32((dev & 0x0000ff00) >> 8)
|
|
|
|
minor := uint32((dev & 0x000000ff) >> 0)
|
|
|
|
minor |= uint32((dev & 0xffff0000) >> 8)
|
|
|
|
h.Devmajor, h.Devminor = int64(major), int64(minor)
|
|
|
|
default:
|
|
|
|
// TODO: Implement solaris (see https://golang.org/issue/8106)
|
|
|
|
}
|
|
|
|
}
|
2017-09-22 09:51:05 -04:00
|
|
|
return nil
|
|
|
|
}
|