2023-04-28 19:15:41 +00:00
|
|
|
#include "Log.h"
|
2023-04-12 16:11:36 +00:00
|
|
|
#include "memory/MemoryManager.h"
|
|
|
|
#include "sys/Syscall.h"
|
|
|
|
#include "thread/Scheduler.h"
|
2023-05-27 09:32:40 +00:00
|
|
|
#include <bits/atfile.h>
|
2023-04-12 16:11:36 +00:00
|
|
|
#include <luna/PathParser.h>
|
|
|
|
|
2023-04-18 17:36:29 +00:00
|
|
|
Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
2023-04-12 16:11:36 +00:00
|
|
|
{
|
2023-04-18 17:36:29 +00:00
|
|
|
int dirfd = (int)args[0];
|
|
|
|
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
|
|
|
|
int flags = (int)args[2];
|
2023-04-12 16:11:36 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2023-04-28 19:15:41 +00:00
|
|
|
kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd);
|
|
|
|
|
2023-05-20 19:46:31 +00:00
|
|
|
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false, false));
|
2023-04-12 16:11:36 +00:00
|
|
|
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
|
|
|
|
|
2023-06-03 09:55:10 +00:00
|
|
|
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);
|
2023-04-12 16:11:36 +00:00
|
|
|
|
|
|
|
TRY(inode->remove_entry(basename.chars()));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2023-05-20 19:46:31 +00:00
|
|
|
|
|
|
|
Result<u64> 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;
|
|
|
|
}
|
2023-05-23 13:42:38 +00:00
|
|
|
|
|
|
|
Result<u64> 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;
|
|
|
|
}
|
2023-05-27 09:32:40 +00:00
|
|
|
|
|
|
|
Result<u64> 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;
|
|
|
|
}
|