Skip to content

Commit 3e2230e

Browse files
authored
os/Chown (#4213)
src/os, src/syscall: add os.Chown
1 parent 4f908f4 commit 3e2230e

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

src/os/file_anyos.go

+14
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,20 @@ func Chmod(name string, mode FileMode) error {
147147
return nil
148148
}
149149

150+
// Chown changes the numeric uid and gid of the named file.
151+
// If the file is a symbolic link, it changes the uid and gid of the link's target.
152+
// A uid or gid of -1 means to not change that value.
153+
// If there is an error, it will be of type *PathError.
154+
func Chown(name string, uid, gid int) error {
155+
e := ignoringEINTR(func() error {
156+
return syscall.Chown(name, uid, gid)
157+
})
158+
if e != nil {
159+
return &PathError{Op: "chown", Path: name, Err: e}
160+
}
161+
return nil
162+
}
163+
150164
// ignoringEINTR makes a function call and repeats it if it returns an
151165
// EINTR error. This appears to be required even though we install all
152166
// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.

src/os/os_chmod_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
package os_test
1010

1111
import (
12+
"errors"
13+
"io/fs"
1214
. "os"
1315
"runtime"
1416
"testing"
1517
)
1618

1719
func TestChmod(t *testing.T) {
20+
// Chmod
1821
f := newFile("TestChmod", t)
1922
defer Remove(f.Name())
2023
defer f.Close()
@@ -28,4 +31,42 @@ func TestChmod(t *testing.T) {
2831
t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
2932
}
3033
checkMode(t, f.Name(), fm)
34+
35+
}
36+
37+
// Since testing syscalls requires a static, predictable environment that has to be controlled
38+
// by the CI, we don't test for success but for failures and verify that the error messages are as expected.
39+
// EACCES is returned when the user does not have the required permissions to change the ownership of the file
40+
// ENOENT is returned when the file does not exist
41+
// ENOTDIR is returned when the file is not a directory
42+
func TestChownErr(t *testing.T) {
43+
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
44+
t.Log("skipping on " + runtime.GOOS)
45+
return
46+
}
47+
48+
var (
49+
TEST_UID_ROOT = 0
50+
TEST_GID_ROOT = 0
51+
)
52+
53+
f := newFile("TestChown", t)
54+
defer Remove(f.Name())
55+
defer f.Close()
56+
57+
// EACCES
58+
if err := Chown(f.Name(), TEST_UID_ROOT, TEST_GID_ROOT); err != nil {
59+
errCmp := fs.PathError{Op: "chown", Path: f.Name(), Err: errors.New("operation not permitted")}
60+
if errors.Is(err, &errCmp) {
61+
t.Fatalf("chown(%s, uid=%v, gid=%v): got '%v', want 'operation not permitted'", f.Name(), TEST_UID_ROOT, TEST_GID_ROOT, err)
62+
}
63+
}
64+
65+
// ENOENT
66+
if err := Chown("invalid", Geteuid(), Getgid()); err != nil {
67+
errCmp := fs.PathError{Op: "chown", Path: "invalid", Err: errors.New("no such file or directory")}
68+
if errors.Is(err, &errCmp) {
69+
t.Fatalf("chown(%s, uid=%v, gid=%v): got '%v', want 'no such file or directory'", f.Name(), Geteuid(), Getegid(), err)
70+
}
71+
}
3172
}

src/syscall/syscall_libc.go

+14
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,15 @@ func Unlink(path string) (err error) {
163163
return
164164
}
165165

166+
func Chown(path string, uid, gid int) (err error) {
167+
data := cstring(path)
168+
fail := int(libc_chown(&data[0], uid, gid))
169+
if fail < 0 {
170+
err = getErrno()
171+
}
172+
return
173+
}
174+
166175
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error)
167176

168177
func Kill(pid int, sig Signal) (err error) {
@@ -357,6 +366,11 @@ func libc_chdir(pathname *byte) int32
357366
//export chmod
358367
func libc_chmod(pathname *byte, mode uint32) int32
359368

369+
// int chown(const char *pathname, uid_t owner, gid_t group);
370+
//
371+
//export chown
372+
func libc_chown(pathname *byte, owner, group int) int32
373+
360374
// int mkdir(const char *pathname, mode_t mode);
361375
//
362376
//export mkdir

0 commit comments

Comments
 (0)