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.
16 // Provided by package runtime.
17 func now() (sec
int64, nsec
int32)
19 var jsProcess
= js
.Global().Get("process")
20 var jsFS
= js
.Global().Get("fs")
21 var constants
= jsFS
.Get("constants")
23 var uint8Array
= js
.Global().Get("Uint8Array")
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()
37 dirIdx
int // entries[:dirIdx] have already been returned in ReadDirent
42 var filesMu sync
.Mutex
43 var files
= map[int]*jsFile
{
49 func fdToFile(fd
int) (*jsFile
, error
) {
59 func Open(path
string, openmode
int, perm
uint32) (int, error
) {
60 if err
:= checkPath(path
); err
!= nil {
65 if openmode
&O_WRONLY
!= 0 {
68 if openmode
&O_RDWR
!= 0 {
71 if openmode
&O_CREATE
!= 0 {
74 if openmode
&O_TRUNC
!= 0 {
77 if openmode
&O_APPEND
!= 0 {
80 if openmode
&O_EXCL
!= 0 {
83 if openmode
&O_SYNC
!= 0 {
84 return 0, errors
.New("syscall.Open: O_SYNC is not supported by js/wasm")
87 jsFD
, err
:= fsCall("open", path
, flags
, perm
)
94 if stat
, err
:= fsCall("fstat", fd
); err
== nil && stat
.Call("isDirectory").Bool() {
95 dir
, err
:= fsCall("readdir", path
)
99 entries
= make([]string, dir
.Length())
100 for i
:= range entries
{
101 entries
[i
] = dir
.Index(i
).String()
115 func Close(fd
int) error
{
119 _
, err
:= fsCall("close", fd
)
123 func CloseOnExec(fd
int) {
124 // nothing to do - no exec
127 func Mkdir(path
string, perm
uint32) error
{
128 if err
:= checkPath(path
); err
!= nil {
131 _
, err
:= fsCall("mkdir", path
, perm
)
135 func ReadDirent(fd
int, buf
[]byte) (int, error
) {
136 f
, err
:= fdToFile(fd
)
140 if f
.entries
== nil {
145 for f
.dirIdx
< len(f
.entries
) {
146 entry
:= f
.entries
[f
.dirIdx
]
152 buf
[1] = byte(l
>> 8)
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
184 func Stat(path
string, st
*Stat_t
) error
{
185 if err
:= checkPath(path
); err
!= nil {
188 jsSt
, err
:= fsCall("stat", path
)
196 func Lstat(path
string, st
*Stat_t
) error
{
197 if err
:= checkPath(path
); err
!= nil {
200 jsSt
, err
:= fsCall("lstat", path
)
208 func Fstat(fd
int, st
*Stat_t
) error
{
209 jsSt
, err
:= fsCall("fstat", fd
)
217 func Unlink(path
string) error
{
218 if err
:= checkPath(path
); err
!= nil {
221 _
, err
:= fsCall("unlink", path
)
225 func Rmdir(path
string) error
{
226 if err
:= checkPath(path
); err
!= nil {
229 _
, err
:= fsCall("rmdir", path
)
233 func Chmod(path
string, mode
uint32) error
{
234 if err
:= checkPath(path
); err
!= nil {
237 _
, err
:= fsCall("chmod", path
, mode
)
241 func Fchmod(fd
int, mode
uint32) error
{
242 _
, err
:= fsCall("fchmod", fd
, mode
)
246 func Chown(path
string, uid
, gid
int) error
{
247 if err
:= checkPath(path
); err
!= nil {
250 _
, err
:= fsCall("chown", path
, uint32(uid
), uint32(gid
))
254 func Fchown(fd
int, uid
, gid
int) error
{
255 _
, err
:= fsCall("fchown", fd
, uint32(uid
), uint32(gid
))
259 func Lchown(path
string, uid
, gid
int) error
{
260 if err
:= checkPath(path
); err
!= nil {
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
268 _
, err
:= fsCall("lchown", path
, uint32(uid
), uint32(gid
))
272 func UtimesNano(path
string, ts
[]Timespec
) error
{
273 if err
:= checkPath(path
); err
!= nil {
281 _
, err
:= fsCall("utimes", path
, atime
, mtime
)
285 func Rename(from
, to
string) error
{
286 if err
:= checkPath(from
); err
!= nil {
289 if err
:= checkPath(to
); err
!= nil {
292 _
, err
:= fsCall("rename", from
, to
)
296 func Truncate(path
string, length
int64) error
{
297 if err
:= checkPath(path
); err
!= nil {
300 _
, err
:= fsCall("truncate", path
, length
)
304 func Ftruncate(fd
int, length
int64) error
{
305 _
, err
:= fsCall("ftruncate", fd
, length
)
309 func Getcwd(buf
[]byte) (n
int, err error
) {
310 defer recoverErr(&err
)
311 cwd
:= jsProcess
.Call("cwd").String()
316 func Chdir(path
string) (err error
) {
317 if err
:= checkPath(path
); err
!= nil {
320 defer recoverErr(&err
)
321 jsProcess
.Call("chdir", path
)
325 func Fchdir(fd
int) error
{
326 f
, err
:= fdToFile(fd
)
333 func Readlink(path
string, buf
[]byte) (n
int, err error
) {
334 if err
:= checkPath(path
); err
!= nil {
337 dst
, err
:= fsCall("readlink", path
)
341 n
= copy(buf
, dst
.String())
345 func Link(path
, link
string) error
{
346 if err
:= checkPath(path
); err
!= nil {
349 if err
:= checkPath(link
); err
!= nil {
352 _
, err
:= fsCall("link", path
, link
)
356 func Symlink(path
, link
string) error
{
357 if err
:= checkPath(path
); err
!= nil {
360 if err
:= checkPath(link
); err
!= nil {
363 _
, err
:= fsCall("symlink", path
, link
)
367 func Fsync(fd
int) error
{
368 _
, err
:= fsCall("fsync", fd
)
372 func Read(fd
int, b
[]byte) (int, error
) {
373 f
, err
:= fdToFile(fd
)
379 n
, err
:= Pread(fd
, b
, f
.pos
)
384 buf
:= uint8Array
.New(len(b
))
385 n
, err
:= fsCall("read", fd
, buf
, 0, len(b
), nil)
389 js
.CopyBytesToGo(b
, buf
)
396 func Write(fd
int, b
[]byte) (int, error
) {
397 f
, err
:= fdToFile(fd
)
403 n
, err
:= Pwrite(fd
, b
, f
.pos
)
408 if faketime
&& (fd
== 1 || fd
== 2) {
409 n
:= faketimeWrite(fd
, b
)
411 return 0, errnoErr(Errno(-n
))
416 buf
:= uint8Array
.New(len(b
))
417 js
.CopyBytesToJS(buf
, b
)
418 n
, err
:= fsCall("write", fd
, buf
, 0, len(b
), nil)
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
)
433 js
.CopyBytesToGo(b
, buf
)
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
)
447 func Seek(fd
int, offset
int64, whence
int) (int64, error
) {
448 f
, err
:= fdToFile(fd
)
458 newPos
= f
.pos
+ offset
461 if err
:= Fstat(fd
, &st
); err
!= nil {
464 newPos
= st
.Size
+ offset
466 return 0, errnoErr(EINVAL
)
470 return 0, errnoErr(EINVAL
)
474 f
.dirIdx
= 0 // Reset directory read position. See issue 35767.
479 func Dup(fd
int) (int, error
) {
483 func Dup2(fd
, newfd
int) error
{
487 func Pipe(fd
[]int) error
{
491 func fsCall(name
string, args
...interface{}) (js
.Value
, error
) {
492 type callResult
struct {
497 c
:= make(chan callResult
, 1)
498 jsFS
.Call(name
, append(args
, js
.FuncOf(func(this js
.Value
, args
[]js
.Value
) interface{} {
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
)
507 res
.val
= js
.Undefined()
516 return res
.val
, res
.err
519 // checkPath checks that the path is not empty and that it contains no null characters.
520 func checkPath(path
string) error
{
524 for i
:= 0; i
< len(path
); i
++ {
525 if path
[i
] == '\x00' {
532 func recoverErr(errPtr
*error
) {
533 if err
:= recover(); err
!= nil {
534 jsErr
, ok
:= err
.(js
.Error
)
538 *errPtr
= mapJSError(jsErr
.Value
)
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()]
548 return errnoErr(Errno(errno
))