Adds platform.File.Chown (#1422)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user