archive/tar: add FileInfoNames interface

An optional interface FileInfoNames has been added.

If the parameter fi of FileInfoHeader implements the interface
the Gname and Uname of the return value Header are
provided by the method of the interface.

Also added testing.

Fixes #50102

Change-Id: I6fd06c7c9aaf29b22b7384542fe57affed33009a

Change-Id: I6fd06c7c9aaf29b22b7384542fe57affed33009a
GitHub-Last-Rev: 5e82257948
GitHub-Pull-Request: golang/go#61662
Reviewed-on: https://go-review.googlesource.com/c/go/+/514235
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
This commit is contained in:
qiulaidongfeng 2023-08-04 05:15:41 +00:00 committed by Gopher Robot
parent a04f5adc3c
commit 834a3f844a
4 changed files with 99 additions and 0 deletions

9
api/next/50102.txt Normal file
View File

@ -0,0 +1,9 @@
pkg archive/tar, type FileInfoNames interface { Gname, IsDir, ModTime, Mode, Name, Size, Sys, Uname } #50102
pkg archive/tar, type FileInfoNames interface, Gname(int) (string, error) #50102
pkg archive/tar, type FileInfoNames interface, IsDir() bool #50102
pkg archive/tar, type FileInfoNames interface, ModTime() time.Time #50102
pkg archive/tar, type FileInfoNames interface, Mode() fs.FileMode #50102
pkg archive/tar, type FileInfoNames interface, Name() string #50102
pkg archive/tar, type FileInfoNames interface, Size() int64 #50102
pkg archive/tar, type FileInfoNames interface, Sys() interface{} #50102
pkg archive/tar, type FileInfoNames interface, Uname(int) (string, error) #50102

View File

@ -614,6 +614,8 @@ func (fi headerFileInfo) String() string {
// sysStat, if non-nil, populates h from system-dependent fields of fi. // 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) error
var loadUidAndGid func(fi fs.FileInfo, uid, gid *int)
const ( const (
// Mode constants from the USTAR spec: // Mode constants from the USTAR spec:
// See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06
@ -639,6 +641,10 @@ const (
// Since fs.FileInfo's Name method only returns the base name of // Since fs.FileInfo's Name method only returns the base name of
// the file it describes, it may be necessary to modify Header.Name // the file it describes, it may be necessary to modify Header.Name
// to provide the full path name of the file. // to provide the full path name of the file.
//
// If fi implements [FileInfoNames]
// the Gname and Uname of the header are
// provided by the methods of the interface.
func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) { func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) {
if fi == nil { if fi == nil {
return nil, errors.New("archive/tar: FileInfo is nil") return nil, errors.New("archive/tar: FileInfo is nil")
@ -711,12 +717,38 @@ func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) {
} }
} }
} }
if iface, ok := fi.(FileInfoNames); ok {
var err error
if loadUidAndGid != nil {
loadUidAndGid(fi, &h.Uid, &h.Gid)
}
h.Gname, err = iface.Gname(h.Gid)
if err != nil {
return nil, err
}
h.Uname, err = iface.Uname(h.Gid)
if err != nil {
return nil, err
}
return h, nil
}
if sysStat != nil { if sysStat != nil {
return h, sysStat(fi, h) return h, sysStat(fi, h)
} }
return h, nil return h, nil
} }
// FileInfoNames extends [FileInfo] to translate UID/GID to names.
// Passing an instance of this to [FileInfoHeader] permits the caller
// to control UID/GID resolution.
type FileInfoNames interface {
fs.FileInfo
// Uname should translate a UID into a user name.
Uname(uid int) (string, error)
// Gname should translate a GID into a group name.
Gname(gid int) (string, error)
}
// isHeaderOnlyType checks if the given type flag is of the type that has no // isHeaderOnlyType checks if the given type flag is of the type that has no
// data section even if a size is specified. // data section even if a size is specified.
func isHeaderOnlyType(flag byte) bool { func isHeaderOnlyType(flag byte) bool {

View File

@ -17,6 +17,7 @@ import (
func init() { func init() {
sysStat = statUnix sysStat = statUnix
loadUidAndGid = loadUidAndGidFunc
} }
// userMap and groupMap caches UID and GID lookups for performance reasons. // userMap and groupMap caches UID and GID lookups for performance reasons.
@ -99,3 +100,12 @@ func statUnix(fi fs.FileInfo, h *Header) error {
} }
return nil return nil
} }
func loadUidAndGidFunc(fi fs.FileInfo, uid, gid *int) {
sys, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return
}
*uid = int(sys.Uid)
*gid = int(sys.Gid)
}

View File

@ -848,3 +848,51 @@ func Benchmark(b *testing.B) {
}) })
} }
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(uid int) (string, error) {
return "Uname", nil
}
func (f *fileInfoNames) Gname(gid int) (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 %v, want %v", header.Uname, "Uname")
}
if header.Gname != "Gname" {
t.Fatalf("header.Gname: got %v, want %v", header.Gname, "Gname")
}
}