| // Copyright 2015 The Emscripten Authors. All rights reserved. |
| // Emscripten is available under two separate licenses, the MIT license and the |
| // University of Illinois/NCSA Open Source License. Both these licenses can be |
| // found in the LICENSE file. |
| |
| mergeInto(LibraryManager.library, { |
| $SYSCALLS__deps: ['$PATH', |
| #if FILESYSTEM && SYSCALLS_REQUIRE_FILESYSTEM |
| '$FS', |
| #endif |
| #if SYSCALL_DEBUG |
| '$ERRNO_MESSAGES' |
| #endif |
| ], |
| $SYSCALLS: { |
| #if SYSCALLS_REQUIRE_FILESYSTEM |
| // global constants |
| DEFAULT_POLLMASK: {{{ cDefine('POLLIN') }}} | {{{ cDefine('POLLOUT') }}}, |
| |
| // global state |
| mappings: {}, |
| umask: 0x1FF, // S_IRWXU | S_IRWXG | S_IRWXO |
| |
| // shared utilities |
| calculateAt: function(dirfd, path) { |
| if (path[0] !== '/') { |
| // relative path |
| var dir; |
| if (dirfd === {{{ cDefine('AT_FDCWD') }}}) { |
| dir = FS.cwd(); |
| } else { |
| var dirstream = FS.getStream(dirfd); |
| if (!dirstream) throw new FS.ErrnoError({{{ cDefine('EBADF') }}}); |
| dir = dirstream.path; |
| } |
| path = PATH.join2(dir, path); |
| } |
| return path; |
| }, |
| |
| doStat: function(func, path, buf) { |
| try { |
| var stat = func(path); |
| } catch (e) { |
| if (e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node))) { |
| // an error occurred while trying to look up the path; we should just report ENOTDIR |
| return -{{{ cDefine('ENOTDIR') }}}; |
| } |
| throw e; |
| } |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_dev, 'stat.dev', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.__st_dev_padding, '0', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.__st_ino_truncated, 'stat.ino', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_mode, 'stat.mode', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_nlink, 'stat.nlink', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_uid, 'stat.uid', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_gid, 'stat.gid', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_rdev, 'stat.rdev', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.__st_rdev_padding, '0', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_size, 'stat.size', 'i64') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_blksize, '4096', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_blocks, 'stat.blocks', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_sec, '(stat.atime.getTime() / 1000)|0', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_nsec, '0', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_sec, '(stat.mtime.getTime() / 1000)|0', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_nsec, '0', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_sec, '(stat.ctime.getTime() / 1000)|0', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_nsec, '0', 'i32') }}}; |
| {{{ makeSetValue('buf', C_STRUCTS.stat.st_ino, 'stat.ino', 'i64') }}}; |
| return 0; |
| }, |
| doMsync: function(addr, stream, len, flags) { |
| var buffer = new Uint8Array(HEAPU8.subarray(addr, addr + len)); |
| FS.msync(stream, buffer, 0, len, flags); |
| }, |
| doMkdir: function(path, mode) { |
| // remove a trailing slash, if one - /a/b/ has basename of '', but |
| // we want to create b in the context of this function |
| path = PATH.normalize(path); |
| if (path[path.length-1] === '/') path = path.substr(0, path.length-1); |
| FS.mkdir(path, mode, 0); |
| return 0; |
| }, |
| doMknod: function(path, mode, dev) { |
| // we don't want this in the JS API as it uses mknod to create all nodes. |
| switch (mode & {{{ cDefine('S_IFMT') }}}) { |
| case {{{ cDefine('S_IFREG') }}}: |
| case {{{ cDefine('S_IFCHR') }}}: |
| case {{{ cDefine('S_IFBLK') }}}: |
| case {{{ cDefine('S_IFIFO') }}}: |
| case {{{ cDefine('S_IFSOCK') }}}: |
| break; |
| default: return -{{{ cDefine('EINVAL') }}}; |
| } |
| FS.mknod(path, mode, dev); |
| return 0; |
| }, |
| doReadlink: function(path, buf, bufsize) { |
| if (bufsize <= 0) return -{{{ cDefine('EINVAL') }}}; |
| var ret = FS.readlink(path); |
| |
| var len = Math.min(bufsize, lengthBytesUTF8(ret)); |
| var endChar = HEAP8[buf+len]; |
| stringToUTF8(ret, buf, bufsize+1); |
| // readlink is one of the rare functions that write out a C string, but does never append a null to the output buffer(!) |
| // stringToUTF8() always appends a null byte, so restore the character under the null byte after the write. |
| HEAP8[buf+len] = endChar; |
| |
| return len; |
| }, |
| doAccess: function(path, amode) { |
| if (amode & ~{{{ cDefine('S_IRWXO') }}}) { |
| // need a valid mode |
| return -{{{ cDefine('EINVAL') }}}; |
| } |
| var node; |
| var lookup = FS.lookupPath(path, { follow: true }); |
| node = lookup.node; |
| if (!node) { |
| return -{{{ cDefine('ENOENT') }}}; |
| } |
| var perms = ''; |
| if (amode & {{{ cDefine('R_OK') }}}) perms += 'r'; |
| if (amode & {{{ cDefine('W_OK') }}}) perms += 'w'; |
| if (amode & {{{ cDefine('X_OK') }}}) perms += 'x'; |
| if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) { |
| return -{{{ cDefine('EACCES') }}}; |
| } |
| return 0; |
| }, |
| doDup: function(path, flags, suggestFD) { |
| var suggest = FS.getStream(suggestFD); |
| if (suggest) FS.close(suggest); |
| return FS.open(path, flags, 0, suggestFD, suggestFD).fd; |
| }, |
| doReadv: function(stream, iov, iovcnt, offset) { |
| var ret = 0; |
| for (var i = 0; i < iovcnt; i++) { |
| var ptr = {{{ makeGetValue('iov', 'i*8', 'i32') }}}; |
| var len = {{{ makeGetValue('iov', 'i*8 + 4', 'i32') }}}; |
| var curr = FS.read(stream, {{{ heapAndOffset('HEAP8', 'ptr') }}}, len, offset); |
| if (curr < 0) return -1; |
| ret += curr; |
| if (curr < len) break; // nothing more to read |
| } |
| return ret; |
| }, |
| doWritev: function(stream, iov, iovcnt, offset) { |
| var ret = 0; |
| for (var i = 0; i < iovcnt; i++) { |
| var ptr = {{{ makeGetValue('iov', 'i*8', 'i32') }}}; |
| var len = {{{ makeGetValue('iov', 'i*8 + 4', 'i32') }}}; |
| var curr = FS.write(stream, {{{ heapAndOffset('HEAP8', 'ptr') }}}, len, offset); |
| if (curr < 0) return -1; |
| ret += curr; |
| } |
| return ret; |
| }, |
| #else |
| // MEMFS filesystem disabled lite handling of stdout and stderr: |
| buffers: [null, [], []], // 1 => stdout, 2 => stderr |
| printChar: function(stream, curr) { |
| var buffer = SYSCALLS.buffers[stream]; |
| #if ASSERTIONS |
| assert(buffer); |
| #endif |
| if (curr === 0 || curr === {{{ charCode('\n') }}}) { |
| (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0)); |
| buffer.length = 0; |
| } else { |
| buffer.push(curr); |
| } |
| }, |
| #endif // SYSCALLS_REQUIRE_FILESYSTEM |
| |
| // arguments handling |
| |
| varargs: 0, |
| |
| get: function(varargs) { |
| SYSCALLS.varargs += 4; |
| var ret = {{{ makeGetValue('SYSCALLS.varargs', '-4', 'i32') }}}; |
| #if SYSCALL_DEBUG |
| err(' (raw: "' + ret + '")'); |
| #endif |
| return ret; |
| }, |
| getStr: function() { |
| var ret = UTF8ToString(SYSCALLS.get()); |
| #if SYSCALL_DEBUG |
| err(' (str: "' + ret + '")'); |
| #endif |
| return ret; |
| }, |
| #if SYSCALLS_REQUIRE_FILESYSTEM |
| getStreamFromFD: function(fd) { |
| // TODO: when all syscalls use wasi, can remove the next line |
| if (fd === undefined) fd = SYSCALLS.get(); |
| var stream = FS.getStream(fd); |
| if (!stream) throw new FS.ErrnoError({{{ cDefine('EBADF') }}}); |
| #if SYSCALL_DEBUG |
| err(' (stream: "' + stream.path + '")'); |
| #endif |
| return stream; |
| }, |
| #endif // SYSCALLS_REQUIRE_FILESYSTEM |
| get64: function() { |
| var low = SYSCALLS.get(), high = SYSCALLS.get(); |
| #if ASSERTIONS |
| if (low >= 0) assert(high === 0); |
| else assert(high === -1); |
| #endif |
| #if SYSCALL_DEBUG |
| err(' (i64: "' + low + '")'); |
| #endif |
| return low; |
| }, |
| getZero: function() { |
| #if ASSERTIONS |
| assert(SYSCALLS.get() === 0); |
| #else |
| SYSCALLS.get(); |
| #endif |
| } |
| }, |
| }); |