diff options
author | qiulaidongfeng <2645477756@qq.com> | 2024-03-15 10:40:23 +0000 |
---|---|---|
committer | Cherry Mui <cherryyz@google.com> | 2024-03-15 16:01:50 +0000 |
commit | d838e4dcdf89124ed051e1c53e8472e900664a6b (patch) | |
tree | 391b301a77fd1a5579cbe8bbcf59815dcd293a1d /src/archive | |
parent | 49204af37efb819bf22f720f27adadfb9b6238fc (diff) | |
download | go-d838e4dcdf89124ed051e1c53e8472e900664a6b.tar.gz go-d838e4dcdf89124ed051e1c53e8472e900664a6b.zip |
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: a213613c79e150d52a2f5c84dca7a49fe123fa40
GitHub-Pull-Request: golang/go#65273
Reviewed-on: https://go-review.googlesource.com/c/go/+/558355
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/archive')
-rw-r--r-- | src/archive/tar/common.go | 32 | ||||
-rw-r--r-- | src/archive/tar/stat_unix.go | 34 | ||||
-rw-r--r-- | src/archive/tar/tar_test.go | 50 |
3 files changed, 97 insertions, 19 deletions
diff --git a/src/archive/tar/common.go b/src/archive/tar/common.go index 4910908f81..16ba53e94d 100644 --- a/src/archive/tar/common.go +++ b/src/archive/tar/common.go @@ -612,7 +612,7 @@ func (fi headerFileInfo) String() string { } // sysStat, if non-nil, populates h from system-dependent fields of fi. -var sysStat func(fi fs.FileInfo, h *Header) error +var sysStat func(fi fs.FileInfo, h *Header, doNameLookups bool) error const ( // Mode constants from the USTAR spec: @@ -639,6 +639,10 @@ const ( // Since fs.FileInfo's Name method only returns the base name of // the file it describes, it may be necessary to modify Header.Name // to provide the full path name of the file. +// +// If fi implements [FileInfoNames] +// Header.Gname and Header.Uname +// are provided by the methods of the interface. func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) { if fi == nil { return nil, errors.New("archive/tar: FileInfo is nil") @@ -711,12 +715,36 @@ func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) { } } } + var doNameLookups = true + if iface, ok := fi.(FileInfoNames); ok { + doNameLookups = false + var err error + h.Gname, err = iface.Gname() + if err != nil { + return nil, err + } + h.Uname, err = iface.Uname() + if err != nil { + return nil, err + } + } if sysStat != nil { - return h, sysStat(fi, h) + return h, sysStat(fi, h, doNameLookups) } return h, nil } +// FileInfoNames extends [fs.FileInfo]. +// Passing an instance of this to [FileInfoHeader] permits the caller +// to avoid a system-dependent name lookup by specifying the Uname and Gname directly. +type FileInfoNames interface { + fs.FileInfo + // Uname should give a user name. + Uname() (string, error) + // Gname should give a group name. + Gname() (string, error) +} + // isHeaderOnlyType checks if the given type flag is of the type that has no // data section even if a size is specified. func isHeaderOnlyType(flag byte) bool { diff --git a/src/archive/tar/stat_unix.go b/src/archive/tar/stat_unix.go index 0f3428bc24..f999f56db6 100644 --- a/src/archive/tar/stat_unix.go +++ b/src/archive/tar/stat_unix.go @@ -23,30 +23,30 @@ func init() { // 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 fs.FileInfo, h *Header) error { +func statUnix(fi fs.FileInfo, h *Header, doNameLookups bool) error { sys, ok := fi.Sys().(*syscall.Stat_t) if !ok { return nil } h.Uid = int(sys.Uid) h.Gid = int(sys.Gid) - - // 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) + if doNameLookups { + // 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) diff --git a/src/archive/tar/tar_test.go b/src/archive/tar/tar_test.go index a476f5eb01..7398e7602a 100644 --- a/src/archive/tar/tar_test.go +++ b/src/archive/tar/tar_test.go @@ -848,3 +848,53 @@ func Benchmark(b *testing.B) { }) } + +var _ fileInfoNames = fileInfoNames{} + +type fileInfoNames struct{} + +func (f *fileInfoNames) Name() string { + return "tmp" +} + +func (f *fileInfoNames) Size() int64 { + return 0 +} + +func (f *fileInfoNames) Mode() fs.FileMode { + return 0777 +} + +func (f *fileInfoNames) ModTime() time.Time { + return time.Time{} +} + +func (f *fileInfoNames) IsDir() bool { + return false +} + +func (f *fileInfoNames) Sys() any { + return nil +} + +func (f *fileInfoNames) Uname() (string, error) { + return "Uname", nil +} + +func (f *fileInfoNames) Gname() (string, error) { + return "Gname", nil +} + +func TestFileInfoHeaderUseFileInfoNames(t *testing.T) { + info := &fileInfoNames{} + header, err := FileInfoHeader(info, "") + if err != nil { + t.Fatal(err) + } + if header.Uname != "Uname" { + t.Fatalf("header.Uname: got %s, want %s", header.Uname, "Uname") + } + if header.Gname != "Gname" { + t.Fatalf("header.Gname: got %s, want %s", header.Gname, "Gname") + } +} |