Source file
src/syscall/fs_js.go
Documentation: syscall
1
2
3
4
5
6
7
8 package syscall
9
10 import (
11 "errors"
12 "sync"
13 "syscall/js"
14 )
15
16
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
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 if path[0] != '/' {
106 cwd := jsProcess.Call("cwd").String()
107 path = cwd + "/" + path
108 }
109 f := &jsFile{
110 path: path,
111 entries: entries,
112 }
113 filesMu.Lock()
114 files[fd] = f
115 filesMu.Unlock()
116 return fd, nil
117 }
118
119 func Close(fd int) error {
120 filesMu.Lock()
121 delete(files, fd)
122 filesMu.Unlock()
123 _, err := fsCall("close", fd)
124 return err
125 }
126
127 func CloseOnExec(fd int) {
128
129 }
130
131 func Mkdir(path string, perm uint32) error {
132 if err := checkPath(path); err != nil {
133 return err
134 }
135 _, err := fsCall("mkdir", path, perm)
136 return err
137 }
138
139 func ReadDirent(fd int, buf []byte) (int, error) {
140 f, err := fdToFile(fd)
141 if err != nil {
142 return 0, err
143 }
144 if f.entries == nil {
145 return 0, EINVAL
146 }
147
148 n := 0
149 for f.dirIdx < len(f.entries) {
150 entry := f.entries[f.dirIdx]
151 l := 2 + len(entry)
152 if l > len(buf) {
153 break
154 }
155 buf[0] = byte(l)
156 buf[1] = byte(l >> 8)
157 copy(buf[2:], entry)
158 buf = buf[l:]
159 n += l
160 f.dirIdx++
161 }
162
163 return n, nil
164 }
165
166 func setStat(st *Stat_t, jsSt js.Value) {
167 st.Dev = int64(jsSt.Get("dev").Int())
168 st.Ino = uint64(jsSt.Get("ino").Int())
169 st.Mode = uint32(jsSt.Get("mode").Int())
170 st.Nlink = uint32(jsSt.Get("nlink").Int())
171 st.Uid = uint32(jsSt.Get("uid").Int())
172 st.Gid = uint32(jsSt.Get("gid").Int())
173 st.Rdev = int64(jsSt.Get("rdev").Int())
174 st.Size = int64(jsSt.Get("size").Int())
175 st.Blksize = int32(jsSt.Get("blksize").Int())
176 st.Blocks = int32(jsSt.Get("blocks").Int())
177 atime := int64(jsSt.Get("atimeMs").Int())
178 st.Atime = atime / 1000
179 st.AtimeNsec = (atime % 1000) * 1000000
180 mtime := int64(jsSt.Get("mtimeMs").Int())
181 st.Mtime = mtime / 1000
182 st.MtimeNsec = (mtime % 1000) * 1000000
183 ctime := int64(jsSt.Get("ctimeMs").Int())
184 st.Ctime = ctime / 1000
185 st.CtimeNsec = (ctime % 1000) * 1000000
186 }
187
188 func Stat(path string, st *Stat_t) error {
189 if err := checkPath(path); err != nil {
190 return err
191 }
192 jsSt, err := fsCall("stat", path)
193 if err != nil {
194 return err
195 }
196 setStat(st, jsSt)
197 return nil
198 }
199
200 func Lstat(path string, st *Stat_t) error {
201 if err := checkPath(path); err != nil {
202 return err
203 }
204 jsSt, err := fsCall("lstat", path)
205 if err != nil {
206 return err
207 }
208 setStat(st, jsSt)
209 return nil
210 }
211
212 func Fstat(fd int, st *Stat_t) error {
213 jsSt, err := fsCall("fstat", fd)
214 if err != nil {
215 return err
216 }
217 setStat(st, jsSt)
218 return nil
219 }
220
221 func Unlink(path string) error {
222 if err := checkPath(path); err != nil {
223 return err
224 }
225 _, err := fsCall("unlink", path)
226 return err
227 }
228
229 func Rmdir(path string) error {
230 if err := checkPath(path); err != nil {
231 return err
232 }
233 _, err := fsCall("rmdir", path)
234 return err
235 }
236
237 func Chmod(path string, mode uint32) error {
238 if err := checkPath(path); err != nil {
239 return err
240 }
241 _, err := fsCall("chmod", path, mode)
242 return err
243 }
244
245 func Fchmod(fd int, mode uint32) error {
246 _, err := fsCall("fchmod", fd, mode)
247 return err
248 }
249
250 func Chown(path string, uid, gid int) error {
251 if err := checkPath(path); err != nil {
252 return err
253 }
254 _, err := fsCall("chown", path, uint32(uid), uint32(gid))
255 return err
256 }
257
258 func Fchown(fd int, uid, gid int) error {
259 _, err := fsCall("fchown", fd, uint32(uid), uint32(gid))
260 return err
261 }
262
263 func Lchown(path string, uid, gid int) error {
264 if err := checkPath(path); err != nil {
265 return err
266 }
267 if jsFS.Get("lchown").IsUndefined() {
268
269
270 return ENOSYS
271 }
272 _, err := fsCall("lchown", path, uint32(uid), uint32(gid))
273 return err
274 }
275
276 func UtimesNano(path string, ts []Timespec) error {
277 if err := checkPath(path); err != nil {
278 return err
279 }
280 if len(ts) != 2 {
281 return EINVAL
282 }
283 atime := ts[0].Sec
284 mtime := ts[1].Sec
285 _, err := fsCall("utimes", path, atime, mtime)
286 return err
287 }
288
289 func Rename(from, to string) error {
290 if err := checkPath(from); err != nil {
291 return err
292 }
293 if err := checkPath(to); err != nil {
294 return err
295 }
296 _, err := fsCall("rename", from, to)
297 return err
298 }
299
300 func Truncate(path string, length int64) error {
301 if err := checkPath(path); err != nil {
302 return err
303 }
304 _, err := fsCall("truncate", path, length)
305 return err
306 }
307
308 func Ftruncate(fd int, length int64) error {
309 _, err := fsCall("ftruncate", fd, length)
310 return err
311 }
312
313 func Getcwd(buf []byte) (n int, err error) {
314 defer recoverErr(&err)
315 cwd := jsProcess.Call("cwd").String()
316 n = copy(buf, cwd)
317 return
318 }
319
320 func Chdir(path string) (err error) {
321 if err := checkPath(path); err != nil {
322 return err
323 }
324 defer recoverErr(&err)
325 jsProcess.Call("chdir", path)
326 return
327 }
328
329 func Fchdir(fd int) error {
330 f, err := fdToFile(fd)
331 if err != nil {
332 return err
333 }
334 return Chdir(f.path)
335 }
336
337 func Readlink(path string, buf []byte) (n int, err error) {
338 if err := checkPath(path); err != nil {
339 return 0, err
340 }
341 dst, err := fsCall("readlink", path)
342 if err != nil {
343 return 0, err
344 }
345 n = copy(buf, dst.String())
346 return n, nil
347 }
348
349 func Link(path, link string) error {
350 if err := checkPath(path); err != nil {
351 return err
352 }
353 if err := checkPath(link); err != nil {
354 return err
355 }
356 _, err := fsCall("link", path, link)
357 return err
358 }
359
360 func Symlink(path, link string) error {
361 if err := checkPath(path); err != nil {
362 return err
363 }
364 if err := checkPath(link); err != nil {
365 return err
366 }
367 _, err := fsCall("symlink", path, link)
368 return err
369 }
370
371 func Fsync(fd int) error {
372 _, err := fsCall("fsync", fd)
373 return err
374 }
375
376 func Read(fd int, b []byte) (int, error) {
377 f, err := fdToFile(fd)
378 if err != nil {
379 return 0, err
380 }
381
382 if f.seeked {
383 n, err := Pread(fd, b, f.pos)
384 f.pos += int64(n)
385 return n, err
386 }
387
388 buf := uint8Array.New(len(b))
389 n, err := fsCall("read", fd, buf, 0, len(b), nil)
390 if err != nil {
391 return 0, err
392 }
393 js.CopyBytesToGo(b, buf)
394
395 n2 := n.Int()
396 f.pos += int64(n2)
397 return n2, err
398 }
399
400 func Write(fd int, b []byte) (int, error) {
401 f, err := fdToFile(fd)
402 if err != nil {
403 return 0, err
404 }
405
406 if f.seeked {
407 n, err := Pwrite(fd, b, f.pos)
408 f.pos += int64(n)
409 return n, err
410 }
411
412 if faketime && (fd == 1 || fd == 2) {
413 n := faketimeWrite(fd, b)
414 if n < 0 {
415 return 0, errnoErr(Errno(-n))
416 }
417 return n, nil
418 }
419
420 buf := uint8Array.New(len(b))
421 js.CopyBytesToJS(buf, b)
422 n, err := fsCall("write", fd, buf, 0, len(b), nil)
423 if err != nil {
424 return 0, err
425 }
426 n2 := n.Int()
427 f.pos += int64(n2)
428 return n2, err
429 }
430
431 func Pread(fd int, b []byte, offset int64) (int, error) {
432 buf := uint8Array.New(len(b))
433 n, err := fsCall("read", fd, buf, 0, len(b), offset)
434 if err != nil {
435 return 0, err
436 }
437 js.CopyBytesToGo(b, buf)
438 return n.Int(), nil
439 }
440
441 func Pwrite(fd int, b []byte, offset int64) (int, error) {
442 buf := uint8Array.New(len(b))
443 js.CopyBytesToJS(buf, b)
444 n, err := fsCall("write", fd, buf, 0, len(b), offset)
445 if err != nil {
446 return 0, err
447 }
448 return n.Int(), nil
449 }
450
451 func Seek(fd int, offset int64, whence int) (int64, error) {
452 f, err := fdToFile(fd)
453 if err != nil {
454 return 0, err
455 }
456
457 var newPos int64
458 switch whence {
459 case 0:
460 newPos = offset
461 case 1:
462 newPos = f.pos + offset
463 case 2:
464 var st Stat_t
465 if err := Fstat(fd, &st); err != nil {
466 return 0, err
467 }
468 newPos = st.Size + offset
469 default:
470 return 0, errnoErr(EINVAL)
471 }
472
473 if newPos < 0 {
474 return 0, errnoErr(EINVAL)
475 }
476
477 f.seeked = true
478 f.dirIdx = 0
479 f.pos = newPos
480 return newPos, nil
481 }
482
483 func Dup(fd int) (int, error) {
484 return 0, ENOSYS
485 }
486
487 func Dup2(fd, newfd int) error {
488 return ENOSYS
489 }
490
491 func Pipe(fd []int) error {
492 return ENOSYS
493 }
494
495 func fsCall(name string, args ...interface{}) (js.Value, error) {
496 type callResult struct {
497 val js.Value
498 err error
499 }
500
501 c := make(chan callResult, 1)
502 f := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
503 var res callResult
504
505 if len(args) >= 1 {
506 if jsErr := args[0]; !jsErr.IsNull() {
507 res.err = mapJSError(jsErr)
508 }
509 }
510
511 res.val = js.Undefined()
512 if len(args) >= 2 {
513 res.val = args[1]
514 }
515
516 c <- res
517 return nil
518 })
519 defer f.Release()
520 jsFS.Call(name, append(args, f)...)
521 res := <-c
522 return res.val, res.err
523 }
524
525
526 func checkPath(path string) error {
527 if path == "" {
528 return EINVAL
529 }
530 for i := 0; i < len(path); i++ {
531 if path[i] == '\x00' {
532 return EINVAL
533 }
534 }
535 return nil
536 }
537
538 func recoverErr(errPtr *error) {
539 if err := recover(); err != nil {
540 jsErr, ok := err.(js.Error)
541 if !ok {
542 panic(err)
543 }
544 *errPtr = mapJSError(jsErr.Value)
545 }
546 }
547
548
549 func mapJSError(jsErr js.Value) error {
550 errno, ok := errnoByCode[jsErr.Get("code").String()]
551 if !ok {
552 panic(jsErr)
553 }
554 return errnoErr(Errno(errno))
555 }
556
View as plain text