#include "Log.h" #include "Pledge.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" #include #include // These flags are needed after open(), the rest only affect open(). constexpr int FLAGS_TO_KEEP = O_RDWR | O_APPEND | O_NONBLOCK; Result sys_openat(Registers*, SyscallArgs args) { int dirfd = (int)args[0]; auto path = TRY(MemoryManager::strdup_from_user(args[1])); int flags = (int)args[2]; mode_t mode = (mode_t)args[3]; Thread* current = Scheduler::current(); SharedPtr inode; // Caller did not pass either O_RDONLY, O_WRONLY or O_RDWR if ((flags & O_RDWR) == 0) { return err(EINVAL); } if (flags & O_RDONLY) TRY(check_pledge(current, Promise::p_rpath)); else if (flags & O_WRONLY) TRY(check_pledge(current, Promise::p_wpath)); if (flags & O_CREAT) TRY(check_pledge(current, Promise::p_cpath)); if (flags & O_TMPFILE) { if (!(flags & O_WRONLY)) return err(EINVAL); if (flags & O_CREAT) return err(EINVAL); } int error; SharedPtr parent_inode; bool ok = current->resolve_atfile(dirfd, path, false, !(flags & O_NOFOLLOW), &parent_inode) .try_set_value_or_error(inode, error); if (!ok) { if (error == ENOENT && (flags & O_CREAT) && !path.is_empty()) { inode = TRY(VFS::create_file(path.chars(), mode & ~current->umask, current->auth, parent_inode)); // FIXME: Pass these in create_file(). auto metadata = inode->metadata(); metadata.uid = current->auth.euid; metadata.gid = current->auth.egid; TRY(inode->set_metadata(metadata)); } else return err(error); } else if (flags & O_EXCL) return err(EEXIST); else { if ((flags & O_RDONLY) && !VFS::can_read(inode, current->auth)) return err(EACCES); if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth)) return err(EACCES); } // This should only be possible if O_NOFOLLOW was in flags. if (inode->type() == VFS::InodeType::Symlink) return err(ELOOP); if (inode->type() == VFS::InodeType::Socket) return err(ENXIO); if (flags & O_TMPFILE) { if (inode->type() != VFS::InodeType::Directory) return err(EINVAL); inode = TRY(inode->fs()->create_file_inode(mode & current->umask)); auto metadata = inode->metadata(); metadata.uid = current->auth.euid; metadata.gid = current->auth.egid; TRY(inode->set_metadata(metadata)); } if ((flags & O_WRONLY) && inode->fs() && inode->fs()->is_readonly()) return err(EROFS); if (inode->type() != VFS::InodeType::Directory && (flags & O_DIRECTORY)) return err(ENOTDIR); if (inode->type() == VFS::InodeType::Directory) { if ((flags & O_WRONLY) || (flags & O_CREAT)) return err(EISDIR); } if ((flags & O_WRONLY) && (flags & O_TRUNC)) inode->truncate(0); int fd = TRY(current->allocate_fd(0)); #ifdef OPEN_DEBUG kdbgln("openat: opening file %s from dirfd %d, flags %d, mode %#o = fd %d", path.chars(), dirfd, flags, mode, fd); #endif current->fd_table[fd] = FileDescriptor { TRY(make_shared(inode, flags & FLAGS_TO_KEEP)), 0, flags & O_CLOEXEC }; return (u64)fd; } Result sys_close(Registers*, SyscallArgs args) { int fd = (int)args[0]; if (fd < 0 || fd >= FD_MAX) return err(EBADF); Thread* current = Scheduler::current(); TRY(check_pledge(current, Promise::p_stdio)); Option& descriptor = current->fd_table[fd]; if (!descriptor.has_value()) return err(EBADF); descriptor = {}; return 0; }