aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorleongross <[email protected]>2024-07-21 10:56:55 +0200
committerGitHub <[email protected]>2024-07-21 10:56:55 +0200
commit3e2230eadb14d0e54db621976d60d654487f891a (patch)
tree6d7a89b45511ff57a5954c6e8e70c307cfd2b794
parent4f908f4877f8ff571002df1e260e338c7c3c8fe0 (diff)
downloadtinygo-3e2230eadb14d0e54db621976d60d654487f891a.tar.gz
tinygo-3e2230eadb14d0e54db621976d60d654487f891a.zip
os/Chown (#4213)
src/os, src/syscall: add os.Chown
-rw-r--r--src/os/file_anyos.go14
-rw-r--r--src/os/os_chmod_test.go41
-rw-r--r--src/syscall/syscall_libc.go14
3 files changed, 69 insertions, 0 deletions
diff --git a/src/os/file_anyos.go b/src/os/file_anyos.go
index 1483f11c2..da70d7284 100644
--- a/src/os/file_anyos.go
+++ b/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.
diff --git a/src/os/os_chmod_test.go b/src/os/os_chmod_test.go
index 3db6467ad..ad151abb0 100644
--- a/src/os/os_chmod_test.go
+++ b/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)
+ }
+ }
}
diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go
index eed18cbd9..07a8acb0b 100644
--- a/src/syscall/syscall_libc.go
+++ b/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