github.com/c12o16h1/go/src@v0.0.0-20200114212001-5a151c0f00ed/syscall/fs_js.go (about)

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