Source file src/pkg/syscall/fs_js.go
1
2
3
4
5
6
7 package syscall
8
9 import (
10 "errors"
11 "io"
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 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
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
264
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 {
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
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
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