]> Cypherpunks repositories - gostls13.git/commitdiff
archive/tar: add FileInfoNames interface
authorqiulaidongfeng <2645477756@qq.com>
Fri, 15 Mar 2024 10:40:23 +0000 (10:40 +0000)
committerCherry Mui <cherryyz@google.com>
Fri, 15 Mar 2024 16:01:50 +0000 (16:01 +0000)
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>

api/next/50102.txt [new file with mode: 0644]
doc/next/6-stdlib/99-minor/archive/tar/50102.md [new file with mode: 0644]
src/archive/tar/common.go
src/archive/tar/stat_unix.go
src/archive/tar/tar_test.go

diff --git a/api/next/50102.txt b/api/next/50102.txt
new file mode 100644 (file)
index 0000000..a142c3c
--- /dev/null
@@ -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() (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() (string, error) #50102
diff --git a/doc/next/6-stdlib/99-minor/archive/tar/50102.md b/doc/next/6-stdlib/99-minor/archive/tar/50102.md
new file mode 100644 (file)
index 0000000..be5592b
--- /dev/null
@@ -0,0 +1,3 @@
+If the argument to [`FileInfoHeader`](/archive/tar#FileInfoHeader) implements the new [`FileInfoNames`](/archive/tar#FileInfoNames) interface,
+then the interface methods will be used to set the Uname/Gname of the file header.
+This allows applications to override the system-dependent Uname/Gname lookup.
index 4910908f81e64f9dbf746dc862fa8bc902b9adac..16ba53e94d651dbb9f227c951939fcf6d3e7d225 100644 (file)
@@ -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 {
index 0f3428bc24b47d3683ace47bd38bd0548f7d5dd7..f999f56db6c915bbc4357e6fb21d703962860597 100644 (file)
@@ -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)
 
index a476f5eb010f21d7cc4559bf860a83aa516a0553..7398e7602ab4d836afbdddfaec8467f1db1d229d 100644 (file)
@@ -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")
+       }
+}