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 {
|
if f, ok := fsc.LookupFile(fd); !ok {
|
||||||
errno = syscall.EBADF
|
errno = syscall.EBADF
|
||||||
} else {
|
} 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)
|
return jsfsInvoke(ctx, mod, callback, errno)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package platform
|
package platform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
@@ -27,15 +26,3 @@ func Lchown(path string, uid, gid int) syscall.Errno {
|
|||||||
err := os.Lchown(path, uid, gid)
|
err := os.Lchown(path, uid, gid)
|
||||||
return UnwrapOSError(err)
|
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()
|
tmpDir := t.TempDir()
|
||||||
|
|
||||||
dir := path.Join(tmpDir, "dir")
|
dir := path.Join(tmpDir, "dir")
|
||||||
@@ -81,12 +81,12 @@ func TestChownFile(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("-1 parameters means leave alone", func(t *testing.T) {
|
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)
|
checkUidGid(t, dir, dirSys.Uid, dirSys.Gid)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("change gid, but not uid", func(t *testing.T) {
|
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))
|
checkUidGid(t, dir, dirSys.Uid, uint32(gid))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,8 +94,8 @@ func TestChownFile(t *testing.T) {
|
|||||||
for _, g := range groups {
|
for _, g := range groups {
|
||||||
g := g
|
g := g
|
||||||
t.Run(fmt.Sprintf("change to gid %d", g), func(t *testing.T) {
|
t.Run(fmt.Sprintf("change to gid %d", g), func(t *testing.T) {
|
||||||
// Test using our ChownFile
|
// Test using our Chown
|
||||||
require.Zero(t, ChownFile(dirF.File(), -1, g))
|
require.Zero(t, dirF.Chown(-1, g))
|
||||||
checkUidGid(t, dir, dirSys.Uid, uint32(g))
|
checkUidGid(t, dir, dirSys.Uid, uint32(g))
|
||||||
|
|
||||||
// Revert back with os.File.Chown
|
// Revert back with os.File.Chown
|
||||||
@@ -106,7 +106,7 @@ func TestChownFile(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("closed", func(t *testing.T) {
|
t.Run("closed", func(t *testing.T) {
|
||||||
require.Zero(t, dirF.Close())
|
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
|
// # 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
|
// - An fs.FileInfo backed implementation sets atim, mtim and ctim to the
|
||||||
// same value.
|
// same value.
|
||||||
// - Windows allows you to stat a closed directory.
|
// - Windows allows you to stat a closed directory.
|
||||||
Stat() (Stat_t, syscall.Errno)
|
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 closes the underlying file.
|
||||||
Close() syscall.Errno
|
Close() syscall.Errno
|
||||||
|
|
||||||
@@ -57,6 +73,11 @@ func (UnimplementedFile) Stat() (Stat_t, syscall.Errno) {
|
|||||||
return Stat_t{}, syscall.ENOSYS
|
return Stat_t{}, syscall.ENOSYS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chown implements File.Chown
|
||||||
|
func (UnimplementedFile) Chown() syscall.Errno {
|
||||||
|
return syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
type DefaultFile struct {
|
type DefaultFile struct {
|
||||||
F fs.File
|
F fs.File
|
||||||
}
|
}
|
||||||
@@ -70,6 +91,14 @@ func (f *DefaultFile) Stat() (Stat_t, syscall.Errno) {
|
|||||||
return st, 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
|
// Close implements File.Close
|
||||||
func (f *DefaultFile) Close() syscall.Errno {
|
func (f *DefaultFile) Close() syscall.Errno {
|
||||||
return UnwrapOSError(f.F.Close())
|
return UnwrapOSError(f.F.Close())
|
||||||
|
|||||||
@@ -162,35 +162,52 @@ type lazyDir struct {
|
|||||||
|
|
||||||
// Stat implements the same method as documented on platform.File
|
// Stat implements the same method as documented on platform.File
|
||||||
func (r *lazyDir) Stat() (platform.Stat_t, syscall.Errno) {
|
func (r *lazyDir) Stat() (platform.Stat_t, syscall.Errno) {
|
||||||
if f, err := r.file(); err != 0 {
|
if f, ok := r.file(); !ok {
|
||||||
return platform.Stat_t{}, err
|
return platform.Stat_t{}, syscall.EBADF
|
||||||
} else {
|
} else {
|
||||||
return f.Stat()
|
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
|
// File implements the same method as documented on platform.File
|
||||||
func (r *lazyDir) File() fs.File {
|
func (r *lazyDir) File() fs.File {
|
||||||
if f, err := r.file(); err != 0 {
|
if f, ok := r.file(); !ok {
|
||||||
panic(err) // Bad, but temporary
|
panic("path doesn't exist")
|
||||||
} else {
|
} else {
|
||||||
return f.File()
|
return f.File()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *lazyDir) file() (f platform.File, errno syscall.Errno) {
|
// file returns the underlying file or false if it doesn't exist.
|
||||||
if f = r.f; r.f != nil {
|
func (r *lazyDir) file() (platform.File, bool) {
|
||||||
return
|
if f := r.f; r.f != nil {
|
||||||
|
return f, true
|
||||||
}
|
}
|
||||||
|
var errno syscall.Errno
|
||||||
r.f, errno = r.fs.OpenFile(".", os.O_RDONLY, 0)
|
r.f, errno = r.fs.OpenFile(".", os.O_RDONLY, 0)
|
||||||
f = r.f
|
switch errno {
|
||||||
return
|
case 0:
|
||||||
|
return r.f, true
|
||||||
|
case syscall.ENOENT:
|
||||||
|
return nil, false
|
||||||
|
default:
|
||||||
|
panic(errno) // unexpected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read implements fs.File
|
// Read implements fs.File
|
||||||
func (r *lazyDir) Read(p []byte) (n int, err error) {
|
func (r *lazyDir) Read(p []byte) (n int, err error) {
|
||||||
if f, errno := r.file(); errno != 0 {
|
if f, ok := r.file(); !ok {
|
||||||
return 0, errno
|
return 0, syscall.EBADF
|
||||||
} else {
|
} else {
|
||||||
return f.File().Read(p)
|
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) {
|
func (f *FileEntry) Stat() (st platform.Stat_t, errno syscall.Errno) {
|
||||||
if ld, ok := f.File.(*lazyDir); ok {
|
if ld, ok := f.File.(*lazyDir); ok {
|
||||||
var sf platform.File
|
var sf platform.File
|
||||||
if sf, errno = ld.file(); errno == 0 {
|
if sf, ok = ld.file(); ok {
|
||||||
st, errno = sf.Stat()
|
st, errno = sf.Stat()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/internal/platform"
|
|
||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"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))
|
require.Zero(t, testFS.Chown("dir", -1, g))
|
||||||
checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, uint32(g))
|
checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, uint32(g))
|
||||||
|
|
||||||
// Revert back with platform.ChownFile
|
// Revert back with File.Chown
|
||||||
require.Zero(t, platform.ChownFile(dirF.File(), -1, gid))
|
require.Zero(t, dirF.Chown(-1, gid))
|
||||||
checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, uint32(gid))
|
checkUidGid(t, path.Join(tmpDir, "dir"), dirSys.Uid, uint32(gid))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user