fs_js.go 11 KB

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