pkg os, method (*Root) Chmod(string, fs.FileMode) error #67002
pkg os, method (*Root) Chown(string, int, int) error #67002
pkg os, method (*Root) Chtimes(string, time.Time, time.Time) error #67002
+pkg os, method (*Root) Lchown(string, int, int) error #67002
* [os.Root.Chmod]
* [os.Root.Chown]
* [os.Root.Chtimes]
+ * [os.Root.Lchown]
return rootChown(r, name, uid, gid)
}
+// Lchown changes the numeric uid and gid of the named file in the root.
+// See [Lchown] for more details.
+func (r *Root) Lchown(name string, uid, gid int) error {
+ return rootLchown(r, name, uid, gid)
+}
+
// Chtimes changes the access and modification times of the named file in the root.
// See [Chtimes] for more details.
func (r *Root) Chtimes(name string, atime time.Time, mtime time.Time) error {
return nil
}
+func rootLchown(r *Root, name string, uid, gid int) error {
+ if err := checkPathEscapesLstat(r, name); err != nil {
+ return &PathError{Op: "lchownat", Path: name, Err: err}
+ }
+ if err := Lchown(joinPath(r.root.name, name), uid, gid); err != nil {
+ return &PathError{Op: "lchownat", Path: name, Err: underlyingError(err)}
+ }
+ return nil
+}
+
func rootChtimes(r *Root, name string, atime time.Time, mtime time.Time) error {
if err := checkPathEscapes(r, name); err != nil {
return &PathError{Op: "chtimesat", Path: name, Err: err}
return nil
}
+func rootLchown(r *Root, name string, uid, gid int) error {
+ _, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) {
+ return struct{}{}, lchownat(parent, name, uid, gid)
+ })
+ if err != nil {
+ return &PathError{Op: "lchownat", Path: name, Err: err}
+ }
+ return err
+}
+
func rootChtimes(r *Root, name string, atime time.Time, mtime time.Time) error {
_, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) {
return struct{}{}, chtimesat(parent, name, atime, mtime)
})
}
+func lchownat(parent int, name string, uid, gid int) error {
+ return ignoringEINTR(func() error {
+ return unix.Fchownat(parent, name, uid, gid, unix.AT_SYMLINK_NOFOLLOW)
+ })
+}
+
func chtimesat(parent int, name string, atime time.Time, mtime time.Time) error {
return afterResolvingSymlink(parent, name, func() error {
return ignoringEINTR(func() error {
import (
"fmt"
"os"
+ "path/filepath"
"runtime"
"syscall"
"testing"
}
}
+func TestRootLchown(t *testing.T) {
+ if runtime.GOOS == "wasip1" {
+ t.Skip("Lchown not supported on " + runtime.GOOS)
+ }
+
+ // Look up the current default uid/gid.
+ f := newFile(t)
+ dir, err := f.Stat()
+ if err != nil {
+ t.Fatal(err)
+ }
+ sys := dir.Sys().(*syscall.Stat_t)
+
+ groups, err := os.Getgroups()
+ if err != nil {
+ t.Fatalf("getgroups: %v", err)
+ }
+ groups = append(groups, os.Getgid())
+ for _, test := range rootTestCases {
+ test.run(t, func(t *testing.T, target string, root *os.Root) {
+ wantError := test.wantError
+ if test.ltarget != "" {
+ wantError = false
+ target = filepath.Join(root.Name(), test.ltarget)
+ } else if target != "" {
+ if err := os.WriteFile(target, nil, 0o666); err != nil {
+ t.Fatal(err)
+ }
+ }
+ for _, gid := range groups {
+ err := root.Lchown(test.open, -1, gid)
+ if errEndsTest(t, err, wantError, "root.Lchown(%q, -1, %v)", test.open, gid) {
+ return
+ }
+ checkUidGid(t, target, int(sys.Uid), gid)
+ }
+ })
+ }
+}
+
func TestRootConsistencyChown(t *testing.T) {
if runtime.GOOS == "wasip1" {
t.Skip("Chown not supported on " + runtime.GOOS)
})
}
}
+
+func TestRootConsistencyLchown(t *testing.T) {
+ if runtime.GOOS == "wasip1" {
+ t.Skip("Lchown not supported on " + runtime.GOOS)
+ }
+ groups, err := os.Getgroups()
+ if err != nil {
+ t.Fatalf("getgroups: %v", err)
+ }
+ var gid int
+ if len(groups) == 0 {
+ gid = os.Getgid()
+ } else {
+ gid = groups[0]
+ }
+ for _, test := range rootConsistencyTestCases {
+ test.run(t, func(t *testing.T, path string, r *os.Root) (string, error) {
+ lchown := os.Lchown
+ lstat := os.Lstat
+ if r != nil {
+ lchown = r.Lchown
+ lstat = r.Lstat
+ }
+ err := lchown(path, -1, gid)
+ if err != nil {
+ return "", err
+ }
+ fi, err := lstat(path)
+ if err != nil {
+ return "", err
+ }
+ sys := fi.Sys().(*syscall.Stat_t)
+ return fmt.Sprintf("%v %v", sys.Uid, sys.Gid), nil
+ })
+ }
+}
return syscall.EWINDOWS // matches syscall.Chown
}
+func lchownat(parent syscall.Handle, name string, uid, gid int) error {
+ return syscall.EWINDOWS // matches syscall.Lchown
+}
+
func mkdirat(dirfd syscall.Handle, name string, perm FileMode) error {
return windows.Mkdirat(dirfd, name, syscallMode(perm))
}