mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
os: add Root.Chown
For #67002 Change-Id: I546537618cbe32217fa72264d49db2b1a1d3b6db Reviewed-on: https://go-review.googlesource.com/c/go/+/648295 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
parent
187fd2698d
commit
807a51b391
@ -1 +1,2 @@
|
||||
pkg os, method (*Root) Chmod(string, fs.FileMode) error #67002
|
||||
pkg os, method (*Root) Chown(string, int, int) error #67002
|
||||
|
@ -1,3 +1,4 @@
|
||||
The [os.Root] type supports the following additional methods:
|
||||
|
||||
* [os.Root.Chmod]
|
||||
* [os.Root.Chown]
|
||||
|
@ -26,3 +26,4 @@ TEXT ·libc_faccessat_trampoline(SB),NOSPLIT,$0-0; JMP libc_faccessat(SB)
|
||||
TEXT ·libc_readlinkat_trampoline(SB),NOSPLIT,$0-0; JMP libc_readlinkat(SB)
|
||||
TEXT ·libc_mkdirat_trampoline(SB),NOSPLIT,$0-0; JMP libc_mkdirat(SB)
|
||||
TEXT ·libc_fchmodat_trampoline(SB),NOSPLIT,$0-0; JMP libc_fchmodat(SB)
|
||||
TEXT ·libc_fchownat_trampoline(SB),NOSPLIT,$0-0; JMP libc_fchownat(SB)
|
||||
|
@ -16,3 +16,5 @@ TEXT ·libc_mkdirat_trampoline(SB),NOSPLIT,$0-0
|
||||
JMP libc_mkdirat(SB)
|
||||
TEXT ·libc_fchmodat_trampoline(SB),NOSPLIT,$0-0
|
||||
JMP libc_fchmodat(SB)
|
||||
TEXT ·libc_fchownat_trampoline(SB),NOSPLIT,$0-0
|
||||
JMP libc_fchownat(SB)
|
||||
|
@ -96,3 +96,21 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
|
||||
p, err := syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, errno := syscall.Syscall6(fchownatTrap,
|
||||
uintptr(dirfd),
|
||||
uintptr(unsafe.Pointer(p)),
|
||||
uintptr(uid),
|
||||
uintptr(gid),
|
||||
uintptr(flags),
|
||||
0)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package unix
|
||||
|
||||
//go:cgo_import_dynamic libc_fchmodat fchmodat "libc.a/shr_64.o"
|
||||
//go:cgo_import_dynamic libc_fchownat fchownat "libc.a/shr_64.o"
|
||||
//go:cgo_import_dynamic libc_fstatat fstatat "libc.a/shr_64.o"
|
||||
//go:cgo_import_dynamic libc_openat openat "libc.a/shr_64.o"
|
||||
//go:cgo_import_dynamic libc_unlinkat unlinkat "libc.a/shr_64.o"
|
||||
|
@ -80,3 +80,25 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func libc_fchownat_trampoline()
|
||||
|
||||
//go:cgo_import_dynamic libc_fchownat fchownat "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
|
||||
p, err := syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_fchownat_trampoline),
|
||||
uintptr(dirfd),
|
||||
uintptr(unsafe.Pointer(p)),
|
||||
uintptr(uid),
|
||||
uintptr(gid),
|
||||
uintptr(flags),
|
||||
0)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
//go:linkname procReadlinkat libc_readlinkat
|
||||
//go:linkname procMkdirat libc_mkdirat
|
||||
//go:linkname procFchmodat libc_fchmodat
|
||||
//go:linkname procFchownat libc_chownat
|
||||
|
||||
var (
|
||||
procFstatat,
|
||||
@ -24,7 +25,8 @@ var (
|
||||
procUnlinkat,
|
||||
procReadlinkat,
|
||||
procMkdirat,
|
||||
procFchmodat uintptr
|
||||
procFchmodat,
|
||||
procFchownat uintptr
|
||||
)
|
||||
|
||||
func Unlinkat(dirfd int, path string, flags int) error {
|
||||
@ -126,3 +128,21 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
|
||||
p, err := syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, errno := syscall6(uintptr(unsafe.Pointer(&procFchownat)), 4,
|
||||
uintptr(dirfd),
|
||||
uintptr(unsafe.Pointer(p)),
|
||||
uintptr(uid),
|
||||
uintptr(gid),
|
||||
uintptr(flags),
|
||||
0)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -71,3 +71,25 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:cgo_import_dynamic libc_fchownat fchownat "libc.so"
|
||||
|
||||
func libc_fchownat_trampoline()
|
||||
|
||||
func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
|
||||
p, err := syscall.BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_fchmodat_trampoline),
|
||||
uintptr(dirfd),
|
||||
uintptr(unsafe.Pointer(p)),
|
||||
uintptr(uid),
|
||||
uintptr(gid),
|
||||
uintptr(flags),
|
||||
0)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, e
|
||||
|
||||
//go:cgo_import_dynamic libc_faccessat faccessat "libc.so"
|
||||
//go:cgo_import_dynamic libc_fchmodat fchmodat "libc.so"
|
||||
//go:cgo_import_dynamic libc_fchownat fchownat "libc.so"
|
||||
//go:cgo_import_dynamic libc_fstatat fstatat "libc.so"
|
||||
//go:cgo_import_dynamic libc_openat openat "libc.so"
|
||||
//go:cgo_import_dynamic libc_unlinkat unlinkat "libc.so"
|
||||
|
@ -13,6 +13,7 @@ const (
|
||||
readlinkatTrap uintptr = syscall.SYS_READLINKAT
|
||||
mkdiratTrap uintptr = syscall.SYS_MKDIRAT
|
||||
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
|
||||
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
|
||||
|
||||
AT_EACCESS = 0x4
|
||||
AT_FDCWD = 0xfffafdcd
|
||||
|
@ -20,4 +20,5 @@ const (
|
||||
readlinkatTrap uintptr = syscall.SYS_READLINKAT
|
||||
mkdiratTrap uintptr = syscall.SYS_MKDIRAT
|
||||
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
|
||||
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
|
||||
)
|
||||
|
@ -12,6 +12,7 @@ const (
|
||||
readlinkatTrap uintptr = syscall.SYS_READLINKAT
|
||||
mkdiratTrap uintptr = syscall.SYS_MKDIRAT
|
||||
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
|
||||
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -13,6 +13,7 @@ const (
|
||||
readlinkatTrap uintptr = syscall.SYS_READLINKAT
|
||||
mkdiratTrap uintptr = syscall.SYS_MKDIRAT
|
||||
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
|
||||
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -106,6 +106,11 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
|
||||
return syscall.ENOSYS
|
||||
}
|
||||
|
||||
func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
|
||||
// WASI preview 1 doesn't support changing file ownership.
|
||||
return syscall.ENOSYS
|
||||
}
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_create_directory
|
||||
//go:noescape
|
||||
func path_create_directory(fd int32, path *byte, pathLen size) syscall.Errno
|
||||
|
@ -151,6 +151,12 @@ func (r *Root) Mkdir(name string, perm FileMode) error {
|
||||
return rootMkdir(r, name, perm)
|
||||
}
|
||||
|
||||
// Chown changes the numeric uid and gid of the named file in the root.
|
||||
// See [Chown] for more details.
|
||||
func (r *Root) Chown(name string, uid, gid int) error {
|
||||
return rootChown(r, name, uid, gid)
|
||||
}
|
||||
|
||||
// Remove removes the named file or (empty) directory in the root.
|
||||
// See [Remove] for more details.
|
||||
func (r *Root) Remove(name string) error {
|
||||
|
@ -105,6 +105,16 @@ func rootChmod(r *Root, name string, mode FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func rootChown(r *Root, name string, uid, gid int) error {
|
||||
if err := checkPathEscapes(r, name); err != nil {
|
||||
return &PathError{Op: "chownat", Path: name, Err: err}
|
||||
}
|
||||
if err := Chown(joinPath(r.root.name, name), uid, gid); err != nil {
|
||||
return &PathError{Op: "chownat", Path: name, Err: underlyingError(err)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func rootMkdir(r *Root, name string, perm FileMode) error {
|
||||
if err := checkPathEscapes(r, name); err != nil {
|
||||
return &PathError{Op: "mkdirat", Path: name, Err: err}
|
||||
|
@ -77,6 +77,16 @@ func rootChmod(r *Root, name string, mode FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func rootChown(r *Root, name string, uid, gid int) error {
|
||||
_, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) {
|
||||
return struct{}{}, chownat(parent, name, uid, gid)
|
||||
})
|
||||
if err != nil {
|
||||
return &PathError{Op: "chownat", Path: name, Err: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func rootMkdir(r *Root, name string, perm FileMode) error {
|
||||
_, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) {
|
||||
return struct{}{}, mkdirat(parent, name, perm)
|
||||
|
@ -157,6 +157,14 @@ func chmodat(parent int, name string, mode FileMode) error {
|
||||
})
|
||||
}
|
||||
|
||||
func chownat(parent int, name string, uid, gid int) error {
|
||||
return afterResolvingSymlink(parent, name, func() error {
|
||||
return ignoringEINTR(func() error {
|
||||
return unix.Fchownat(parent, name, uid, gid, unix.AT_SYMLINK_NOFOLLOW)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func mkdirat(fd int, name string, perm FileMode) error {
|
||||
return ignoringEINTR(func() error {
|
||||
return unix.Mkdirat(fd, name, syscallMode(perm))
|
||||
|
87
src/os/root_unix_test.go
Normal file
87
src/os/root_unix_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build unix || (js && wasm) || wasip1
|
||||
|
||||
package os_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRootChown(t *testing.T) {
|
||||
if runtime.GOOS == "wasip1" {
|
||||
t.Skip("Chown 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) {
|
||||
if target != "" {
|
||||
if err := os.WriteFile(target, nil, 0o666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, gid := range groups {
|
||||
err := root.Chown(test.open, -1, gid)
|
||||
if errEndsTest(t, err, test.wantError, "root.Chown(%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)
|
||||
}
|
||||
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) {
|
||||
chown := os.Chown
|
||||
lstat := os.Lstat
|
||||
if r != nil {
|
||||
chown = r.Chown
|
||||
lstat = r.Lstat
|
||||
}
|
||||
err := chown(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
|
||||
})
|
||||
}
|
||||
}
|
@ -276,6 +276,10 @@ func chmodat(parent syscall.Handle, name string, mode FileMode) error {
|
||||
return windows.SetFileInformationByHandle(h, windows.FileBasicInfo, unsafe.Pointer(&fbi), uint32(unsafe.Sizeof(fbi)))
|
||||
}
|
||||
|
||||
func chownat(parent syscall.Handle, name string, uid, gid int) error {
|
||||
return syscall.EWINDOWS // matches syscall.Chown
|
||||
}
|
||||
|
||||
func mkdirat(dirfd syscall.Handle, name string, perm FileMode) error {
|
||||
return windows.Mkdirat(dirfd, name, syscallMode(perm))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user