Source file src/syscall/fs_js.go

Documentation: syscall

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build js,wasm
     6  
     7  package syscall
     8  
     9  import (
    10  	"errors"
    11  	"io"
    12  	"sync"
    13  	"syscall/js"
    14  )
    15  
    16  // Provided by package runtime.
    17  func now() (sec int64, nsec int32)
    18  
    19  var jsProcess = js.Global().Get("process")
    20  var jsFS = js.Global().Get("fs")
    21  var constants = jsFS.Get("constants")
    22  
    23  var uint8Array = js.Global().Get("Uint8Array")
    24  
    25  var (
    26  	nodeWRONLY = constants.Get("O_WRONLY").Int()
    27  	nodeRDWR   = constants.Get("O_RDWR").Int()
    28  	nodeCREATE = constants.Get("O_CREAT").Int()
    29  	nodeTRUNC  = constants.Get("O_TRUNC").Int()
    30  	nodeAPPEND = constants.Get("O_APPEND").Int()
    31  	nodeEXCL   = constants.Get("O_EXCL").Int()
    32  )
    33  
    34  type jsFile struct {
    35  	path    string
    36  	entries []string
    37  	pos     int64
    38  	seeked  bool
    39  }
    40  
    41  var filesMu sync.Mutex
    42  var files = map[int]*jsFile{
    43  	0: {},
    44  	1: {},
    45  	2: {},
    46  }
    47  
    48  func fdToFile(fd int) (*jsFile, error) {
    49  	filesMu.Lock()
    50  	f, ok := files[fd]
    51  	filesMu.Unlock()
    52  	if !ok {
    53  		return nil, EBADF
    54  	}
    55  	return f, nil
    56  }
    57  
    58  func Open(path string, openmode int, perm uint32) (int, error) {
    59  	if err := checkPath(path); err != nil {
    60  		return 0, err
    61  	}
    62  
    63  	flags := 0
    64  	if openmode&O_WRONLY != 0 {
    65  		flags |= nodeWRONLY
    66  	}
    67  	if openmode&O_RDWR != 0 {
    68  		flags |= nodeRDWR
    69  	}
    70  	if openmode&O_CREATE != 0 {
    71  		flags |= nodeCREATE
    72  	}
    73  	if openmode&O_TRUNC != 0 {
    74  		flags |= nodeTRUNC
    75  	}
    76  	if openmode&O_APPEND != 0 {
    77  		flags |= nodeAPPEND
    78  	}
    79  	if openmode&O_EXCL != 0 {
    80  		flags |= nodeEXCL
    81  	}
    82  	if openmode&O_SYNC != 0 {
    83  		return 0, errors.New("syscall.Open: O_SYNC is not supported by js/wasm")
    84  	}
    85  
    86  	jsFD, err := fsCall("open", path, flags, perm)
    87  	if err != nil {
    88  		return 0, err
    89  	}
    90  	fd := jsFD.Int()
    91  
    92  	var entries []string
    93  	if stat, err := fsCall("fstat", fd); err == nil && stat.Call("isDirectory").Bool() {
    94  		dir, err := fsCall("readdir", path)
    95  		if err != nil {
    96  			return 0, err
    97  		}
    98  		entries = make([]string, dir.Length())
    99  		for i := range entries {
   100  			entries[i] = dir.Index(i).String()
   101  		}
   102  	}
   103  
   104  	f := &jsFile{
   105  		path:    path,
   106  		entries: entries,
   107  	}
   108  	filesMu.Lock()
   109  	files[fd] = f
   110  	filesMu.Unlock()
   111  	return fd, nil
   112  }
   113  
   114  func Close(fd int) error {
   115  	filesMu.Lock()
   116  	delete(files, fd)
   117  	filesMu.Unlock()
   118  	_, err := fsCall("close", fd)
   119  	return err
   120  }
   121  
   122  func CloseOnExec(fd int) {
   123  	// nothing to do - no exec
   124  }
   125  
   126  func Mkdir(path string, perm uint32) error {
   127  	if err := checkPath(path); err != nil {
   128  		return err
   129  	}
   130  	_, err := fsCall("mkdir", path, perm)
   131  	return err
   132  }
   133  
   134  func ReadDirent(fd int, buf []byte) (int, error) {
   135  	f, err := fdToFile(fd)
   136  	if err != nil {
   137  		return 0, err
   138  	}
   139  	if f.entries == nil {
   140  		return 0, EINVAL
   141  	}
   142  
   143  	n := 0
   144  	for len(f.entries) > 0 {
   145  		entry := f.entries[0]
   146  		l := 2 + len(entry)
   147  		if l > len(buf) {
   148  			break
   149  		}
   150  		buf[0] = byte(l)
   151  		buf[1] = byte(l >> 8)
   152  		copy(buf[2:], entry)
   153  		buf = buf[l:]
   154  		n += l
   155  		f.entries = f.entries[1:]
   156  	}
   157  
   158  	return n, nil
   159  }
   160  
   161  func setStat(st *Stat_t, jsSt js.Value) {
   162  	st.Dev = int64(jsSt.Get("dev").Int())
   163  	st.Ino = uint64(jsSt.Get("ino").Int())
   164  	st.Mode = uint32(jsSt.Get("mode").Int())
   165  	st.Nlink = uint32(jsSt.Get("nlink").Int())
   166  	st.Uid = uint32(jsSt.Get("uid").Int())
   167  	st.Gid = uint32(jsSt.Get("gid").Int())
   168  	st.Rdev = int64(jsSt.Get("rdev").Int())
   169  	st.Size = int64(jsSt.Get("size").Int())
   170  	st.Blksize = int32(jsSt.Get("blksize").Int())
   171  	st.Blocks = int32(jsSt.Get("blocks").Int())
   172  	atime := int64(jsSt.Get("atimeMs").Int())
   173  	st.Atime = atime / 1000
   174  	st.AtimeNsec = (atime % 1000) * 1000000
   175  	mtime := int64(jsSt.Get("mtimeMs").Int())
   176  	st.Mtime = mtime / 1000
   177  	st.MtimeNsec = (mtime % 1000) * 1000000
   178  	ctime := int64(jsSt.Get("ctimeMs").Int())
   179  	st.Ctime = ctime / 1000
   180  	st.CtimeNsec = (ctime % 1000) * 1000000
   181  }
   182  
   183  func Stat(path string, st *Stat_t) error {
   184  	if err := checkPath(path); err != nil {
   185  		return err
   186  	}
   187  	jsSt, err := fsCall("stat", path)
   188  	if err != nil {
   189  		return err
   190  	}
   191  	setStat(st, jsSt)
   192  	return nil
   193  }
   194  
   195  func Lstat(path string, st *Stat_t) error {
   196  	if err := checkPath(path); err != nil {
   197  		return err
   198  	}
   199  	jsSt, err := fsCall("lstat", path)
   200  	if err != nil {
   201  		return err
   202  	}
   203  	setStat(st, jsSt)
   204  	return nil
   205  }
   206  
   207  func Fstat(fd int, st *Stat_t) error {
   208  	jsSt, err := fsCall("fstat", fd)
   209  	if err != nil {
   210  		return err
   211  	}
   212  	setStat(st, jsSt)
   213  	return nil
   214  }
   215  
   216  func Unlink(path string) error {
   217  	if err := checkPath(path); err != nil {
   218  		return err
   219  	}
   220  	_, err := fsCall("unlink", path)
   221  	return err
   222  }
   223  
   224  func Rmdir(path string) error {
   225  	if err := checkPath(path); err != nil {
   226  		return err
   227  	}
   228  	_, err := fsCall("rmdir", path)
   229  	return err
   230  }
   231  
   232  func Chmod(path string, mode uint32) error {
   233  	if err := checkPath(path); err != nil {
   234  		return err
   235  	}
   236  	_, err := fsCall("chmod", path, mode)
   237  	return err
   238  }
   239  
   240  func Fchmod(fd int, mode uint32) error {
   241  	_, err := fsCall("fchmod", fd, mode)
   242  	return err
   243  }
   244  
   245  func Chown(path string, uid, gid int) error {
   246  	if err := checkPath(path); err != nil {
   247  		return err
   248  	}
   249  	_, err := fsCall("chown", path, uint32(uid), uint32(gid))
   250  	return err
   251  }
   252  
   253  func Fchown(fd int, uid, gid int) error {
   254  	_, err := fsCall("fchown", fd, uint32(uid), uint32(gid))
   255  	return err
   256  }
   257  
   258  func Lchown(path string, uid, gid int) error {
   259  	if err := checkPath(path); err != nil {
   260  		return err
   261  	}
   262  	if jsFS.Get("lchown") == js.Undefined() {
   263  		// fs.lchown is unavailable on Linux until Node.js 10.6.0
   264  		// TODO(neelance): remove when we require at least this Node.js version
   265  		return ENOSYS
   266  	}
   267  	_, err := fsCall("lchown", path, uint32(uid), uint32(gid))
   268  	return err
   269  }
   270  
   271  func UtimesNano(path string, ts []Timespec) error {
   272  	if err := checkPath(path); err != nil {
   273  		return err
   274  	}
   275  	if len(ts) != 2 {
   276  		return EINVAL
   277  	}
   278  	atime := ts[0].Sec
   279  	mtime := ts[1].Sec
   280  	_, err := fsCall("utimes", path, atime, mtime)
   281  	return err
   282  }
   283  
   284  func Rename(from, to string) error {
   285  	if err := checkPath(from); err != nil {
   286  		return err
   287  	}
   288  	if err := checkPath(to); err != nil {
   289  		return err
   290  	}
   291  	_, err := fsCall("rename", from, to)
   292  	return err
   293  }
   294  
   295  func Truncate(path string, length int64) error {
   296  	if err := checkPath(path); err != nil {
   297  		return err
   298  	}
   299  	_, err := fsCall("truncate", path, length)
   300  	return err
   301  }
   302  
   303  func Ftruncate(fd int, length int64) error {
   304  	_, err := fsCall("ftruncate", fd, length)
   305  	return err
   306  }
   307  
   308  func Getcwd(buf []byte) (n int, err error) {
   309  	defer recoverErr(&err)
   310  	cwd := jsProcess.Call("cwd").String()
   311  	n = copy(buf, cwd)
   312  	return
   313  }
   314  
   315  func Chdir(path string) (err error) {
   316  	if err := checkPath(path); err != nil {
   317  		return err
   318  	}
   319  	defer recoverErr(&err)
   320  	jsProcess.Call("chdir", path)
   321  	return
   322  }
   323  
   324  func Fchdir(fd int) error {
   325  	f, err := fdToFile(fd)
   326  	if err != nil {
   327  		return err
   328  	}
   329  	return Chdir(f.path)
   330  }
   331  
   332  func Readlink(path string, buf []byte) (n int, err error) {
   333  	if err := checkPath(path); err != nil {
   334  		return 0, err
   335  	}
   336  	dst, err := fsCall("readlink", path)
   337  	if err != nil {
   338  		return 0, err
   339  	}
   340  	n = copy(buf, dst.String())
   341  	return n, nil
   342  }
   343  
   344  func Link(path, link string) error {
   345  	if err := checkPath(path); err != nil {
   346  		return err
   347  	}
   348  	if err := checkPath(link); err != nil {
   349  		return err
   350  	}
   351  	_, err := fsCall("link", path, link)
   352  	return err
   353  }
   354  
   355  func Symlink(path, link string) error {
   356  	if err := checkPath(path); err != nil {
   357  		return err
   358  	}
   359  	if err := checkPath(link); err != nil {
   360  		return err
   361  	}
   362  	_, err := fsCall("symlink", path, link)
   363  	return err
   364  }
   365  
   366  func Fsync(fd int) error {
   367  	_, err := fsCall("fsync", fd)
   368  	return err
   369  }
   370  
   371  func Read(fd int, b []byte) (int, error) {
   372  	f, err := fdToFile(fd)
   373  	if err != nil {
   374  		return 0, err
   375  	}
   376  
   377  	if f.seeked {
   378  		n, err := Pread(fd, b, f.pos)
   379  		f.pos += int64(n)
   380  		return n, err
   381  	}
   382  
   383  	buf := uint8Array.New(len(b))
   384  	n, err := fsCall("read", fd, buf, 0, len(b), nil)
   385  	if err != nil {
   386  		return 0, err
   387  	}
   388  	js.CopyBytesToGo(b, buf)
   389  
   390  	n2 := n.Int()
   391  	f.pos += int64(n2)
   392  	return n2, err
   393  }
   394  
   395  func Write(fd int, b []byte) (int, error) {
   396  	f, err := fdToFile(fd)
   397  	if err != nil {
   398  		return 0, err
   399  	}
   400  
   401  	if f.seeked {
   402  		n, err := Pwrite(fd, b, f.pos)
   403  		f.pos += int64(n)
   404  		return n, err
   405  	}
   406  
   407  	buf := uint8Array.New(len(b))
   408  	js.CopyBytesToJS(buf, b)
   409  	n, err := fsCall("write", fd, buf, 0, len(b), nil)
   410  	if err != nil {
   411  		return 0, err
   412  	}
   413  	n2 := n.Int()
   414  	f.pos += int64(n2)
   415  	return n2, err
   416  }
   417  
   418  func Pread(fd int, b []byte, offset int64) (int, error) {
   419  	buf := uint8Array.New(len(b))
   420  	n, err := fsCall("read", fd, buf, 0, len(b), offset)
   421  	if err != nil {
   422  		return 0, err
   423  	}
   424  	js.CopyBytesToGo(b, buf)
   425  	return n.Int(), nil
   426  }
   427  
   428  func Pwrite(fd int, b []byte, offset int64) (int, error) {
   429  	buf := uint8Array.New(len(b))
   430  	js.CopyBytesToJS(buf, b)
   431  	n, err := fsCall("write", fd, buf, 0, len(b), offset)
   432  	if err != nil {
   433  		return 0, err
   434  	}
   435  	return n.Int(), nil
   436  }
   437  
   438  func Seek(fd int, offset int64, whence int) (int64, error) {
   439  	f, err := fdToFile(fd)
   440  	if err != nil {
   441  		return 0, err
   442  	}
   443  
   444  	var newPos int64
   445  	switch whence {
   446  	case io.SeekStart:
   447  		newPos = offset
   448  	case io.SeekCurrent:
   449  		newPos = f.pos + offset
   450  	case io.SeekEnd:
   451  		var st Stat_t
   452  		if err := Fstat(fd, &st); err != nil {
   453  			return 0, err
   454  		}
   455  		newPos = st.Size + offset
   456  	default:
   457  		return 0, errnoErr(EINVAL)
   458  	}
   459  
   460  	if newPos < 0 {
   461  		return 0, errnoErr(EINVAL)
   462  	}
   463  
   464  	f.seeked = true
   465  	f.pos = newPos
   466  	return newPos, nil
   467  }
   468  
   469  func Dup(fd int) (int, error) {
   470  	return 0, ENOSYS
   471  }
   472  
   473  func Dup2(fd, newfd int) error {
   474  	return ENOSYS
   475  }
   476  
   477  func Pipe(fd []int) error {
   478  	return ENOSYS
   479  }
   480  
   481  func fsCall(name string, args ...interface{}) (js.Value, error) {
   482  	type callResult struct {
   483  		val js.Value
   484  		err error
   485  	}
   486  
   487  	c := make(chan callResult, 1)
   488  	jsFS.Call(name, append(args, js.FuncOf(func(this js.Value, args []js.Value) interface{} {
   489  		var res callResult
   490  
   491  		if len(args) >= 1 { // on Node.js 8, fs.utimes calls the callback without any arguments
   492  			if jsErr := args[0]; jsErr != js.Null() {
   493  				res.err = mapJSError(jsErr)
   494  			}
   495  		}
   496  
   497  		res.val = js.Undefined()
   498  		if len(args) >= 2 {
   499  			res.val = args[1]
   500  		}
   501  
   502  		c <- res
   503  		return nil
   504  	}))...)
   505  	res := <-c
   506  	return res.val, res.err
   507  }
   508  
   509  // checkPath checks that the path is not empty and that it contains no null characters.
   510  func checkPath(path string) error {
   511  	if path == "" {
   512  		return EINVAL
   513  	}
   514  	for i := 0; i < len(path); i++ {
   515  		if path[i] == '\x00' {
   516  			return EINVAL
   517  		}
   518  	}
   519  	return nil
   520  }
   521  
   522  func recoverErr(errPtr *error) {
   523  	if err := recover(); err != nil {
   524  		jsErr, ok := err.(js.Error)
   525  		if !ok {
   526  			panic(err)
   527  		}
   528  		*errPtr = mapJSError(jsErr.Value)
   529  	}
   530  }
   531  
   532  // mapJSError maps an error given by Node.js to the appropriate Go error
   533  func mapJSError(jsErr js.Value) error {
   534  	errno, ok := errnoByCode[jsErr.Get("code").String()]
   535  	if !ok {
   536  		panic(jsErr)
   537  	}
   538  	return errnoErr(Errno(errno))
   539  }
   540  

View as plain text