Skip to content

Commit d838e4d

Browse files
qiulaidongfengcherrymui
authored andcommitted
archive/tar: add FileInfoNames interface
An optional interface FileInfoNames has been added. If the parameter fi of FileInfoHeader implements the interface the Gname/Uname of the return value Header are provided by the method of the interface. Also added testing. Fixes #50102 Change-Id: I47976e238eb20ed43113b060e4f83a14ae49493e GitHub-Last-Rev: a213613 GitHub-Pull-Request: #65273 Reviewed-on: https://go-review.googlesource.com/c/go/+/558355 Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 49204af commit d838e4d

File tree

5 files changed

+109
-19
lines changed

5 files changed

+109
-19
lines changed

api/next/50102.txt

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pkg archive/tar, type FileInfoNames interface { Gname, IsDir, ModTime, Mode, Name, Size, Sys, Uname } #50102
2+
pkg archive/tar, type FileInfoNames interface, Gname() (string, error) #50102
3+
pkg archive/tar, type FileInfoNames interface, IsDir() bool #50102
4+
pkg archive/tar, type FileInfoNames interface, ModTime() time.Time #50102
5+
pkg archive/tar, type FileInfoNames interface, Mode() fs.FileMode #50102
6+
pkg archive/tar, type FileInfoNames interface, Name() string #50102
7+
pkg archive/tar, type FileInfoNames interface, Size() int64 #50102
8+
pkg archive/tar, type FileInfoNames interface, Sys() interface{} #50102
9+
pkg archive/tar, type FileInfoNames interface, Uname() (string, error) #50102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
If the argument to [`FileInfoHeader`](/archive/tar#FileInfoHeader) implements the new [`FileInfoNames`](/archive/tar#FileInfoNames) interface,
2+
then the interface methods will be used to set the Uname/Gname of the file header.
3+
This allows applications to override the system-dependent Uname/Gname lookup.

src/archive/tar/common.go

+30-2
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ func (fi headerFileInfo) String() string {
612612
}
613613

614614
// sysStat, if non-nil, populates h from system-dependent fields of fi.
615-
var sysStat func(fi fs.FileInfo, h *Header) error
615+
var sysStat func(fi fs.FileInfo, h *Header, doNameLookups bool) error
616616

617617
const (
618618
// Mode constants from the USTAR spec:
@@ -639,6 +639,10 @@ const (
639639
// Since fs.FileInfo's Name method only returns the base name of
640640
// the file it describes, it may be necessary to modify Header.Name
641641
// to provide the full path name of the file.
642+
//
643+
// If fi implements [FileInfoNames]
644+
// Header.Gname and Header.Uname
645+
// are provided by the methods of the interface.
642646
func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) {
643647
if fi == nil {
644648
return nil, errors.New("archive/tar: FileInfo is nil")
@@ -711,12 +715,36 @@ func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) {
711715
}
712716
}
713717
}
718+
var doNameLookups = true
719+
if iface, ok := fi.(FileInfoNames); ok {
720+
doNameLookups = false
721+
var err error
722+
h.Gname, err = iface.Gname()
723+
if err != nil {
724+
return nil, err
725+
}
726+
h.Uname, err = iface.Uname()
727+
if err != nil {
728+
return nil, err
729+
}
730+
}
714731
if sysStat != nil {
715-
return h, sysStat(fi, h)
732+
return h, sysStat(fi, h, doNameLookups)
716733
}
717734
return h, nil
718735
}
719736

737+
// FileInfoNames extends [fs.FileInfo].
738+
// Passing an instance of this to [FileInfoHeader] permits the caller
739+
// to avoid a system-dependent name lookup by specifying the Uname and Gname directly.
740+
type FileInfoNames interface {
741+
fs.FileInfo
742+
// Uname should give a user name.
743+
Uname() (string, error)
744+
// Gname should give a group name.
745+
Gname() (string, error)
746+
}
747+
720748
// isHeaderOnlyType checks if the given type flag is of the type that has no
721749
// data section even if a size is specified.
722750
func isHeaderOnlyType(flag byte) bool {

src/archive/tar/stat_unix.go

+17-17
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,30 @@ func init() {
2323
// The downside is that renaming uname or gname by the OS never takes effect.
2424
var userMap, groupMap sync.Map // map[int]string
2525

26-
func statUnix(fi fs.FileInfo, h *Header) error {
26+
func statUnix(fi fs.FileInfo, h *Header, doNameLookups bool) error {
2727
sys, ok := fi.Sys().(*syscall.Stat_t)
2828
if !ok {
2929
return nil
3030
}
3131
h.Uid = int(sys.Uid)
3232
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)
33+
if doNameLookups {
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+
}
4849
}
49-
5050
h.AccessTime = statAtime(sys)
5151
h.ChangeTime = statCtime(sys)
5252

src/archive/tar/tar_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -848,3 +848,53 @@ func Benchmark(b *testing.B) {
848848
})
849849

850850
}
851+
852+
var _ fileInfoNames = fileInfoNames{}
853+
854+
type fileInfoNames struct{}
855+
856+
func (f *fileInfoNames) Name() string {
857+
return "tmp"
858+
}
859+
860+
func (f *fileInfoNames) Size() int64 {
861+
return 0
862+
}
863+
864+
func (f *fileInfoNames) Mode() fs.FileMode {
865+
return 0777
866+
}
867+
868+
func (f *fileInfoNames) ModTime() time.Time {
869+
return time.Time{}
870+
}
871+
872+
func (f *fileInfoNames) IsDir() bool {
873+
return false
874+
}
875+
876+
func (f *fileInfoNames) Sys() any {
877+
return nil
878+
}
879+
880+
func (f *fileInfoNames) Uname() (string, error) {
881+
return "Uname", nil
882+
}
883+
884+
func (f *fileInfoNames) Gname() (string, error) {
885+
return "Gname", nil
886+
}
887+
888+
func TestFileInfoHeaderUseFileInfoNames(t *testing.T) {
889+
info := &fileInfoNames{}
890+
header, err := FileInfoHeader(info, "")
891+
if err != nil {
892+
t.Fatal(err)
893+
}
894+
if header.Uname != "Uname" {
895+
t.Fatalf("header.Uname: got %s, want %s", header.Uname, "Uname")
896+
}
897+
if header.Gname != "Gname" {
898+
t.Fatalf("header.Gname: got %s, want %s", header.Gname, "Gname")
899+
}
900+
}

0 commit comments

Comments
 (0)