...

Source file src/pkg/syscall/fs_js.go

     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