...
Run Format

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

View as plain text