blob: 3646907cb5301151deb07ed5eae52faa6f61674c [file] [log] [blame] [edit]
// 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
}
},
});