os: implement and smoketest os.Chmod

Этот коммит содержится в:
Dan Kegel 2021-12-04 13:50:32 -08:00 коммит произвёл Ron Evans
родитель e668c8c1a7
коммит be7bbba4ca
4 изменённых файлов: 106 добавлений и 16 удалений

Просмотреть файл

@ -1,5 +1,9 @@
// +build !baremetal,!js // +build !baremetal,!js
// Portions copyright 2009 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.
package os package os
import ( import (
@ -133,6 +137,48 @@ func (f unixFileHandle) Close() error {
return handleSyscallError(syscall.Close(syscallFd(f))) return handleSyscallError(syscall.Close(syscallFd(f)))
} }
// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
// If there is an error, it will be of type *PathError.
//
// A different subset of the mode bits are used, depending on the
// operating system.
//
// On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and
// ModeSticky are used.
//
// On Windows, only the 0200 bit (owner writable) of mode is used; it
// controls whether the file's read-only attribute is set or cleared.
// The other bits are currently unused. For compatibility with Go 1.12
// and earlier, use a non-zero mode. Use mode 0400 for a read-only
// file and 0600 for a readable+writable file.
func Chmod(name string, mode FileMode) error {
longName := fixLongPath(name)
e := ignoringEINTR(func() error {
return syscall.Chmod(longName, syscallMode(mode))
})
if e != nil {
return &PathError{Op: "chmod", 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.
// Also #20400 and #36644 are issues in which a signal handler is
// installed without setting SA_RESTART. None of these are the common case,
// but there are enough of them that it seems that we can't avoid
// an EINTR loop.
func ignoringEINTR(fn func() error) error {
for {
err := fn()
if err != syscall.EINTR {
return err
}
}
}
// handleSyscallError converts syscall errors into regular os package errors. // handleSyscallError converts syscall errors into regular os package errors.
// The err parameter must be either nil or of type syscall.Errno. // The err parameter must be either nil or of type syscall.Errno.
func handleSyscallError(err error) error { func handleSyscallError(err error) error {
@ -148,3 +194,19 @@ func handleSyscallError(err error) error {
return err return err
} }
} }
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
func syscallMode(i FileMode) (o uint32) {
o |= uint32(i.Perm())
if i&ModeSetuid != 0 {
o |= syscall.S_ISUID
}
if i&ModeSetgid != 0 {
o |= syscall.S_ISGID
}
if i&ModeSticky != 0 {
o |= syscall.S_ISVTX
}
// No mapping for Go's ModeTemporary (plan9 only).
return
}

Просмотреть файл

@ -13,6 +13,11 @@ import (
type syscallFd = int type syscallFd = int
// fixLongPath is a noop on non-Windows platforms.
func fixLongPath(path string) string {
return path
}
func Pipe() (r *File, w *File, err error) { func Pipe() (r *File, w *File, err error) {
var p [2]int var p [2]int
err = handleSyscallError(syscall.Pipe2(p[:], syscall.O_CLOEXEC)) err = handleSyscallError(syscall.Pipe2(p[:], syscall.O_CLOEXEC))
@ -50,19 +55,3 @@ func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) {
} }
return return
} }
// 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.
// Also #20400 and #36644 are issues in which a signal handler is
// installed without setting SA_RESTART. None of these are the common case,
// but there are enough of them that it seems that we can't avoid
// an EINTR loop.
func ignoringEINTR(fn func() error) error {
for {
err := fn()
if err != syscall.EINTR {
return err
}
}
}

Просмотреть файл

@ -25,6 +25,32 @@ func newFile(testName string, t *testing.T) (f *File) {
return return
} }
func checkMode(t *testing.T, path string, mode FileMode) {
dir, err := Stat(path)
if err != nil {
t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
}
if dir.Mode()&ModePerm != mode {
t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode)
}
}
func TestChmod(t *testing.T) {
f := newFile("TestChmod", t)
defer Remove(f.Name())
defer f.Close()
// Creation mode is read write
fm := FileMode(0456)
if runtime.GOOS == "windows" {
fm = FileMode(0444) // read-only file
}
if err := Chmod(f.Name(), fm); err != nil {
t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
}
checkMode(t, f.Name(), fm)
}
func TestReadAt(t *testing.T) { func TestReadAt(t *testing.T) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
t.Log("TODO: implement Pread for Windows") t.Log("TODO: implement Pread for Windows")

Просмотреть файл

@ -68,6 +68,15 @@ func Chdir(path string) (err error) {
return return
} }
func Chmod(path string, mode uint32) (err error) {
data := cstring(path)
fail := int(libc_chmod(&data[0], mode))
if fail < 0 {
err = getErrno()
}
return
}
func Mkdir(path string, mode uint32) (err error) { func Mkdir(path string, mode uint32) (err error) {
data := cstring(path) data := cstring(path)
fail := int(libc_mkdir(&data[0], mode)) fail := int(libc_mkdir(&data[0], mode))
@ -211,6 +220,10 @@ func libc_mprotect(addr unsafe.Pointer, len uintptr, prot int32) int32
//export chdir //export chdir
func libc_chdir(pathname *byte) int32 func libc_chdir(pathname *byte) int32
// int chmod(const char *pathname, mode_t mode);
//export chmod
func libc_chmod(pathname *byte, mode uint32) int32
// int mkdir(const char *pathname, mode_t mode); // int mkdir(const char *pathname, mode_t mode);
//export mkdir //export mkdir
func libc_mkdir(pathname *byte, mode uint32) int32 func libc_mkdir(pathname *byte, mode uint32) int32