#include "Log.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" #include #include Result sys_unlinkat(Registers*, SyscallArgs args) { int dirfd = (int)args[0]; auto path = TRY(MemoryManager::strdup_from_user(args[1])); int flags = (int)args[2]; Thread* current = Scheduler::current(); PathParser parser = TRY(PathParser::create(path.chars())); auto dirname = TRY(parser.dirname()); auto basename = TRY(parser.basename()); if (basename.view() == ".") return err(EINVAL); kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd); auto inode = TRY(current->resolve_atfile(dirfd, dirname, false, false)); if (!VFS::can_write(inode, current->auth)) return err(EACCES); auto child = TRY(inode->find(basename.chars())); if (flags == AT_REMOVEDIR && child->type() != VFS::InodeType::Directory) return err(ENOTDIR); if (current->auth.euid != 0 && VFS::is_sticky(inode) && current->auth.euid != inode->uid() && current->auth.euid != child->uid()) return err(EACCES); TRY(inode->remove_entry(basename.chars())); return 0; } Result sys_symlinkat(Registers*, SyscallArgs args) { auto target = TRY(MemoryManager::strdup_from_user(args[0])); int dirfd = (int)args[1]; auto linkpath = TRY(MemoryManager::strdup_from_user(args[2])); if (target.is_empty()) return err(ENOENT); auto* current = Scheduler::current(); auto parser = TRY(PathParser::create(linkpath.chars())); auto parent = TRY(parser.dirname()); auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true)); if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES); auto child_name = TRY(parser.basename()); TRY(VFS::validate_filename(child_name.view())); auto inode = TRY(parent_inode->fs()->create_symlink_inode(target.view())); TRY(inode->chown(current->auth.euid, current->auth.egid)); TRY(parent_inode->add_entry(inode, child_name.chars())); return 0; } Result sys_readlinkat(Registers*, SyscallArgs args) { int dirfd = (int)args[0]; auto path = TRY(MemoryManager::strdup_from_user(args[1])); char* buf = (char*)args[2]; usize bufsiz = (usize)args[3]; auto* current = Scheduler::current(); auto symlink = TRY(current->resolve_atfile(dirfd, path, true, false)); if (symlink->type() != VFS::InodeType::Symlink) return err(EINVAL); auto linkpath = TRY(symlink->readlink()); check(!linkpath.is_empty()); usize nread = linkpath.length(); if (nread > bufsiz) nread = bufsiz; kdbgln("readlink: reading %zu bytes from symlink (%s)", nread, linkpath.chars()); if (!MemoryManager::copy_to_user(buf, linkpath.chars(), nread)) return err(EFAULT); return nread; } Result sys_linkat(Registers*, SyscallArgs args) { int olddirfd = (int)args[0]; auto oldpath = TRY(MemoryManager::strdup_from_user(args[1])); int newdirfd = (int)args[2]; auto newpath = TRY(MemoryManager::strdup_from_user(args[3])); int flags = (int)args[4]; auto* current = Scheduler::current(); auto parser = TRY(PathParser::create(newpath.chars())); auto parent = TRY(parser.dirname()); // FIXME: Use AT_SYMLINK_FOLLOW. auto target = TRY(current->resolve_atfile(olddirfd, oldpath, flags & AT_EMPTY_PATH, false)); if (target->type() == VFS::InodeType::Directory) return err(EPERM); auto parent_inode = TRY(current->resolve_atfile(newdirfd, parent, false, true)); if (target->fs() != parent_inode->fs()) return err(EXDEV); if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES); auto child_name = TRY(parser.basename()); TRY(VFS::validate_filename(child_name.view())); TRY(parent_inode->add_entry(target, child_name.chars())); return 0; }