#include "debug.h" #include #include #include "kernel/calls.h" #include "kernel/errno.h" #include "kernel/task.h" #include "kernel/fs.h" #include "fs/fd.h" #include "fs/path.h" static struct fd *at_fd(fd_t f) { if (f == AT_FDCWD_) return AT_PWD; return f_get(f); } static void apply_umask(mode_t_ *mode) { struct fs_info *fs = current->fs; lock(&fs->lock); *mode &= ~fs->umask; unlock(&fs->lock); } // TODO ENAMETOOLONG dword_t sys_access(addr_t path_addr, dword_t mode) { return sys_faccessat(AT_FDCWD_, path_addr, mode, 0); } // TODO: Something about flags dword_t sys_faccessat(fd_t at_f, addr_t path_addr, mode_t_ mode, dword_t flags) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; STRACE("faccessat(%d, \"%s\", 0x%x, %d)", at_f, path, mode, flags); return generic_accessat(at, path, mode); } fd_t sys_openat(fd_t at_f, addr_t path_addr, dword_t flags, mode_t_ mode) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; STRACE("openat(%d, \"%s\", 0x%x, 0x%x)", at_f, path, flags, mode); if (flags & O_CREAT_) apply_umask(&mode); struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; struct fd *fd = generic_openat(at, path, flags, mode); if (IS_ERR(fd)) return PTR_ERR(fd); return f_install(fd); } fd_t sys_open(addr_t path_addr, dword_t flags, mode_t_ mode) { return sys_openat(AT_FDCWD_, path_addr, flags, mode); } dword_t sys_readlinkat(fd_t at_f, addr_t path_addr, addr_t buf_addr, dword_t bufsize) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; char buf[bufsize]; int err = generic_readlinkat(at, path, buf, bufsize); if (err >= 0) if (user_write_string(buf_addr, buf)) return _EFAULT; return err; } dword_t sys_readlink(addr_t path_addr, addr_t buf_addr, dword_t bufsize) { return sys_readlinkat(AT_FDCWD_, path_addr, buf_addr, bufsize); } dword_t sys_linkat(fd_t src_at_f, addr_t src_addr, fd_t dst_at_f, addr_t dst_addr) { char src[MAX_PATH]; if (user_read_string(src_addr, src, sizeof(src))) return _EFAULT; char dst[MAX_PATH]; if (user_read_string(dst_addr, dst, sizeof(dst))) return _EFAULT; STRACE("linkat(%d, \"%s\", %d, \"%s\")", src_at_f, src, dst_at_f, dst); struct fd *src_at = at_fd(src_at_f); if (src_at == NULL) return _EBADF; struct fd *dst_at = at_fd(dst_at_f); if (dst_at == NULL) return _EBADF; return generic_linkat(src_at, src, dst_at, dst); } dword_t sys_link(addr_t src_addr, addr_t dst_addr) { return sys_linkat(AT_FDCWD_, src_addr, AT_FDCWD_, dst_addr); } dword_t sys_unlinkat(fd_t at_f, addr_t path_addr) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; STRACE("unlinkat(%d, \"%s\")", at_f, path); struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; return generic_unlinkat(at, path); } dword_t sys_unlink(addr_t path_addr) { return sys_unlinkat(AT_FDCWD_, path_addr); } dword_t sys_renameat(fd_t src_at_f, addr_t src_addr, fd_t dst_at_f, addr_t dst_addr) { char src[MAX_PATH]; if (user_read_string(src_addr, src, sizeof(src))) return _EFAULT; char dst[MAX_PATH]; if (user_read_string(dst_addr, dst, sizeof(dst))) return _EFAULT; STRACE("renameat(%d, \"%s\", %d, \"%s\")", src_at_f, src, dst_at_f, dst); struct fd *src_at = at_fd(src_at_f); if (src_at == NULL) return _EBADF; struct fd *dst_at = at_fd(dst_at_f); if (dst_at == NULL) return _EBADF; return generic_renameat(src_at, src, dst_at, dst); } dword_t sys_rename(addr_t src_addr, addr_t dst_addr) { return sys_renameat(AT_FDCWD_, src_addr, AT_FDCWD_, dst_addr); } dword_t sys_symlinkat(addr_t target_addr, fd_t at_f, addr_t link_addr) { char target[MAX_PATH]; if (user_read_string(target_addr, target, sizeof(target))) return _EFAULT; char link[MAX_PATH]; if (user_read_string(link_addr, link, sizeof(link))) return _EFAULT; STRACE("symlinkat(\"%s\", %d, \"%s\")", target, at_f, link); struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; return generic_symlinkat(target, at, link); } dword_t sys_symlink(addr_t target_addr, addr_t link_addr) { return sys_symlinkat(target_addr, AT_FDCWD_, link_addr); } dword_t sys_read(fd_t fd_no, addr_t buf_addr, dword_t size) { STRACE("read(%d, 0x%x, %d)", fd_no, buf_addr, size); char buf[size+1]; struct fd *fd = f_get(fd_no); if (fd == NULL) return _EBADF; int res = fd->ops->read(fd, buf, size); if (res >= 0) if (user_write(buf_addr, buf, res)) return _EFAULT; return res; } dword_t sys_readv(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count) { struct io_vec iovecs[iovec_count]; if (user_get(iovec_addr, iovecs)) return _EFAULT; int res; dword_t count = 0; for (unsigned i = 0; i < iovec_count; i++) { res = sys_read(fd_no, iovecs[i].base, iovecs[i].len); if (res < 0) return res; count += res; if (res < iovecs[i].len) break; } return count; } dword_t sys_write(fd_t fd_no, addr_t buf_addr, dword_t size) { char buf[size+1]; if (user_read(buf_addr, buf, size)) return _EFAULT; buf[size] = '\0'; STRACE("write(%d, \"%s\", %d)", fd_no, buf, size); struct fd *fd = f_get(fd_no); if (fd == NULL) return _EBADF; return fd->ops->write(fd, buf, size); } dword_t sys_writev(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count) { struct io_vec iovecs[iovec_count]; if (user_get(iovec_addr, iovecs)) return _EFAULT; int res; dword_t count = 0; for (unsigned i = 0; i < iovec_count; i++) { res = sys_write(fd_no, iovecs[i].base, iovecs[i].len); if (res < 0) return res; count += res; if (res < iovecs[i].len) break; } return count; } dword_t sys__llseek(fd_t f, dword_t off_high, dword_t off_low, addr_t res_addr, dword_t whence) { struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; if (!fd->ops->lseek) return _ESPIPE; lock(&fd->lock); off_t_ off = ((off_t_) off_high << 32) | off_low; off_t_ res = fd->ops->lseek(fd, off, whence); unlock(&fd->lock); if (res < 0) return res; if (user_put(res_addr, res)) return _EFAULT; return 0; } dword_t sys_lseek(fd_t f, dword_t off, dword_t whence) { struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; lock(&fd->lock); off_t res = fd->ops->lseek(fd, off, whence); unlock(&fd->lock); if ((dword_t) res != res) return _EOVERFLOW; return res; } dword_t sys_pread(fd_t f, addr_t buf_addr, dword_t size, off_t_ off) { struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; char buf[size+1]; lock(&fd->lock); sdword_t res = fd->ops->lseek(fd, off, LSEEK_SET); if (res < 0) goto out; res = fd->ops->read(fd, buf, size); if (res >= 0) if (user_write(buf_addr, buf, res)) return _EFAULT; out: unlock(&fd->lock); return res; } dword_t sys_ioctl(fd_t f, dword_t cmd, dword_t arg) { STRACE("ioctl(%d, 0x%x, 0x%x)", f, cmd, arg); struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; if (!fd->ops->ioctl_size) return _EINVAL; ssize_t size = fd->ops->ioctl_size(fd, cmd); if (size < 0) { println("unknown ioctl %x", cmd); return _EINVAL; } if (size == 0) return fd->ops->ioctl(fd, cmd, (void *) (long) arg); // praying that this won't break char buf[size]; if (user_read(arg, buf, size)) return _EFAULT; int res = fd->ops->ioctl(fd, cmd, buf); if (res < 0) return res; if (user_write(arg, buf, size)) return _EFAULT; return res; } dword_t sys_getcwd(addr_t buf_addr, dword_t size) { lock(¤t->fs->lock); struct fd *wd = current->fs->pwd; char pwd[MAX_PATH]; int err = wd->mount->fs->getpath(wd, pwd); unlock(¤t->fs->lock); if (err < 0) return err; if (strlen(pwd) + 1 > size) return _ERANGE; char buf[size]; strcpy(buf, pwd); if (user_write(buf_addr, buf, sizeof(buf))) return _EFAULT; return size; } static struct fd *open_dir(const char *path) { struct statbuf stat; int err = generic_statat(AT_PWD, path, &stat, true); if (err < 0) return ERR_PTR(err); if (!(stat.mode & S_IFDIR)) return ERR_PTR(_ENOTDIR); return generic_open(path, O_RDONLY_, 0); } void fs_chdir(struct fs_info *fs, struct fd *fd) { lock(&fs->lock); fd_close(fs->pwd); fs->pwd = fd; unlock(&fs->lock); } static void fs_chroot(struct fs_info *fs, struct fd *fd) { lock(&fs->lock); fd->refcount++; fd_close(fs->root); fs->root = fd; unlock(&fs->lock); } dword_t sys_chdir(addr_t path_addr) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; STRACE("chdir(\"%s\")", path); struct fd *dir = open_dir(path); if (IS_ERR(dir)) return PTR_ERR(dir); fs_chdir(current->fs, dir); return 0; } dword_t sys_fchdir(fd_t f) { STRACE("fchdir(%d)", f); struct fd *dir = f_get(f); if (dir == NULL) return _EBADF; dir->refcount++; fs_chdir(current->fs, dir); return 0; } dword_t sys_chroot(addr_t path_addr) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; STRACE("chroot(\"%s\")", path); struct fd *dir = open_dir(path); if (IS_ERR(dir)) return PTR_ERR(dir); fs_chroot(current->fs, dir); return 0; } dword_t sys_umask(dword_t mask) { STRACE("umask(0%o)", mask); struct fs_info *fs = current->fs; lock(&fs->lock); mode_t_ old_umask = fs->umask; fs->umask = ((mode_t_) mask) & 0777; unlock(&fs->lock); return old_umask; } static dword_t statfs_mount(struct mount *mount, addr_t buf_addr) { struct statfsbuf stat; int err = mount->fs->statfs(mount, &stat); if (err >= 0) if (user_put(buf_addr, stat)) return _EFAULT; return err; } dword_t sys_statfs64(addr_t path_addr, addr_t buf_addr) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; return statfs_mount(find_mount(path), buf_addr); } dword_t sys_fstatfs64(fd_t f, addr_t buf_addr) { return statfs_mount(f_get(f)->mount, buf_addr); } dword_t sys_flock(fd_t f, dword_t operation) { struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; return fd->mount->fs->flock(fd, operation); } static struct timespec convert_timespec(struct timespec_ t) { struct timespec ts; ts.tv_sec = t.sec; ts.tv_nsec = t.nsec; return ts; } dword_t sys_utimensat(fd_t at_f, addr_t path_addr, addr_t times_addr, dword_t flags) { struct timespec_ times[2]; if (times_addr == 0) { // if times is NULL, set both times to the current time struct timespec now; int err = clock_gettime(CLOCK_REALTIME, &now); if (err < 0) return errno_map(); times[0] = times[1] = (struct timespec_) {.sec = now.tv_sec, .nsec = now.tv_nsec}; } else { if (user_get(times_addr, times)) return _EFAULT; } char path[MAX_PATH]; if (path_addr != 0) if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; STRACE("utimensat(%d, %s, {{%d, %d}, {%d, %d}}, %d)", at_f, path, times[0].sec, times[0].nsec, times[1].sec, times[1].nsec, flags); struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; struct timespec atime = convert_timespec(times[0]); struct timespec mtime = convert_timespec(times[1]); bool follow_links = flags & AT_SYMLINK_NOFOLLOW_ ? false : true; return generic_utime(at, path_addr != 0 ? path : ".", atime, mtime, follow_links); // TODO implement } dword_t sys_fchmod(fd_t f, dword_t mode) { struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; mode &= ~S_IFMT; return fd->mount->fs->fsetattr(fd, make_attr(mode, mode)); } dword_t sys_fchmodat(fd_t at_f, addr_t path_addr, dword_t mode, int flags) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; bool follow_links = flags & AT_SYMLINK_NOFOLLOW_ ? false : true; return generic_setattrat(at, path, make_attr(mode, mode), follow_links); } dword_t sys_chmod(addr_t path_addr, dword_t mode) { return sys_fchmodat(AT_FDCWD_, path_addr, mode, 0); } dword_t sys_fchown32(fd_t f, dword_t owner, dword_t group) { struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; int err; if (owner != -1) { err = fd->mount->fs->fsetattr(fd, make_attr(uid, owner)); if (err < 0) return err; } if (group != -1) { err = fd->mount->fs->fsetattr(fd, make_attr(gid, group)); if (err < 0) return err; } return 0; } dword_t sys_fchownat(fd_t at_f, addr_t path_addr, dword_t owner, dword_t group, int flags) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; STRACE("fchownat(%d, %s, %d, %d, %d)", at_f, path, owner, group, flags); struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; int err; bool follow_links = flags & AT_SYMLINK_NOFOLLOW_ ? false : true; if (owner != -1) { err = generic_setattrat(at, path, make_attr(uid, owner), follow_links); if (err < 0) return err; } if (group != -1) { err = generic_setattrat(at, path, make_attr(gid, group), follow_links); if (err < 0) return err; } return 0; } dword_t sys_chown32(addr_t path_addr, uid_t_ owner, uid_t_ group) { return sys_fchownat(AT_FDCWD_, path_addr, owner, group, 0); } dword_t sys_truncate64(addr_t path_addr, dword_t size_low, dword_t size_high) { off_t_ size = ((off_t_) size_high << 32) | size_low; char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; return generic_setattrat(NULL, path, make_attr(size, size), true); } dword_t sys_ftruncate64(fd_t f, dword_t size_low, dword_t size_high) { off_t_ size = ((off_t_) size_high << 32) | size_low; struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; return fd->mount->fs->fsetattr(fd, make_attr(size, size)); } dword_t sys_fallocate(fd_t f, dword_t mode, dword_t offset_low, dword_t offset_high, dword_t len_low, dword_t len_high) { off_t_ offset = ((off_t_) offset_high << 32) | offset_low; off_t_ len = ((off_t_) len_high << 32) | len_low; struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; struct statbuf statbuf; int err = fd->mount->fs->fstat(fd, &statbuf); if (err < 0) return err; if (offset + len > statbuf.size) return fd->mount->fs->fsetattr(fd, make_attr(size, offset + len)); return 0; } dword_t sys_mkdirat(fd_t at_f, addr_t path_addr, mode_t_ mode) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; STRACE("mkdirat(%d, %s, 0%o)", at_f, path, mode); struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; apply_umask(&mode); return generic_mkdirat(at, path, mode); } dword_t sys_mkdir(addr_t path_addr, mode_t_ mode) { return sys_mkdirat(AT_FDCWD_, path_addr, mode); } dword_t sys_rmdir(addr_t path_addr) { char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; return generic_rmdirat(AT_PWD, path); } dword_t sys_fsync(fd_t f) { struct fd *fd = f_get(f); if (fd == NULL) return _EBADF; int err = 0; if (fd->ops->fsync) err = fd->ops->fsync(fd); return err; } // a few stubs dword_t sys_sendfile(fd_t out_fd, fd_t in_fd, addr_t offset_addr, dword_t count) { return _EINVAL; } dword_t sys_sendfile64(fd_t out_fd, fd_t in_fd, addr_t offset_addr, dword_t count) { return _EINVAL; } dword_t sys_mount(addr_t source_addr, addr_t target_addr, addr_t type_addr, dword_t flags, addr_t data_addr) { return _EINVAL; // I'm sorry, we do not support this action at this time. } dword_t sys_umount2(addr_t target_addr, dword_t flags) { return _EINVAL; // I'm sorry, we do not support this action at this time. } dword_t sys_fsetxattr(addr_t path_addr, addr_t name_addr, addr_t value_addr, dword_t size, dword_t flags) { return _ENOTSUP; } struct mount *mounts;