Adds platform.File.Chown (#1422)

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-05-01 18:32:13 +08:00
committed by GitHub
parent 493fe2d410
commit 1047ddee78
6 changed files with 67 additions and 35 deletions

View File

@@ -543,7 +543,7 @@ func (jsfsFchown) invoke(ctx context.Context, mod api.Module, args ...interface{
if f, ok := fsc.LookupFile(fd); !ok {
errno = syscall.EBADF
} else {
errno = platform.ChownFile(f.File.File(), int(uid), int(gid))
errno = f.File.Chown(int(uid), int(gid))
}
return jsfsInvoke(ctx, mod, callback, errno)

View File

@@ -1,7 +1,6 @@
package platform
import (
"io/fs"
"os"
"syscall"
)
@@ -27,15 +26,3 @@ func Lchown(path string, uid, gid int) syscall.Errno {
err := os.Lchown(path, uid, gid)
return UnwrapOSError(err)
}
// ChownFile is like syscall.Fchown, but for nanosecond precision and
// fs.File instead of a file descriptor. This returns syscall.EBADF if the file
// or directory was closed. See https://linux.die.net/man/3/fchown
//
// Note: This always returns syscall.ENOSYS on windows.
func ChownFile(f fs.File, uid, gid int) syscall.Errno {
if f, ok := f.(fdFile); ok {
return fchown(f.Fd(), uid, gid)
}
return syscall.ENOSYS
}

View File

@@ -60,7 +60,7 @@ func TestChown(t *testing.T) {
})
}
func TestChownFile(t *testing.T) {
func TestDefaultFileChown(t *testing.T) {
tmpDir := t.TempDir()
dir := path.Join(tmpDir, "dir")
@@ -81,12 +81,12 @@ func TestChownFile(t *testing.T) {
require.NoError(t, err)
t.Run("-1 parameters means leave alone", func(t *testing.T) {
require.Zero(t, ChownFile(dirF.File(), -1, -1))
require.Zero(t, dirF.Chown(-1, -1))
checkUidGid(t, dir, dirSys.Uid, dirSys.Gid)
})
t.Run("change gid, but not uid", func(t *testing.T) {
require.Zero(t, ChownFile(dirF.File(), -1, gid))
require.Zero(t, dirF.Chown(-1, gid))
checkUidGid(t, dir, dirSys.Uid, uint32(gid))
})
@@ -94,8 +94,8 @@ func TestChownFile(t *testing.T) {
for _, g := range groups {
g := g
t.Run(fmt.Sprintf("change to gid %d", g), func(t *testing.T) {
// Test using our ChownFile
require.Zero(t, ChownFile(dirF.File(), -1, g))
// Test using our Chown
require.Zero(t, dirF.Chown(-1, g))
checkUidGid(t, dir, dirSys.Uid, uint32(g))
// Revert back with os.File.Chown
@@ -106,7 +106,7 @@ func TestChownFile(t *testing.T) {
t.Run("closed", func(t *testing.T) {
require.Zero(t, dirF.Close())
require.EqualErrno(t, syscall.EBADF, ChownFile(dirF.File(), -1, gid))
require.EqualErrno(t, syscall.EBADF, dirF.Chown(-1, gid))
})
}

View File

@@ -36,11 +36,27 @@ type File interface {
//
// # Notes
//
// - This is like `fstatat` with `AT_FDCWD` in POSIX. See
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html
// - An fs.FileInfo backed implementation sets atim, mtim and ctim to the
// same value.
// - Windows allows you to stat a closed directory.
Stat() (Stat_t, syscall.Errno)
// Chown is like syscall.Fchown, but for nanosecond precision.
//
// # Errors
//
// The following errors are expected:
// - syscall.EBADF if the file or directory was closed.
//
// # Notes
//
// - This is like `fchown` in POSIX. See
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html
// - This always returns syscall.ENOSYS on windows.
Chown(uid, gid int) syscall.Errno
// Close closes the underlying file.
Close() syscall.Errno
@@ -57,6 +73,11 @@ func (UnimplementedFile) Stat() (Stat_t, syscall.Errno) {
return Stat_t{}, syscall.ENOSYS
}
// Chown implements File.Chown
func (UnimplementedFile) Chown() syscall.Errno {
return syscall.ENOSYS
}
type DefaultFile struct {
F fs.File
}
@@ -70,6 +91,14 @@ func (f *DefaultFile) Stat() (Stat_t, syscall.Errno) {
return st, errno
}
// Chown implements File.Chown
func (f *DefaultFile) Chown(uid, gid int) syscall.Errno {
if f, ok := f.F.(fdFile); ok {
return fchown(f.Fd(), uid, gid)
}
return syscall.ENOSYS
}
// Close implements File.Close
func (f *DefaultFile) Close() syscall.Errno {
return UnwrapOSError(f.F.Close())

View File

@@ -162,35 +162,52 @@ type lazyDir struct {
// Stat implements the same method as documented on platform.File
func (r *lazyDir) Stat() (platform.Stat_t, syscall.Errno) {
if f, err := r.file(); err != 0 {
return platform.Stat_t{}, err
if f, ok := r.file(); !ok {
return platform.Stat_t{}, syscall.EBADF
} else {
return f.Stat()
}
}
// Chown implements the same method as documented on platform.File
func (r *lazyDir) Chown(uid, gid int) syscall.Errno {
if f, ok := r.file(); !ok {
return syscall.EBADF
} else {
return f.Chown(uid, gid)
}
}
// File implements the same method as documented on platform.File
func (r *lazyDir) File() fs.File {
if f, err := r.file(); err != 0 {
panic(err) // Bad, but temporary
if f, ok := r.file(); !ok {
panic("path doesn't exist")
} else {
return f.File()
}
}
func (r *lazyDir) file() (f platform.File, errno syscall.Errno) {
if f = r.f; r.f != nil {
return
// file returns the underlying file or false if it doesn't exist.
func (r *lazyDir) file() (platform.File, bool) {
if f := r.f; r.f != nil {
return f, true
}
var errno syscall.Errno
r.f, errno = r.fs.OpenFile(".", os.O_RDONLY, 0)
f = r.f
return
switch errno {
case 0:
return r.f, true
case syscall.ENOENT:
return nil, false
default:
panic(errno) // unexpected
}
}
// Read implements fs.File
func (r *lazyDir) Read(p []byte) (n int, err error) {
if f, errno := r.file(); errno != 0 {
return 0, errno
if f, ok := r.file(); !ok {
return 0, syscall.EBADF
} else {
return f.File().Read(p)
}
@@ -257,7 +274,7 @@ func (f *FileEntry) CachedStat() (ino uint64, fileType fs.FileMode, errno syscal
func (f *FileEntry) Stat() (st platform.Stat_t, errno syscall.Errno) {
if ld, ok := f.File.(*lazyDir); ok {
var sf platform.File
if sf, errno = ld.file(); errno == 0 {
if sf, ok = ld.file(); ok {
st, errno = sf.Stat()
}
} else {

View File

@@ -9,7 +9,6 @@ import (
"syscall"
"testing"
"github.com/tetratelabs/wazero/internal/platform"
"github.com/tetratelabs/wazero/internal/testing/require"
)
@@ -50,8 +49,8 @@ func TestDirFS_Chown(t *testing.T) {
require.Zero(t, testFS.Chown("dir", -1, g))
checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, uint32(g))
// Revert back with platform.ChownFile
require.Zero(t, platform.ChownFile(dirF.File(), -1, gid))
// Revert back with File.Chown
require.Zero(t, dirF.Chown(-1, gid))
checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, uint32(gid))
})
}