blob: 6c44ad3dd8e338983650cb0792e253b67d65ce0b [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <inttypes.h>
#include <netdb.h>
#include <pthread.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <termios.h>
#include "nacl-mounts/base/irt_syscalls.h"
#include "file_system.h"
extern "C" {
#define VLOG_SYSCALL_ENTER() VLOG("SYSCALL: %s(", __func__)
#define VLOG_SYSCALL_EXIT(ret) \
do { \
VLOG(") = %i", ret); \
if (ret == -1) \
VLOG(" errno=%i(%s)", errno, strerror(errno)); \
VLOG("\n"); \
} while (0)
#define LOG_SYSCALL_ENTER() LOG("SYSCALL: %s(", __func__)
#define LOG_SYSCALL_EXIT(ret) \
do { \
LOG(") = %i", ret); \
if (ret == -1) \
LOG(" errno=%i(%s)", errno, strerror(errno)); \
LOG("\n"); \
} while (0)
#define LOG_SYSCALL_STUB(ret, fmt, args...) \
LOG("SYSCALL: [stub] %s(" fmt ") = %i\n", __func__, ##args, ret)
#define WRAP(name) __user_irt_##name
#define REAL(name) libnacl_##name
/*
* Wrapped functions will return 0/errno on success, and pass back the real
* value via an argument. Shuffle the values around to match standard C lib
* API where errors are set in errno.
*/
#define HANDLE_ERRNO(call, success) \
({ \
__typeof(success) ret = (call); \
if (ret) { \
errno = ret; \
ret = -1; \
} else { \
ret = (success); \
} \
ret; \
})
void debug_log(const char* format, ...) {
// This shouldn't be necessary, but can't rely on the underlying C lib
// not messing with the errno value.
int saved = errno;
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
errno = saved;
}
static bool g_exit_called = false;
static int WRAP(open)(const char* pathname, int oflag, mode_t cmode,
int* newfd) {
return FileSystem::GetFileSystem()->open(pathname, oflag, cmode, newfd);
}
#ifndef O_TMPFILE
# define O_TMPFILE 0
#endif
int open(const char* file, int oflag, ...) {
LOG_SYSCALL_ENTER();
int newfd;
mode_t cmode = 0;
// Only peel off the mode if the call requires it. Otherwise we enter
// "undefined" territory and get garbage or a crash or ...
if (oflag & (O_CREAT | O_TMPFILE)) {
va_list ap;
va_start(ap, oflag);
cmode = va_arg(ap, mode_t);
va_end(ap);
}
LOG("file=\"%s\", flags=%#x[", file, oflag);
switch (oflag & O_ACCMODE) {
case O_RDONLY:
LOG("O_RDONLY");
break;
case O_WRONLY:
LOG("O_WRONLY");
break;
case O_RDWR:
LOG("O_RDWR");
break;
}
LOG("], mode=%#o", cmode);
int ret = HANDLE_ERRNO(WRAP(open)(file, oflag, cmode, &newfd), newfd);
LOG_SYSCALL_EXIT(ret);
return ret;
}
static int WRAP(close)(int fd) {
return FileSystem::GetFileSystem()->close(fd);
}
int close(int fd) {
LOG_SYSCALL_ENTER();
LOG("fd=%i", fd);
int ret = HANDLE_ERRNO(WRAP(close)(fd), 0);
LOG_SYSCALL_EXIT(ret);
return ret;
}
static int WRAP(read)(int fd, void* buf, size_t count, size_t* nread) {
return FileSystem::GetFileSystem()->read(fd, (char*)buf, count, nread);
}
ssize_t read(int fd, void* buf, size_t count) {
VLOG_SYSCALL_ENTER();
VLOG("fd=%d, buf=%p, count=%d", fd, buf, count);
ssize_t rv;
ssize_t ret = HANDLE_ERRNO(WRAP(read)(fd, buf, count, (size_t*)&rv), rv);
VLOG_SYSCALL_EXIT(ret);
return ret;
}
/* TODO(olonho): ugly hack to get access to the real write(). Fortunately,
NaCl library ABI is pretty stable.*/
#define NACL_IRT_FDIO_v0_1 "nacl-irt-fdio-0.1"
extern struct nacl_irt_fdio __libnacl_irt_fdio;
struct nacl_irt_fdio {
int (*close)(int fd);
int (*dup)(int fd, int* newfd);
int (*dup2)(int fd, int newfd);
int (*read)(int fd, void* buf, size_t count, size_t* nread);
int (*write)(int fd, const void* buf, size_t count, size_t* nwrote);
int (*seek)(int fd, off_t offset, int whence, off_t* new_offset);
int (*fstat)(int fd, struct stat* );
};
int libnacl_write(int fd, const void* buf, size_t count, size_t* nwrote) {
return __libnacl_irt_fdio.write(fd, buf, count, nwrote);
}
static int WRAP(write)(int fd, const void* buf, size_t count, size_t* nwrote) {
#ifndef NDEBUG
// Have debug builds write stdout/stderr to the program's stdout/stderr too.
// This helps when debugging on Linux systems. We also pass it back to the
// JS layer as CrOS doesn't have a way of viewing the program's stdout/stderr.
if (fd == 1 || fd == 2) {
REAL(write)(fd, buf, count, nwrote);
}
#endif
return FileSystem::GetFileSystem()->write(fd, (const char*)buf, count,
nwrote);
}
ssize_t write(int fd, const void* buf, size_t count) {
if (fd != 1 && fd != 2) {
VLOG_SYSCALL_ENTER();
VLOG("fd=%i, buf=%p, count=%zu", fd, buf, count);
}
ssize_t rv;
ssize_t ret = HANDLE_ERRNO(WRAP(write)(fd, buf, count, (size_t*)&rv), rv);
if (fd != 1 && fd != 2)
VLOG_SYSCALL_EXIT(ret);
return ret;
}
static int WRAP(seek)(int fd, nacl_abi_off_t offset, int whence,
nacl_abi_off_t* new_offset) {
LOG("SYSCALL: seek: fd=%d offset=%d whence=%d\n", fd, (int)offset, whence);
return FileSystem::GetFileSystem()->seek(fd, offset, whence, new_offset);
}
off_t lseek(int fd, off_t offset, int whence) {
nacl_abi_off_t rv;
return HANDLE_ERRNO(WRAP(seek)(fd, offset, whence, &rv), rv);
}
static int WRAP(dup)(int fd, int* newfd) {
return FileSystem::GetFileSystem()->dup(fd, newfd);
}
int dup(int oldfd) {
LOG_SYSCALL_ENTER();
LOG("oldfd=%i", oldfd);
int rv;
int ret = HANDLE_ERRNO(WRAP(dup)(oldfd, &rv), rv);
LOG_SYSCALL_EXIT(ret);
return ret;
}
static int WRAP(dup2)(int fd, int newfd) {
return FileSystem::GetFileSystem()->dup2(fd, newfd);
}
int dup2(int oldfd, int newfd) {
LOG_SYSCALL_ENTER();
LOG("oldfd=%i, newfd=%i", oldfd, newfd);
int ret = HANDLE_ERRNO(WRAP(dup2)(oldfd, newfd), newfd);
LOG_SYSCALL_EXIT(ret);
return ret;
}
static int WRAP(stat)(const char* pathname, struct nacl_abi_stat* buf) {
return FileSystem::GetFileSystem()->stat(pathname, buf);
}
static void stat_n2u(struct nacl_abi_stat* nacl_buf, struct stat* buf) {
buf->st_dev = nacl_buf->nacl_abi_st_dev;
buf->st_ino = nacl_buf->nacl_abi_st_ino;
buf->st_mode = nacl_buf->nacl_abi_st_mode;
buf->st_nlink = nacl_buf->nacl_abi_st_nlink;
buf->st_uid = nacl_buf->nacl_abi_st_uid;
buf->st_gid = nacl_buf->nacl_abi_st_gid;
buf->st_rdev = nacl_buf->nacl_abi_st_rdev;
buf->st_size = nacl_buf->nacl_abi_st_size;
buf->st_blksize = nacl_buf->nacl_abi_st_blksize;
buf->st_blocks = nacl_buf->nacl_abi_st_blocks;
buf->st_atime = nacl_buf->nacl_abi_st_atime;
buf->st_mtime = nacl_buf->nacl_abi_st_mtime;
buf->st_ctime = nacl_buf->nacl_abi_st_ctime;
}
int stat(const char* path, struct stat* buf) {
LOG_SYSCALL_ENTER();
LOG("path=\"%s\", buf=%p", path, buf);
struct nacl_abi_stat nacl_buf;
int rv = WRAP(stat)(path, &nacl_buf);
if (rv == 0)
stat_n2u(&nacl_buf, buf);
int ret = HANDLE_ERRNO(rv, 0);
LOG_SYSCALL_EXIT(ret);
return ret;
}
static int WRAP(fstat)(int fd, struct nacl_abi_stat* buf) {
return FileSystem::GetFileSystem()->fstat(fd, buf);
}
int fstat(int fd, struct stat* buf) {
LOG_SYSCALL_ENTER();
LOG("fd=%i, buf=%p", fd, buf);
struct nacl_abi_stat nacl_buf;
int rv = WRAP(fstat)(fd, &nacl_buf);
if (rv == 0)
stat_n2u(&nacl_buf, buf);
int ret = HANDLE_ERRNO(rv, 0);
LOG_SYSCALL_EXIT(ret);
return ret;
}
int isatty(int fd) {
LOG_SYSCALL_ENTER();
LOG("fd=%i", fd);
int ret = FileSystem::GetFileSystem()->isatty(fd);
LOG_SYSCALL_EXIT(ret);
return ret;
}
int fcntl(int fd, int cmd, ...) {
LOG_SYSCALL_ENTER();
LOG("fd=%i, cmd=%#x", fd, cmd);
va_list ap;
va_start(ap, cmd);
int ret = FileSystem::GetFileSystem()->fcntl(fd, cmd, ap);
va_end(ap);
LOG_SYSCALL_EXIT(ret);
return ret;
}
int ioctl(int fd, unsigned long request, ...) {
LOG_SYSCALL_ENTER();
LOG("fd=%i, request=%#lx", fd, request);
va_list ap;
va_start(ap, request);
int ret = FileSystem::GetFileSystem()->ioctl(fd, request, ap);
va_end(ap);
LOG_SYSCALL_EXIT(ret);
return ret;
}
int select(int nfds, fd_set* readfds, fd_set* writefds,
fd_set* exceptfds, struct timeval* timeout) {
VLOG_SYSCALL_ENTER();
VLOG("nfds=%d, readfds=%p, writefds=%p, exceptfds=%p,"
" timeout=%p({tv_sec=%" PRIu64 ", tv_usec=%" PRIu64 "})",
nfds, readfds, writefds, exceptfds, timeout,
timeout ? (uint64_t)timeout->tv_sec : 0,
timeout ? (uint64_t)timeout->tv_usec : 0);
int ret = FileSystem::GetFileSystem()->select(
nfds, readfds, writefds, exceptfds, timeout);
VLOG_SYSCALL_EXIT(ret);
return ret;
}
//------------------------------------------------------------------------------
// Wrap exit and _exit so JavaScript gets our exit code. We don't wrap
// abort so that we have something to chain to, but abort has no exit
// code to report anyway.
void exit(int status) {
LOG_SYSCALL_ENTER();
LOG("status=%i", status);
g_exit_called = true;
FileSystem::GetFileSystem()->exit(status);
LOG_SYSCALL_EXIT(status);
abort(); // Can we chain to the real exit?
}
void _exit(int status) {
LOG_SYSCALL_ENTER();
LOG("status=%i", status);
if (g_exit_called) {
// Infinity exit loop detected. It happens in case of NewLib when abort
// calls exit inside. The only thing we can do is to stop this thread.
pthread_exit(NULL);
}
g_exit_called = true;
FileSystem::GetFileSystem()->exit(status);
LOG_SYSCALL_EXIT(status);
abort(); // Can we chain to the real _exit?
}
int seteuid(uid_t euid) {
LOG_SYSCALL_STUB(0, "euid=%i", euid);
return 0;
}
int setresgid(gid_t rgid, gid_t egid, gid_t sgid) {
LOG_SYSCALL_STUB(0, "rgid=%i, egid=%i, sgid=%i", rgid, egid, sgid);
return 0;
}
int setresuid(uid_t ruid, uid_t euid, uid_t suid) {
LOG_SYSCALL_STUB(0, "ruid=%i, euid=%i, suid=%i", ruid, euid, suid);
return 0;
}
// We disable the threadsafe lint func here because it applies to the standard
// C library versions, not our stub one here that is actually safe.
struct passwd* getpwuid(uid_t uid) { // NOLINT(runtime/threadsafe_fn)
static struct passwd pwd;
LOG("SYSCALL: [stub] %s(uid=%i) = %p\n", __func__, uid, &pwd);
pwd.pw_name = (char*)"";
pwd.pw_passwd = (char*)"";
pwd.pw_uid = 0;
pwd.pw_gid = 0;
pwd.pw_gecos = (char*)"";
pwd.pw_dir = (char*)"";
pwd.pw_shell = (char*)"";
return &pwd;
}
int gethostname(char* name, size_t len) {
const char* kHostname = "localhost";
strncpy(name, kHostname, len);
LOG("SYSCALL: [stub] %s(name=%p[%s], len=%zu) = 0\n",
__func__, name, name, len);
return 0;
}
int getaddrinfo(const char* hostname, const char* servname,
const struct addrinfo* hints, struct addrinfo** res) {
LOG("SYSCALL: getaddrinfo: %s %s\n",
hostname ? hostname : "", servname ? servname : "");
return FileSystem::GetFileSystem()->getaddrinfo(
hostname, servname, hints, res);
}
void freeaddrinfo(struct addrinfo* ai) {
LOG("SYSCALL: freeaddrinfo\n");
return FileSystem::GetFileSystem()->freeaddrinfo(ai);
}
int getnameinfo(const struct sockaddr* sa, socklen_t salen,
char* host, socklen_t hostlen,
char* serv, socklen_t servlen, unsigned int flags) {
LOG("SYSCALL: getnameinfo\n");
return FileSystem::GetFileSystem()->getnameinfo(
sa, salen, host, hostlen, serv, servlen, flags);
}
int socket(int socket_family, int socket_type, int protocol) {
LOG("SYSCALL: socket: %d %d %d\n", socket_family, socket_type, protocol);
return FileSystem::GetFileSystem()->socket(
socket_family, socket_type, protocol);
}
int connect(int sockfd, const struct sockaddr* serv_addr, socklen_t addrlen) {
LOG("SYSCALL: connect: %d\n", sockfd);
return FileSystem::GetFileSystem()->connect(sockfd, serv_addr, addrlen);
}
pid_t waitpid(pid_t pid, int* status, int options) {
LOG_SYSCALL_STUB(-1, "pid=%i, status=%p, options=%i",
pid, status, options);
return -1;
}
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen) {
LOG("SYSCALL: accept(sockfd=%i)\n", sockfd);
return FileSystem::GetFileSystem()->accept(sockfd, addr, addrlen);
}
int sigaction(int signum, const struct sigaction* act,
struct sigaction* oldact) {
LOG_SYSCALL_ENTER();
LOG("signum=%i, act=%p, oldact=%p", signum, act, oldact);
int ret = FileSystem::GetFileSystem()->sigaction(signum, act, oldact);
LOG_SYSCALL_EXIT(ret);
return ret;
}
int kill(pid_t pid, int sig) {
LOG_SYSCALL_STUB(-1, "pid=%i, sig=%i", pid, sig);
return -1;
}
pid_t fork(void) {
LOG_SYSCALL_STUB(-1, "");
return -1;
}
pid_t getpid(void) {
LOG_SYSCALL_STUB(100, "");
return 100;
}
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen) {
LOG("SYSCALL: bind(sockfd=%i)\n", sockfd);
return FileSystem::GetFileSystem()->bind(sockfd, my_addr, addrlen);
}
int getpeername(int socket, struct sockaddr* address,
socklen_t* address_len) {
LOG_SYSCALL_STUB(-1, "socket=%i, address=%p, addrlen=%p",
socket, address, address_len);
return -1;
}
int getsockname(int s, struct sockaddr* name, socklen_t* namelen) {
LOG("SYSCALL: getsockname(socket=%i)\n", s);
return FileSystem::GetFileSystem()->getsockname(s, name, namelen);
}
int listen(int sockfd, int backlog) {
LOG("SYSCALL: listen(sockfd=%i, backlog=%i)\n", sockfd, backlog);
return FileSystem::GetFileSystem()->listen(sockfd, backlog);
}
int setsockopt(int socket, int level, int option_name,
const void* option_value, socklen_t option_len) {
LOG_SYSCALL_STUB(0, "socket=%i, level=%i, optname=%i, optval=%p, optlen=%i",
socket, level, option_name, option_value, option_len);
return 0;
}
int getsockopt(int socket, int level, int option_name,
void* option_value, socklen_t* option_len) {
LOG_SYSCALL_STUB(0, "socket=%i, level=%i, optname=%i, optval=%p, optlen=%p",
socket, level, option_name, option_value, option_len);
memset(option_value, 0, *option_len);
return 0;
}
int shutdown(int s, int how) {
LOG("SYSCALL: shutdown(socket=%i, how=%i)\n", s, how);
return FileSystem::GetFileSystem()->shutdown(s, how);
}
int tcgetattr(int fd, struct termios* termios_p) {
LOG_SYSCALL_ENTER();
LOG("fd=%i, termios_p=%p", fd, termios_p);
int ret = FileSystem::GetFileSystem()->tcgetattr(fd, termios_p);
LOG_SYSCALL_EXIT(ret);
return ret;
}
int tcsetattr(int fd, int optional_actions, const struct termios* termios_p) {
LOG_SYSCALL_ENTER();
LOG("fd=%i, actions=%i[", fd, optional_actions);
switch (optional_actions) {
case TCSANOW:
LOG("TCSANOW");
break;
case TCSADRAIN:
LOG("TCSADRAIN");
break;
case TCSAFLUSH:
LOG("TCSAFLUSH");
break;
default:
LOG("???");
break;
}
LOG("], termios_p=%p", termios_p);
if (termios_p) {
LOG("{c_iflag=%#x, c_oflag=%#x, c_cflag=%#x, ",
termios_p->c_iflag, termios_p->c_oflag, termios_p->c_cflag);
LOG("c_lflag=%#x[", termios_p->c_lflag);
#define LOG_FLAG(flag) LOG(" %s" #flag, termios_p->c_lflag & flag ? "" : "-")
LOG_FLAG(ICANON);
LOG_FLAG(ISIG);
LOG_FLAG(ECHO);
LOG_FLAG(ECHOE);
LOG_FLAG(ECHOK);
LOG_FLAG(ECHONL);
#undef LOG_FLAG
LOG("]}");
}
int ret = FileSystem::GetFileSystem()->tcsetattr(
fd, optional_actions, termios_p);
LOG_SYSCALL_EXIT(ret);
return ret;
}
int mkdir(const char* pathname, mode_t mode) {
LOG_SYSCALL_ENTER();
LOG("path=\"%s\", mode=%#o", pathname, mode);
int ret = FileSystem::GetFileSystem()->mkdir(pathname, mode);
LOG_SYSCALL_EXIT(ret);
return ret;
}
int sched_setscheduler(pid_t pid, int policy,
const struct sched_param* param) {
LOG_SYSCALL_STUB(0, "pid=%i, policy=%i, param=%p", pid, policy, param);
return 0;
}
ssize_t send(int fd, const void* buf, size_t count, int flags) {
VLOG_SYSCALL_ENTER();
VLOG("fd=%i, count=%i", fd, count);
size_t sent = 0;
int rv = FileSystem::GetFileSystem()->write(fd, (const char*)buf,
count, &sent);
ssize_t ret = HANDLE_ERRNO(rv, sent);
VLOG_SYSCALL_EXIT(ret);
return ret;
}
ssize_t recv(int fd, void* buf, size_t count, int flags) {
VLOG_SYSCALL_ENTER();
VLOG("fd=%i, buf=%p, len=%zu, flags=%#x", fd, buf, count, flags);
size_t recvd = 0;
int rv = FileSystem::GetFileSystem()->read(fd, (char*)buf, count, &recvd);
ssize_t ret = HANDLE_ERRNO(rv, recvd);
VLOG_SYSCALL_EXIT(ret);
return ret;
}
ssize_t sendto(int sockfd, const void* buf, size_t len, int flags,
const struct sockaddr* dest_addr, socklen_t addrlen) {
LOG_SYSCALL_ENTER();
LOG("sockfd=%i, buf=%p, len=%zu, flags=%i, addr=%p, addrlen=%u",
sockfd, buf, len, flags, dest_addr, addrlen);
ssize_t ret = FileSystem::GetFileSystem()->sendto(
sockfd, (char*)buf, len, flags, dest_addr, addrlen);
LOG_SYSCALL_EXIT(ret);
return ret;
}
ssize_t recvfrom(int socket, void* buffer, size_t len, int flags,
struct sockaddr* addr, socklen_t* addrlen) {
LOG_SYSCALL_ENTER();
LOG("sockfd=%i, buf=%p, len=%zu, flags=%i, addr=%p, addrlen=%p",
socket, buffer, len, flags, addr, addrlen);
ssize_t ret = FileSystem::GetFileSystem()->recvfrom(
socket, (char*)buffer, len, flags, addr, addrlen);
LOG_SYSCALL_EXIT(ret);
return ret;
}
int socketpair(int domain, int type, int protocol, int socket_vector[2]) {
LOG_SYSCALL_STUB(-1, "domain=%i, type=%i, protocol=%i, sv=%p",
domain, type, protocol, socket_vector);
errno = EACCES;
return -1;
}
int clock_gettime(clockid_t clk_id, struct timespec* tp) {
LOG_SYSCALL_STUB(-1, "clk_id=%i, timespec=%p", clk_id, tp);
errno = EINVAL;
return -1;
}
speed_t cfgetospeed(const struct termios* t) {
return t->c_ospeed;
}
speed_t cfgetispeed(const struct termios* t) {
return t->c_ispeed;
}
int cfsetospeed(struct termios* t, speed_t speed) {
t->c_ospeed = speed;
return 0;
}
int cfsetispeed(struct termios* t, speed_t speed) {
t->c_ispeed = speed;
return 0;
}
} // extern "C"