gojs: adds support for uid and gid (#1245)
This adds `gojs.WithOSUser` which passes through current user IDs so that GOOS=js compiled wasm can read them. This also adds support for reading back the uid and gid on files. In summary, this passes `os.TestChown` except on windows where it will not work due to lack of support. Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -1,18 +1,25 @@
|
||||
package gojs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/gojs/custom"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/config"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/goos"
|
||||
)
|
||||
|
||||
// newJsGlobal = js.Global() // js.go init
|
||||
func newJsGlobal(rt http.RoundTripper) *jsVal {
|
||||
func newJsGlobal(config *config.Config) *jsVal {
|
||||
var fetchProperty interface{} = goos.Undefined
|
||||
if rt != nil {
|
||||
uid, gid, euid := config.Uid, config.Gid, config.Euid
|
||||
groups := config.Groups
|
||||
proc := &processState{
|
||||
cwd: config.Workdir,
|
||||
umask: config.Umask,
|
||||
}
|
||||
rt := config.Rt
|
||||
|
||||
if config.Rt != nil {
|
||||
fetchProperty = goos.RefHttpFetch
|
||||
}
|
||||
|
||||
return newJsVal(goos.RefValueGlobal, "global").
|
||||
addProperties(map[string]interface{}{
|
||||
"Object": objectConstructor,
|
||||
@@ -22,8 +29,8 @@ func newJsGlobal(rt http.RoundTripper) *jsVal {
|
||||
"fetch": fetchProperty,
|
||||
"AbortController": goos.Undefined,
|
||||
"Headers": headersConstructor,
|
||||
"process": jsProcess,
|
||||
"fs": jsfs,
|
||||
"process": newJsProcess(uid, gid, euid, groups, proc),
|
||||
"fs": newJsFs(proc),
|
||||
"Date": jsDateConstructor,
|
||||
}).
|
||||
addFunction("fetch", &httpFetch{rt})
|
||||
@@ -43,20 +50,6 @@ var (
|
||||
// Get("Array") // js.go init
|
||||
arrayConstructor = newJsVal(goos.RefArrayConstructor, "Array")
|
||||
|
||||
// jsProcess = js.Global().Get("process") // fs_js.go init
|
||||
jsProcess = newJsVal(goos.RefJsProcess, custom.NameProcess).
|
||||
addProperties(map[string]interface{}{
|
||||
"pid": float64(1), // Get("pid").Int() in syscall_js.go for syscall.Getpid
|
||||
"ppid": goos.RefValueZero, // Get("ppid").Int() in syscall_js.go for syscall.Getppid
|
||||
}).
|
||||
addFunction(custom.NameProcessCwd, processCwd{}). // syscall.Cwd in fs_js.go
|
||||
addFunction(custom.NameProcessChdir, processChdir{}). // syscall.Chdir in fs_js.go
|
||||
addFunction(custom.NameProcessGetuid, returnZero{}). // syscall.Getuid in syscall_js.go
|
||||
addFunction(custom.NameProcessGetgid, returnZero{}). // syscall.Getgid in syscall_js.go
|
||||
addFunction(custom.NameProcessGeteuid, returnZero{}). // syscall.Geteuid in syscall_js.go
|
||||
addFunction(custom.NameProcessGetgroups, returnSliceOfZero{}). // syscall.Getgroups in syscall_js.go
|
||||
addFunction(custom.NameProcessUmask, processUmask{}) // syscall.Umask in syscall_js.go
|
||||
|
||||
// uint8ArrayConstructor = js.Global().Get("Uint8Array")
|
||||
// // fs_js.go, rand_js.go, roundtrip_js.go init
|
||||
//
|
||||
|
||||
@@ -3,23 +3,41 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
OsWorkdir bool
|
||||
Rt http.RoundTripper
|
||||
OsUser bool
|
||||
|
||||
Uid, Gid, Euid int
|
||||
Groups []int
|
||||
|
||||
// Workdir is the actual working directory value.
|
||||
Workdir string
|
||||
Umask uint32
|
||||
Rt http.RoundTripper
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
return &Config{Workdir: "/"}
|
||||
return &Config{
|
||||
OsWorkdir: false,
|
||||
OsUser: false,
|
||||
Uid: 0,
|
||||
Gid: 0,
|
||||
Euid: 0,
|
||||
Groups: []int{0},
|
||||
Workdir: "/",
|
||||
Umask: uint32(0o0022),
|
||||
Rt: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) Clone() *Config {
|
||||
@@ -38,5 +56,16 @@ func (c *Config) Init() error {
|
||||
// Strip the volume of the path, for example C:\
|
||||
c.Workdir = workdir[len(filepath.VolumeName(workdir)):]
|
||||
}
|
||||
|
||||
// Windows does not support any of these properties
|
||||
if c.OsUser && runtime.GOOS != "windows" {
|
||||
c.Uid = syscall.Getuid()
|
||||
c.Gid = syscall.Getgid()
|
||||
c.Euid = syscall.Geteuid()
|
||||
var err error
|
||||
if c.Groups, err = syscall.Getgroups(); err != nil {
|
||||
return fmt.Errorf("couldn't read groups: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
@@ -10,8 +12,37 @@ import (
|
||||
func TestConfig_Init(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OsWorkdir", func(t *testing.T) {
|
||||
c := &Config{OsWorkdir: true}
|
||||
t.Run("User", func(t *testing.T) {
|
||||
c := NewConfig()
|
||||
|
||||
// values should be 0 which is root
|
||||
require.Equal(t, 0, c.Uid)
|
||||
require.Equal(t, 0, c.Gid)
|
||||
require.Equal(t, 0, c.Euid)
|
||||
require.Equal(t, []int{0}, c.Groups)
|
||||
require.False(t, c.OsUser)
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
c.OsUser = true
|
||||
require.NoError(t, c.Init())
|
||||
|
||||
require.Equal(t, syscall.Getuid(), c.Uid)
|
||||
require.Equal(t, syscall.Getgid(), c.Gid)
|
||||
require.Equal(t, syscall.Geteuid(), c.Euid)
|
||||
|
||||
groups, err := syscall.Getgroups()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, groups, c.Groups)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Workdir", func(t *testing.T) {
|
||||
c := NewConfig()
|
||||
require.Equal(t, "/", c.Workdir)
|
||||
require.False(t, c.OsWorkdir)
|
||||
|
||||
c.OsWorkdir = true
|
||||
|
||||
require.NoError(t, c.Init())
|
||||
actual := c.Workdir
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/custom"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/goos"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/util"
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
internalsys "github.com/tetratelabs/wazero/internal/sys"
|
||||
"github.com/tetratelabs/wazero/internal/sysfs"
|
||||
@@ -19,40 +19,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// jsfs = js.Global().Get("fs") // fs_js.go init
|
||||
//
|
||||
// js.fsCall conventions:
|
||||
// * funcWrapper callback is the last parameter
|
||||
// * arg0 is error and up to one result in arg1
|
||||
jsfs = newJsVal(goos.RefJsfs, custom.NameFs).
|
||||
addProperties(map[string]interface{}{
|
||||
"constants": jsfsConstants, // = jsfs.Get("constants") // init
|
||||
}).
|
||||
addFunction(custom.NameFsOpen, jsfsOpen{}).
|
||||
addFunction(custom.NameFsStat, jsfsStat{}).
|
||||
addFunction(custom.NameFsFstat, jsfsFstat{}).
|
||||
addFunction(custom.NameFsLstat, jsfsLstat{}).
|
||||
addFunction(custom.NameFsClose, jsfsClose{}).
|
||||
addFunction(custom.NameFsRead, jsfsRead{}).
|
||||
addFunction(custom.NameFsWrite, jsfsWrite{}).
|
||||
addFunction(custom.NameFsReaddir, jsfsReaddir{}).
|
||||
addFunction(custom.NameFsMkdir, jsfsMkdir{}).
|
||||
addFunction(custom.NameFsRmdir, jsfsRmdir{}).
|
||||
addFunction(custom.NameFsRename, jsfsRename{}).
|
||||
addFunction(custom.NameFsUnlink, jsfsUnlink{}).
|
||||
addFunction(custom.NameFsUtimes, jsfsUtimes{}).
|
||||
addFunction(custom.NameFsChmod, jsfsChmod{}).
|
||||
addFunction(custom.NameFsFchmod, jsfsFchmod{}).
|
||||
addFunction(custom.NameFsChown, jsfsChown{}).
|
||||
addFunction(custom.NameFsFchown, jsfsFchown{}).
|
||||
addFunction(custom.NameFsLchown, jsfsLchown{}).
|
||||
addFunction(custom.NameFsTruncate, jsfsTruncate{}).
|
||||
addFunction(custom.NameFsFtruncate, jsfsFtruncate{}).
|
||||
addFunction(custom.NameFsReadlink, jsfsReadlink{}).
|
||||
addFunction(custom.NameFsLink, jsfsLink{}).
|
||||
addFunction(custom.NameFsSymlink, jsfsSymlink{}).
|
||||
addFunction(custom.NameFsFsync, jsfsFsync{})
|
||||
|
||||
// jsfsConstants = jsfs Get("constants") // fs_js.go init
|
||||
jsfsConstants = newJsVal(goos.RefJsfsConstants, "constants").
|
||||
addProperties(map[string]interface{}{
|
||||
@@ -93,15 +59,53 @@ type (
|
||||
truncateFile interface{ Truncate(size int64) error }
|
||||
)
|
||||
|
||||
// jsfs = js.Global().Get("fs") // fs_js.go init
|
||||
//
|
||||
// js.fsCall conventions:
|
||||
// * funcWrapper callback is the last parameter
|
||||
// - arg0 is error and up to one result in arg1
|
||||
func newJsFs(proc *processState) *jsVal {
|
||||
return newJsVal(goos.RefJsfs, custom.NameFs).
|
||||
addProperties(map[string]interface{}{
|
||||
"constants": jsfsConstants, // = jsfs.Get("constants") // init
|
||||
}).
|
||||
addFunction(custom.NameFsOpen, &jsfsOpen{proc: proc}).
|
||||
addFunction(custom.NameFsStat, &jsfsStat{proc: proc}).
|
||||
addFunction(custom.NameFsFstat, jsfsFstat{}).
|
||||
addFunction(custom.NameFsLstat, &jsfsLstat{proc: proc}).
|
||||
addFunction(custom.NameFsClose, jsfsClose{}).
|
||||
addFunction(custom.NameFsRead, jsfsRead{}).
|
||||
addFunction(custom.NameFsWrite, jsfsWrite{}).
|
||||
addFunction(custom.NameFsReaddir, &jsfsReaddir{proc: proc}).
|
||||
addFunction(custom.NameFsMkdir, &jsfsMkdir{proc: proc}).
|
||||
addFunction(custom.NameFsRmdir, &jsfsRmdir{proc: proc}).
|
||||
addFunction(custom.NameFsRename, &jsfsRename{proc: proc}).
|
||||
addFunction(custom.NameFsUnlink, &jsfsUnlink{proc: proc}).
|
||||
addFunction(custom.NameFsUtimes, &jsfsUtimes{proc: proc}).
|
||||
addFunction(custom.NameFsChmod, &jsfsChmod{proc: proc}).
|
||||
addFunction(custom.NameFsFchmod, jsfsFchmod{}).
|
||||
addFunction(custom.NameFsChown, &jsfsChown{proc: proc}).
|
||||
addFunction(custom.NameFsFchown, jsfsFchown{}).
|
||||
addFunction(custom.NameFsLchown, &jsfsLchown{proc: proc}).
|
||||
addFunction(custom.NameFsTruncate, &jsfsTruncate{proc: proc}).
|
||||
addFunction(custom.NameFsFtruncate, jsfsFtruncate{}).
|
||||
addFunction(custom.NameFsReadlink, &jsfsReadlink{proc: proc}).
|
||||
addFunction(custom.NameFsLink, &jsfsLink{proc: proc}).
|
||||
addFunction(custom.NameFsSymlink, &jsfsSymlink{proc: proc}).
|
||||
addFunction(custom.NameFsFsync, jsfsFsync{})
|
||||
}
|
||||
|
||||
// jsfsOpen implements implements jsFn for syscall.Open
|
||||
//
|
||||
// jsFD /* Int */, err := fsCall("open", path, flags, perm)
|
||||
type jsfsOpen struct{}
|
||||
type jsfsOpen struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsOpen) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (o *jsfsOpen) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(o.proc.cwd, args[0].(string))
|
||||
flags := toUint64(args[1]) // flags are derived from constants like oWRONLY
|
||||
perm := getPerm(ctx, goos.ValueToUint32(args[2]))
|
||||
perm := custom.FromJsMode(goos.ValueToUint32(args[2]), o.proc.umask)
|
||||
callback := args[3].(funcWrapper)
|
||||
|
||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||
@@ -114,10 +118,12 @@ func (jsfsOpen) invoke(ctx context.Context, mod api.Module, args ...interface{})
|
||||
// jsfsStat implements jsFn for syscall.Stat
|
||||
//
|
||||
// jsSt, err := fsCall("stat", path)
|
||||
type jsfsStat struct{}
|
||||
type jsfsStat struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsStat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (s *jsfsStat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(s.proc.cwd, args[0].(string))
|
||||
callback := args[1].(funcWrapper)
|
||||
|
||||
stat, err := syscallStat(mod, path)
|
||||
@@ -138,10 +144,12 @@ func syscallStat(mod api.Module, path string) (*jsSt, error) {
|
||||
// jsfsLstat implements jsFn for syscall.Lstat
|
||||
//
|
||||
// jsSt, err := fsCall("lstat", path)
|
||||
type jsfsLstat struct{}
|
||||
type jsfsLstat struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsLstat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (l *jsfsLstat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(l.proc.cwd, args[0].(string))
|
||||
callback := args[1].(funcWrapper)
|
||||
|
||||
lstat, err := syscallLstat(mod, path)
|
||||
@@ -194,6 +202,8 @@ func newJsSt(st *platform.Stat_t) *jsSt {
|
||||
ret.isDir = st.Mode.IsDir()
|
||||
ret.dev = st.Dev
|
||||
ret.ino = st.Ino
|
||||
ret.uid = st.Uid
|
||||
ret.gid = st.Gid
|
||||
ret.mode = custom.ToJsMode(st.Mode)
|
||||
ret.nlink = uint32(st.Nlink)
|
||||
ret.size = st.Size
|
||||
@@ -319,10 +329,12 @@ func syscallWrite(mod api.Module, fd uint32, offset interface{}, p []byte) (n ui
|
||||
//
|
||||
// dir, err := fsCall("readdir", path)
|
||||
// dir.Length(), dir.Index(i).String()
|
||||
type jsfsReaddir struct{}
|
||||
type jsfsReaddir struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsReaddir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (r *jsfsReaddir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(r.proc.cwd, args[0].(string))
|
||||
callback := args[1].(funcWrapper)
|
||||
|
||||
stat, err := syscallReaddir(ctx, mod, path)
|
||||
@@ -350,64 +362,16 @@ func syscallReaddir(_ context.Context, mod api.Module, name string) (*objectArra
|
||||
}
|
||||
}
|
||||
|
||||
// returnZero implements jsFn
|
||||
type returnZero struct{}
|
||||
|
||||
func (returnZero) invoke(context.Context, api.Module, ...interface{}) (interface{}, error) {
|
||||
return goos.RefValueZero, nil
|
||||
}
|
||||
|
||||
// returnSliceOfZero implements jsFn
|
||||
type returnSliceOfZero struct{}
|
||||
|
||||
func (returnSliceOfZero) invoke(context.Context, api.Module, ...interface{}) (interface{}, error) {
|
||||
return &objectArray{slice: []interface{}{goos.RefValueZero}}, nil
|
||||
}
|
||||
|
||||
// processCwd implements jsFn for fs.Open syscall.Getcwd in fs_js.go
|
||||
type processCwd struct{}
|
||||
|
||||
func (processCwd) invoke(ctx context.Context, _ api.Module, _ ...interface{}) (interface{}, error) {
|
||||
return getState(ctx).cwd, nil
|
||||
}
|
||||
|
||||
// processChdir implements jsFn for fs.Open syscall.Chdir in fs_js.go
|
||||
type processChdir struct{}
|
||||
|
||||
func (processChdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := path.Clean(args[0].(string))
|
||||
|
||||
if s, err := syscallStat(mod, path); err != nil {
|
||||
return nil, err
|
||||
} else if !s.isDir {
|
||||
return nil, syscall.ENOTDIR
|
||||
} else {
|
||||
getState(ctx).cwd = path
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// processUmask implements jsFn for fs.Open syscall.Umask in fs_js.go
|
||||
type processUmask struct{}
|
||||
|
||||
func (processUmask) invoke(ctx context.Context, _ api.Module, args ...interface{}) (interface{}, error) {
|
||||
mask := goos.ValueToUint32(args[0])
|
||||
|
||||
s := getState(ctx)
|
||||
oldmask := s.umask
|
||||
s.umask = mask
|
||||
|
||||
return oldmask, nil
|
||||
}
|
||||
|
||||
// jsfsMkdir implements implements jsFn for fs.Mkdir
|
||||
//
|
||||
// jsFD /* Int */, err := fsCall("mkdir", path, perm)
|
||||
type jsfsMkdir struct{}
|
||||
type jsfsMkdir struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsMkdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
perm := getPerm(ctx, goos.ValueToUint32(args[1]))
|
||||
func (m *jsfsMkdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(m.proc.cwd, args[0].(string))
|
||||
perm := custom.FromJsMode(goos.ValueToUint32(args[1]), m.proc.umask)
|
||||
callback := args[2].(funcWrapper)
|
||||
|
||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||
@@ -429,10 +393,12 @@ func (jsfsMkdir) invoke(ctx context.Context, mod api.Module, args ...interface{}
|
||||
// jsfsRmdir implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("rmdir", path) // syscall.Rmdir
|
||||
type jsfsRmdir struct{}
|
||||
type jsfsRmdir struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsRmdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (r *jsfsRmdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(r.proc.cwd, args[0].(string))
|
||||
callback := args[1].(funcWrapper)
|
||||
|
||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||
@@ -444,11 +410,14 @@ func (jsfsRmdir) invoke(ctx context.Context, mod api.Module, args ...interface{}
|
||||
// jsfsRename implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("rename", from, to) // syscall.Rename
|
||||
type jsfsRename struct{}
|
||||
type jsfsRename struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsRename) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
from := resolvePath(ctx, args[0].(string))
|
||||
to := resolvePath(ctx, args[1].(string))
|
||||
func (r *jsfsRename) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
cwd := r.proc.cwd
|
||||
from := util.ResolvePath(cwd, args[0].(string))
|
||||
to := util.ResolvePath(cwd, args[1].(string))
|
||||
callback := args[2].(funcWrapper)
|
||||
|
||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||
@@ -460,10 +429,12 @@ func (jsfsRename) invoke(ctx context.Context, mod api.Module, args ...interface{
|
||||
// jsfsUnlink implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("unlink", path) // syscall.Unlink
|
||||
type jsfsUnlink struct{}
|
||||
type jsfsUnlink struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsUnlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (u *jsfsUnlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(u.proc.cwd, args[0].(string))
|
||||
callback := args[1].(funcWrapper)
|
||||
|
||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||
@@ -475,10 +446,12 @@ func (jsfsUnlink) invoke(ctx context.Context, mod api.Module, args ...interface{
|
||||
// jsfsUtimes implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("utimes", path, atime, mtime) // syscall.Utimens
|
||||
type jsfsUtimes struct{}
|
||||
type jsfsUtimes struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsUtimes) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (u *jsfsUtimes) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(u.proc.cwd, args[0].(string))
|
||||
atimeSec := toInt64(args[1])
|
||||
mtimeSec := toInt64(args[2])
|
||||
callback := args[3].(funcWrapper)
|
||||
@@ -495,10 +468,12 @@ func (jsfsUtimes) invoke(ctx context.Context, mod api.Module, args ...interface{
|
||||
// jsfsChmod implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("chmod", path, mode) // syscall.Chmod
|
||||
type jsfsChmod struct{}
|
||||
type jsfsChmod struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsChmod) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (c *jsfsChmod) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(c.proc.cwd, args[0].(string))
|
||||
mode := custom.FromJsMode(goos.ValueToUint32(args[1]), 0)
|
||||
callback := args[2].(funcWrapper)
|
||||
|
||||
@@ -535,10 +510,12 @@ func (jsfsFchmod) invoke(ctx context.Context, mod api.Module, args ...interface{
|
||||
// jsfsChown implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("chown", path, uint32(uid), uint32(gid)) // syscall.Chown
|
||||
type jsfsChown struct{}
|
||||
type jsfsChown struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsChown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (c *jsfsChown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(c.proc.cwd, args[0].(string))
|
||||
uid := goos.ValueToInt32(args[1])
|
||||
gid := goos.ValueToInt32(args[2])
|
||||
callback := args[3].(funcWrapper)
|
||||
@@ -575,10 +552,12 @@ func (jsfsFchown) invoke(ctx context.Context, mod api.Module, args ...interface{
|
||||
// jsfsLchown implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("lchown", path, uint32(uid), uint32(gid)) // syscall.Lchown
|
||||
type jsfsLchown struct{}
|
||||
type jsfsLchown struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsLchown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (l *jsfsLchown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(l.proc.cwd, args[0].(string))
|
||||
uid := goos.ValueToUint32(args[1])
|
||||
gid := goos.ValueToUint32(args[2])
|
||||
callback := args[3].(funcWrapper)
|
||||
@@ -592,10 +571,12 @@ func (jsfsLchown) invoke(ctx context.Context, mod api.Module, args ...interface{
|
||||
// jsfsTruncate implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("truncate", path, length) // syscall.Truncate
|
||||
type jsfsTruncate struct{}
|
||||
type jsfsTruncate struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsTruncate) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (t *jsfsTruncate) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(t.proc.cwd, args[0].(string))
|
||||
length := toInt64(args[1])
|
||||
callback := args[2].(funcWrapper)
|
||||
|
||||
@@ -632,10 +613,12 @@ func (jsfsFtruncate) invoke(ctx context.Context, mod api.Module, args ...interfa
|
||||
// jsfsReadlink implements jsFn for syscall.Readlink
|
||||
//
|
||||
// dst, err := fsCall("readlink", path) // syscall.Readlink
|
||||
type jsfsReadlink struct{}
|
||||
type jsfsReadlink struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsReadlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
func (r *jsfsReadlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := util.ResolvePath(r.proc.cwd, args[0].(string))
|
||||
callback := args[1].(funcWrapper)
|
||||
|
||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||
@@ -647,11 +630,14 @@ func (jsfsReadlink) invoke(ctx context.Context, mod api.Module, args ...interfac
|
||||
// jsfsLink implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("link", path, link) // syscall.Link
|
||||
type jsfsLink struct{}
|
||||
type jsfsLink struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsLink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
path := resolvePath(ctx, args[0].(string))
|
||||
link := resolvePath(ctx, args[1].(string))
|
||||
func (l *jsfsLink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
cwd := l.proc.cwd
|
||||
path := util.ResolvePath(cwd, args[0].(string))
|
||||
link := util.ResolvePath(cwd, args[1].(string))
|
||||
callback := args[2].(funcWrapper)
|
||||
|
||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||
@@ -663,11 +649,13 @@ func (jsfsLink) invoke(ctx context.Context, mod api.Module, args ...interface{})
|
||||
// jsfsSymlink implements jsFn for the following
|
||||
//
|
||||
// _, err := fsCall("symlink", path, link) // syscall.Symlink
|
||||
type jsfsSymlink struct{}
|
||||
type jsfsSymlink struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (jsfsSymlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
func (s *jsfsSymlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
dst := args[0].(string) // The dst of a symlink must not be resolved, as it should be resolved during readLink.
|
||||
link := resolvePath(ctx, args[1].(string))
|
||||
link := util.ResolvePath(s.proc.cwd, args[1].(string))
|
||||
callback := args[2].(funcWrapper)
|
||||
|
||||
fsc := mod.(*wasm.CallContext).Sys.FS()
|
||||
@@ -723,7 +711,7 @@ func (s *jsSt) String() string {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on goos.GetFunction
|
||||
func (s *jsSt) Get(_ context.Context, propertyKey string) interface{} {
|
||||
func (s *jsSt) Get(propertyKey string) interface{} {
|
||||
switch propertyKey {
|
||||
case "dev":
|
||||
return s.dev
|
||||
@@ -766,25 +754,3 @@ func (s *jsSt) call(_ context.Context, _ api.Module, _ goos.Ref, method string,
|
||||
func jsfsInvoke(ctx context.Context, mod api.Module, callback funcWrapper, err error) (interface{}, error) {
|
||||
return callback.invoke(ctx, mod, goos.RefJsfs, err, err == nil) // note: error first
|
||||
}
|
||||
|
||||
// resolvePath is needed when a non-absolute path is given to a function.
|
||||
// Unlike other host ABI, GOOS=js maintains the CWD host side.
|
||||
func resolvePath(ctx context.Context, path string) string {
|
||||
if len(path) == 0 || path[0] == '/' {
|
||||
return path // leave alone .. or absolute paths.
|
||||
}
|
||||
return joinPath(getState(ctx).cwd, path)
|
||||
}
|
||||
|
||||
// joinPath avoids us having to rename fields just to avoid conflict with the
|
||||
// path package.
|
||||
func joinPath(dirName, baseName string) string {
|
||||
return path.Join(dirName, baseName)
|
||||
}
|
||||
|
||||
// getPerm converts the input js permissions to a go-compatible one, after
|
||||
// subtracting the current umask.
|
||||
func getPerm(ctx context.Context, perm uint32) fs.FileMode {
|
||||
umask := getState(ctx).umask
|
||||
return custom.FromJsMode(perm, umask)
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ func Test_fs(t *testing.T) {
|
||||
|
||||
require.Zero(t, stderr)
|
||||
require.EqualError(t, err, `module "" closed with exit_code(0)`)
|
||||
require.Equal(t, `wd ok
|
||||
Not a directory
|
||||
sub mode drwxr-xr-x
|
||||
require.Equal(t, `sub mode drwxr-xr-x
|
||||
/animals.txt mode -rw-r--r--
|
||||
animals.txt mode -rw-r--r--
|
||||
contents: bear
|
||||
|
||||
@@ -276,7 +276,7 @@ func ValueToInt32(arg interface{}) int32 {
|
||||
|
||||
// GetFunction allows getting a JavaScript property by name.
|
||||
type GetFunction interface {
|
||||
Get(ctx context.Context, propertyKey string) interface{}
|
||||
Get(propertyKey string) interface{}
|
||||
}
|
||||
|
||||
// ByteArray is a result of uint8ArrayConstructor which temporarily stores
|
||||
@@ -297,7 +297,7 @@ func (a *ByteArray) Unwrap() []byte {
|
||||
}
|
||||
|
||||
// Get implements GetFunction
|
||||
func (a *ByteArray) Get(_ context.Context, propertyKey string) interface{} {
|
||||
func (a *ByteArray) Get(propertyKey string) interface{} {
|
||||
switch propertyKey {
|
||||
case "byteLength":
|
||||
return uint32(len(a.slice))
|
||||
|
||||
@@ -67,7 +67,7 @@ type fetchResult struct {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on goos.GetFunction
|
||||
func (s *fetchResult) Get(_ context.Context, propertyKey string) interface{} {
|
||||
func (s *fetchResult) Get(propertyKey string) interface{} {
|
||||
switch propertyKey {
|
||||
case "headers":
|
||||
names := make([]string, 0, len(s.res.Header))
|
||||
@@ -104,7 +104,7 @@ type headers struct {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on goos.GetFunction
|
||||
func (h *headers) Get(_ context.Context, propertyKey string) interface{} {
|
||||
func (h *headers) Get(propertyKey string) interface{} {
|
||||
switch propertyKey {
|
||||
case "done":
|
||||
return h.i == len(h.names)
|
||||
|
||||
@@ -41,9 +41,9 @@ func Test_http(t *testing.T) {
|
||||
})
|
||||
|
||||
stdout, stderr, err := compileAndRun(testCtx, "http", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) {
|
||||
return moduleConfig.WithEnv("BASE_URL", "http://host"), &config.Config{
|
||||
Rt: rt,
|
||||
}
|
||||
config := config.NewConfig()
|
||||
config.Rt = rt
|
||||
return moduleConfig.WithEnv("BASE_URL", "http://host"), config
|
||||
})
|
||||
|
||||
require.EqualError(t, err, `module "" closed with exit_code(0)`)
|
||||
|
||||
@@ -51,7 +51,7 @@ func (v *jsVal) addFunction(method string, fn jsFn) *jsVal {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on goos.GetFunction
|
||||
func (v *jsVal) Get(_ context.Context, propertyKey string) interface{} {
|
||||
func (v *jsVal) Get(propertyKey string) interface{} {
|
||||
if v, ok := v.properties[propertyKey]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
103
internal/gojs/process.go
Normal file
103
internal/gojs/process.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package gojs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/custom"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/goos"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/util"
|
||||
)
|
||||
|
||||
// processState are the mutable fields of the current process.
|
||||
type processState struct {
|
||||
cwd string
|
||||
umask uint32
|
||||
}
|
||||
|
||||
func newJsProcess(uid, gid, euid int, groups []int, proc *processState) *jsVal {
|
||||
uidRef := toFloatRef(float64(uid))
|
||||
gidRef := toFloatRef(float64(gid))
|
||||
euidRef := toFloatRef(float64(euid))
|
||||
groupSlice := make([]interface{}, 0, len(groups))
|
||||
for _, group := range groups {
|
||||
groupSlice = append(groupSlice, toFloatRef(float64(group)))
|
||||
}
|
||||
|
||||
// jsProcess = js.Global().Get("process") // fs_js.go init
|
||||
return newJsVal(goos.RefJsProcess, custom.NameProcess).
|
||||
addProperties(map[string]interface{}{
|
||||
"pid": float64(1), // Get("pid").Int() in syscall_js.go for syscall.Getpid
|
||||
"ppid": goos.RefValueZero, // Get("ppid").Int() in syscall_js.go for syscall.Getppid
|
||||
}).
|
||||
addFunction(custom.NameProcessCwd, &processCwd{proc: proc}). // syscall.Cwd in fs_js.go
|
||||
addFunction(custom.NameProcessChdir, &processChdir{proc: proc}). // syscall.Chdir in fs_js.go
|
||||
addFunction(custom.NameProcessGetuid, getId(uidRef)). // syscall.Getuid in syscall_js.go
|
||||
addFunction(custom.NameProcessGetgid, getId(gidRef)). // syscall.Getgid in syscall_js.go
|
||||
addFunction(custom.NameProcessGeteuid, getId(euidRef)). // syscall.Geteuid in syscall_js.go
|
||||
addFunction(custom.NameProcessGetgroups, returnSlice(groupSlice)). // syscall.Getgroups in syscall_js.go
|
||||
addFunction(custom.NameProcessUmask, &processUmask{proc: proc}) // syscall.Umask in syscall_js.go
|
||||
}
|
||||
|
||||
// processCwd implements jsFn for fs.Open syscall.Getcwd in fs_js.go
|
||||
type processCwd struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (p *processCwd) invoke(_ context.Context, _ api.Module, _ ...interface{}) (interface{}, error) {
|
||||
return p.proc.cwd, nil
|
||||
}
|
||||
|
||||
// processChdir implements jsFn for fs.Open syscall.Chdir in fs_js.go
|
||||
type processChdir struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (p *processChdir) invoke(_ context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
|
||||
oldWd := p.proc.cwd
|
||||
newWd := util.ResolvePath(oldWd, args[0].(string))
|
||||
|
||||
newWd = path.Clean(newWd)
|
||||
if newWd == oldWd { // handle .
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if s, err := syscallStat(mod, newWd); err != nil {
|
||||
return nil, err
|
||||
} else if !s.isDir {
|
||||
return nil, syscall.ENOTDIR
|
||||
} else {
|
||||
p.proc.cwd = newWd
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// processUmask implements jsFn for fs.Open syscall.Umask in fs_js.go
|
||||
type processUmask struct {
|
||||
proc *processState
|
||||
}
|
||||
|
||||
func (p *processUmask) invoke(_ context.Context, _ api.Module, args ...interface{}) (interface{}, error) {
|
||||
newUmask := goos.ValueToUint32(args[0])
|
||||
|
||||
oldUmask := p.proc.umask
|
||||
p.proc.umask = newUmask
|
||||
|
||||
return oldUmask, nil
|
||||
}
|
||||
|
||||
// getId implements jsFn for syscall.Getuid, syscall.Getgid and syscall.Geteuid in syscall_js.go
|
||||
type getId goos.Ref
|
||||
|
||||
func (i getId) invoke(_ context.Context, _ api.Module, _ ...interface{}) (interface{}, error) {
|
||||
return goos.Ref(i), nil
|
||||
}
|
||||
|
||||
// returnSlice implements jsFn for syscall.Getgroups in syscall_js.go
|
||||
type returnSlice []interface{}
|
||||
|
||||
func (s returnSlice) invoke(context.Context, api.Module, ...interface{}) (interface{}, error) {
|
||||
return &objectArray{slice: s}, nil
|
||||
}
|
||||
@@ -1,18 +1,24 @@
|
||||
package gojs_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/config"
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
)
|
||||
|
||||
func Test_syscall(t *testing.T) {
|
||||
func Test_process(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
stdout, stderr, err := compileAndRun(testCtx, "syscall", defaultConfig)
|
||||
require.NoError(t, os.Chdir("/.."))
|
||||
stdout, stderr, err := compileAndRun(testCtx, "process", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) {
|
||||
return defaultConfig(moduleConfig.WithFS(testFS))
|
||||
})
|
||||
|
||||
require.EqualError(t, err, `module "" closed with exit_code(0)`)
|
||||
require.Zero(t, stderr)
|
||||
require.EqualError(t, err, `module "" closed with exit_code(0)`)
|
||||
require.Equal(t, `syscall.Getpid()=1
|
||||
syscall.Getppid()=0
|
||||
syscall.Getuid()=0
|
||||
@@ -21,5 +27,7 @@ syscall.Geteuid()=0
|
||||
syscall.Umask(0077)=0o22
|
||||
syscall.Getgroups()=[0]
|
||||
os.FindProcess(1).Pid=1
|
||||
wd ok
|
||||
Not a directory
|
||||
`, stdout)
|
||||
}
|
||||
@@ -14,9 +14,7 @@ import (
|
||||
func NewState(config *config.Config) *State {
|
||||
return &State{
|
||||
values: values.NewValues(),
|
||||
valueGlobal: newJsGlobal(config.Rt),
|
||||
cwd: config.Workdir,
|
||||
umask: 0o0022,
|
||||
valueGlobal: newJsGlobal(config),
|
||||
_nextCallbackTimeoutID: 1,
|
||||
_scheduledTimeouts: map[uint32]chan bool{},
|
||||
}
|
||||
@@ -48,7 +46,7 @@ type event struct {
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on goos.GetFunction
|
||||
func (e *event) Get(_ context.Context, propertyKey string) interface{} {
|
||||
func (e *event) Get(propertyKey string) interface{} {
|
||||
switch propertyKey {
|
||||
case "id":
|
||||
return e.id
|
||||
@@ -89,9 +87,9 @@ func LoadValue(ctx context.Context, ref goos.Ref) interface{} { //nolint
|
||||
case goos.RefArrayConstructor:
|
||||
return arrayConstructor
|
||||
case goos.RefJsProcess:
|
||||
return jsProcess
|
||||
return getState(ctx).valueGlobal.Get("process")
|
||||
case goos.RefJsfs:
|
||||
return jsfs
|
||||
return getState(ctx).valueGlobal.Get("fs")
|
||||
case goos.RefJsfsConstants:
|
||||
return jsfsConstants
|
||||
case goos.RefUint8ArrayConstructor:
|
||||
@@ -180,15 +178,10 @@ type State struct {
|
||||
|
||||
_nextCallbackTimeoutID uint32
|
||||
_scheduledTimeouts map[uint32]chan bool
|
||||
|
||||
// cwd is initially "/"
|
||||
cwd string
|
||||
// umask is initially 0022
|
||||
umask uint32
|
||||
}
|
||||
|
||||
// Get implements the same method as documented on goos.GetFunction
|
||||
func (s *State) Get(_ context.Context, propertyKey string) interface{} {
|
||||
func (s *State) Get(propertyKey string) interface{} {
|
||||
switch propertyKey {
|
||||
case "_pendingEvent":
|
||||
return s._pendingEvent
|
||||
@@ -220,8 +213,6 @@ func (s *State) close() {
|
||||
s._pendingEvent = nil
|
||||
s._lastEvent = nil
|
||||
s._nextCallbackTimeoutID = 1
|
||||
s.cwd = "/"
|
||||
s.umask = 0o0022
|
||||
}
|
||||
|
||||
func toInt64(arg interface{}) int64 {
|
||||
|
||||
@@ -55,7 +55,7 @@ func valueGet(ctx context.Context, mod api.Module, stack goos.Stack) {
|
||||
|
||||
var result interface{}
|
||||
if g, ok := v.(goos.GetFunction); ok {
|
||||
result = g.Get(ctx, p)
|
||||
result = g.Get(p)
|
||||
} else if e, ok := v.(error); ok {
|
||||
switch p {
|
||||
case "message": // js (GOOS=js) error, can be anything.
|
||||
|
||||
14
internal/gojs/testdata/fs/main.go
vendored
14
internal/gojs/testdata/fs/main.go
vendored
@@ -6,7 +6,6 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
@@ -14,19 +13,6 @@ func Main() {
|
||||
}
|
||||
|
||||
func testAdHoc() {
|
||||
if wd, err := syscall.Getwd(); err != nil {
|
||||
log.Panicln(err)
|
||||
} else if wd != "/" {
|
||||
log.Panicln("not root")
|
||||
}
|
||||
fmt.Println("wd ok")
|
||||
|
||||
if err := syscall.Chdir("/animals.txt"); err == nil {
|
||||
log.Panicln("shouldn't be able to chdir to file")
|
||||
} else {
|
||||
fmt.Println(err) // should be the textual message of the errno.
|
||||
}
|
||||
|
||||
// Ensure stat works, particularly mode.
|
||||
for _, path := range []string{"sub", "/animals.txt", "animals.txt"} {
|
||||
if stat, err := os.Stat(path); err != nil {
|
||||
|
||||
18
internal/gojs/testdata/main.go
vendored
18
internal/gojs/testdata/main.go
vendored
@@ -11,8 +11,8 @@ import (
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/goroutine"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/http"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/mem"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/process"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/stdio"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/syscall"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/testfs"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/time"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/testdata/writefs"
|
||||
@@ -29,24 +29,24 @@ func main() {
|
||||
os.Exit(255)
|
||||
case "fs":
|
||||
fs.Main()
|
||||
case "testfs":
|
||||
testfs.Main()
|
||||
case "writefs":
|
||||
writefs.Main()
|
||||
case "gc":
|
||||
gc.Main()
|
||||
case "goroutine":
|
||||
goroutine.Main()
|
||||
case "http":
|
||||
http.Main()
|
||||
case "goroutine":
|
||||
goroutine.Main()
|
||||
case "mem":
|
||||
mem.Main()
|
||||
case "process":
|
||||
process.Main()
|
||||
case "stdio":
|
||||
stdio.Main()
|
||||
case "syscall":
|
||||
syscall.Main()
|
||||
case "testfs":
|
||||
testfs.Main()
|
||||
case "time":
|
||||
time.Main()
|
||||
case "writefs":
|
||||
writefs.Main()
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported arg: %s", os.Args[1]))
|
||||
}
|
||||
|
||||
64
internal/gojs/testdata/process/main.go
vendored
Normal file
64
internal/gojs/testdata/process/main.go
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// Package process is an integration test of system calls mapped to the
|
||||
// JavaScript object "process". e.g. `go.syscall/js.valueCall(process.chdir...`
|
||||
package process
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
fmt.Printf("syscall.Getpid()=%d\n", syscall.Getpid())
|
||||
fmt.Printf("syscall.Getppid()=%d\n", syscall.Getppid())
|
||||
fmt.Printf("syscall.Getuid()=%d\n", syscall.Getuid())
|
||||
fmt.Printf("syscall.Getgid()=%d\n", syscall.Getgid())
|
||||
fmt.Printf("syscall.Geteuid()=%d\n", syscall.Geteuid())
|
||||
fmt.Printf("syscall.Umask(0077)=%O\n", syscall.Umask(0o077))
|
||||
if g, err := syscall.Getgroups(); err != nil {
|
||||
log.Panicln(err)
|
||||
} else {
|
||||
fmt.Printf("syscall.Getgroups()=%v\n", g)
|
||||
}
|
||||
|
||||
pid := syscall.Getpid()
|
||||
if p, err := os.FindProcess(pid); err != nil {
|
||||
log.Panicln(err)
|
||||
} else {
|
||||
fmt.Printf("os.FindProcess(%d).Pid=%d\n", pid, p.Pid)
|
||||
}
|
||||
|
||||
if wd, err := syscall.Getwd(); err != nil {
|
||||
log.Panicln(err)
|
||||
} else if wd != "/" {
|
||||
log.Panicln("not root")
|
||||
}
|
||||
fmt.Println("wd ok")
|
||||
|
||||
dirs := []struct {
|
||||
path, wd string
|
||||
}{
|
||||
{"dir", "/dir"},
|
||||
{".", "/dir"},
|
||||
{"..", "/"},
|
||||
{".", "/"},
|
||||
{"..", "/"},
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
if err := syscall.Chdir(dir.path); err != nil {
|
||||
log.Panicln(dir.path, err)
|
||||
} else if wd, err := syscall.Getwd(); err != nil {
|
||||
log.Panicln(dir.path, err)
|
||||
} else if wd != dir.wd {
|
||||
log.Panicf("cd %s: expected wd=%s, but have %s", dir.path, dir.wd, wd)
|
||||
}
|
||||
}
|
||||
|
||||
if err := syscall.Chdir("/animals.txt"); err == nil {
|
||||
log.Panicln("shouldn't be able to chdir to file")
|
||||
} else {
|
||||
fmt.Println(err) // should be the textual message of the errno.
|
||||
}
|
||||
}
|
||||
29
internal/gojs/testdata/syscall/main.go
vendored
29
internal/gojs/testdata/syscall/main.go
vendored
@@ -1,29 +0,0 @@
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
fmt.Printf("syscall.Getpid()=%d\n", syscall.Getpid())
|
||||
fmt.Printf("syscall.Getppid()=%d\n", syscall.Getppid())
|
||||
fmt.Printf("syscall.Getuid()=%d\n", syscall.Getuid())
|
||||
fmt.Printf("syscall.Getgid()=%d\n", syscall.Getgid())
|
||||
fmt.Printf("syscall.Geteuid()=%d\n", syscall.Geteuid())
|
||||
fmt.Printf("syscall.Umask(0077)=%O\n", syscall.Umask(0o077))
|
||||
if g, err := syscall.Getgroups(); err != nil {
|
||||
log.Panicln(err)
|
||||
} else {
|
||||
fmt.Printf("syscall.Getgroups()=%v\n", g)
|
||||
}
|
||||
|
||||
pid := syscall.Getpid()
|
||||
if p, err := os.FindProcess(pid); err != nil {
|
||||
log.Panicln(err)
|
||||
} else {
|
||||
fmt.Printf("os.FindProcess(%d).Pid=%d\n", pid, p.Pid)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
pathutil "path"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/internal/gojs/custom"
|
||||
@@ -44,3 +45,26 @@ func NewFunc(name string, goFunc api.GoModuleFunc) *wasm.HostFunc {
|
||||
Code: wasm.Code{GoFunc: goFunc},
|
||||
}
|
||||
}
|
||||
|
||||
// ResolvePath is needed when a non-absolute path is given to a function.
|
||||
// Unlike other host ABI, GOOS=js maintains the CWD host side.
|
||||
func ResolvePath(cwd, path string) (resolved string) {
|
||||
pathLen := len(path)
|
||||
switch {
|
||||
case pathLen == 0:
|
||||
return cwd
|
||||
case pathLen == 1 && path[0] == '.':
|
||||
return cwd
|
||||
case path[0] == '/':
|
||||
resolved = pathutil.Clean(path)
|
||||
default:
|
||||
resolved = pathutil.Join(cwd, path)
|
||||
}
|
||||
|
||||
// If there's a trailing slash, we need to retain it for symlink edge
|
||||
// cases. See https://github.com/golang/go/issues/27225
|
||||
if len(resolved) > 1 && path[pathLen-1] == '/' {
|
||||
return resolved + "/"
|
||||
}
|
||||
return resolved
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/gojs/custom"
|
||||
@@ -73,3 +74,39 @@ func TestMustRead(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvePath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
cwd, path string
|
||||
expected string
|
||||
}{
|
||||
{cwd: "/", path: ".", expected: "/"},
|
||||
{cwd: "/", path: "/", expected: "/"},
|
||||
{cwd: "/", path: "..", expected: "/"},
|
||||
{cwd: "/", path: "a", expected: "/a"},
|
||||
{cwd: "/", path: "/a", expected: "/a"},
|
||||
{cwd: "/", path: "./a/", expected: "/a/"}, // retain trailing slash
|
||||
{cwd: "/", path: "./a/.", expected: "/a"},
|
||||
{cwd: "/", path: "a/.", expected: "/a"},
|
||||
{cwd: "/a", path: "/..", expected: "/"},
|
||||
{cwd: "/a", path: "/", expected: "/"},
|
||||
{cwd: "/a", path: "b", expected: "/a/b"},
|
||||
{cwd: "/a", path: "/b", expected: "/b"},
|
||||
{cwd: "/a", path: "/b/", expected: "/b/"}, // retain trailing slash
|
||||
{cwd: "/a", path: "./b/.", expected: "/a/b"},
|
||||
{cwd: "/a/b", path: ".", expected: "/a/b"},
|
||||
{cwd: "/a/b", path: "../.", expected: "/a"},
|
||||
{cwd: "/a/b", path: "../..", expected: "/"},
|
||||
{cwd: "/a/b", path: "../../..", expected: "/"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
|
||||
t.Run(fmt.Sprintf("%s,%s", tc.cwd, tc.path), func(t *testing.T) {
|
||||
require.Equal(t, tc.expected, ResolvePath(tc.cwd, tc.path))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user