1// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build aix hurd linux darwin dragonfly freebsd openbsd netbsd solaris
6
7package tar
8
9import (
10	"io/fs"
11	"os/user"
12	"runtime"
13	"strconv"
14	"sync"
15	"syscall"
16)
17
18func init() {
19	sysStat = statUnix
20}
21
22// userMap and groupMap caches UID and GID lookups for performance reasons.
23// The downside is that renaming uname or gname by the OS never takes effect.
24var userMap, groupMap sync.Map // map[int]string
25
26func statUnix(fi fs.FileInfo, h *Header) error {
27	sys, ok := fi.Sys().(*syscall.Stat_t)
28	if !ok {
29		return nil
30	}
31	h.Uid = int(sys.Uid)
32	h.Gid = int(sys.Gid)
33
34	// Best effort at populating Uname and Gname.
35	// The os/user functions may fail for any number of reasons
36	// (not implemented on that platform, cgo not enabled, etc).
37	if u, ok := userMap.Load(h.Uid); ok {
38		h.Uname = u.(string)
39	} else if u, err := user.LookupId(strconv.Itoa(h.Uid)); err == nil {
40		h.Uname = u.Username
41		userMap.Store(h.Uid, h.Uname)
42	}
43	if g, ok := groupMap.Load(h.Gid); ok {
44		h.Gname = g.(string)
45	} else if g, err := user.LookupGroupId(strconv.Itoa(h.Gid)); err == nil {
46		h.Gname = g.Name
47		groupMap.Store(h.Gid, h.Gname)
48	}
49
50	h.AccessTime = statAtime(sys)
51	h.ChangeTime = statCtime(sys)
52
53	// Best effort at populating Devmajor and Devminor.
54	if h.Typeflag == TypeChar || h.Typeflag == TypeBlock {
55		dev := uint64(sys.Rdev) // May be int32 or uint32
56		switch runtime.GOOS {
57		case "aix":
58			var major, minor uint32
59			if runtime.GOARCH == "ppc64" {
60				major = uint32((dev & 0x3fffffff00000000) >> 32)
61				minor = uint32((dev & 0x00000000ffffffff) >> 0)
62			} else {
63				major = uint32((dev >> 16) & 0xffff)
64				minor = uint32(dev & 0xffff)
65			}
66			h.Devmajor, h.Devminor = int64(major), int64(minor)
67		case "linux":
68			// Copied from golang.org/x/sys/unix/dev_linux.go.
69			major := uint32((dev & 0x00000000000fff00) >> 8)
70			major |= uint32((dev & 0xfffff00000000000) >> 32)
71			minor := uint32((dev & 0x00000000000000ff) >> 0)
72			minor |= uint32((dev & 0x00000ffffff00000) >> 12)
73			h.Devmajor, h.Devminor = int64(major), int64(minor)
74		case "darwin", "ios":
75			// Copied from golang.org/x/sys/unix/dev_darwin.go.
76			major := uint32((dev >> 24) & 0xff)
77			minor := uint32(dev & 0xffffff)
78			h.Devmajor, h.Devminor = int64(major), int64(minor)
79		case "dragonfly":
80			// Copied from golang.org/x/sys/unix/dev_dragonfly.go.
81			major := uint32((dev >> 8) & 0xff)
82			minor := uint32(dev & 0xffff00ff)
83			h.Devmajor, h.Devminor = int64(major), int64(minor)
84		case "freebsd":
85			// Copied from golang.org/x/sys/unix/dev_freebsd.go.
86			major := uint32((dev >> 8) & 0xff)
87			minor := uint32(dev & 0xffff00ff)
88			h.Devmajor, h.Devminor = int64(major), int64(minor)
89		case "netbsd":
90			// Copied from golang.org/x/sys/unix/dev_netbsd.go.
91			major := uint32((dev & 0x000fff00) >> 8)
92			minor := uint32((dev & 0x000000ff) >> 0)
93			minor |= uint32((dev & 0xfff00000) >> 12)
94			h.Devmajor, h.Devminor = int64(major), int64(minor)
95		case "openbsd":
96			// Copied from golang.org/x/sys/unix/dev_openbsd.go.
97			major := uint32((dev & 0x0000ff00) >> 8)
98			minor := uint32((dev & 0x000000ff) >> 0)
99			minor |= uint32((dev & 0xffff0000) >> 8)
100			h.Devmajor, h.Devminor = int64(major), int64(minor)
101		default:
102			// TODO: Implement solaris (see https://golang.org/issue/8106)
103		}
104	}
105	return nil
106}
107