Browse Source

os/Chown (#4213)

src/os, src/syscall: add os.Chown
pull/4354/head
leongross 4 months ago
committed by GitHub
parent
commit
3e2230eadb
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 14
      src/os/file_anyos.go
  2. 41
      src/os/os_chmod_test.go
  3. 14
      src/syscall/syscall_libc.go

14
src/os/file_anyos.go

@ -147,6 +147,20 @@ func Chmod(name string, mode FileMode) error {
return nil
}
// Chown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link's target.
// A uid or gid of -1 means to not change that value.
// If there is an error, it will be of type *PathError.
func Chown(name string, uid, gid int) error {
e := ignoringEINTR(func() error {
return syscall.Chown(name, uid, gid)
})
if e != nil {
return &PathError{Op: "chown", Path: name, Err: e}
}
return nil
}
// ignoringEINTR makes a function call and repeats it if it returns an
// EINTR error. This appears to be required even though we install all
// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.

41
src/os/os_chmod_test.go

@ -9,12 +9,15 @@
package os_test
import (
"errors"
"io/fs"
. "os"
"runtime"
"testing"
)
func TestChmod(t *testing.T) {
// Chmod
f := newFile("TestChmod", t)
defer Remove(f.Name())
defer f.Close()
@ -28,4 +31,42 @@ func TestChmod(t *testing.T) {
t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
}
checkMode(t, f.Name(), fm)
}
// Since testing syscalls requires a static, predictable environment that has to be controlled
// by the CI, we don't test for success but for failures and verify that the error messages are as expected.
// EACCES is returned when the user does not have the required permissions to change the ownership of the file
// ENOENT is returned when the file does not exist
// ENOTDIR is returned when the file is not a directory
func TestChownErr(t *testing.T) {
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
t.Log("skipping on " + runtime.GOOS)
return
}
var (
TEST_UID_ROOT = 0
TEST_GID_ROOT = 0
)
f := newFile("TestChown", t)
defer Remove(f.Name())
defer f.Close()
// EACCES
if err := Chown(f.Name(), TEST_UID_ROOT, TEST_GID_ROOT); err != nil {
errCmp := fs.PathError{Op: "chown", Path: f.Name(), Err: errors.New("operation not permitted")}
if errors.Is(err, &errCmp) {
t.Fatalf("chown(%s, uid=%v, gid=%v): got '%v', want 'operation not permitted'", f.Name(), TEST_UID_ROOT, TEST_GID_ROOT, err)
}
}
// ENOENT
if err := Chown("invalid", Geteuid(), Getgid()); err != nil {
errCmp := fs.PathError{Op: "chown", Path: "invalid", Err: errors.New("no such file or directory")}
if errors.Is(err, &errCmp) {
t.Fatalf("chown(%s, uid=%v, gid=%v): got '%v', want 'no such file or directory'", f.Name(), Geteuid(), Getegid(), err)
}
}
}

14
src/syscall/syscall_libc.go

@ -163,6 +163,15 @@ func Unlink(path string) (err error) {
return
}
func Chown(path string, uid, gid int) (err error) {
data := cstring(path)
fail := int(libc_chown(&data[0], uid, gid))
if fail < 0 {
err = getErrno()
}
return
}
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error)
func Kill(pid int, sig Signal) (err error) {
@ -357,6 +366,11 @@ func libc_chdir(pathname *byte) int32
//export chmod
func libc_chmod(pathname *byte, mode uint32) int32
// int chown(const char *pathname, uid_t owner, gid_t group);
//
//export chown
func libc_chown(pathname *byte, owner, group int) int32
// int mkdir(const char *pathname, mode_t mode);
//
//export mkdir

Loading…
Cancel
Save