diff --git a/apps/ln.cpp b/apps/ln.cpp index 232d4104..1d859c23 100644 --- a/apps/ln.cpp +++ b/apps/ln.cpp @@ -5,16 +5,21 @@ Result luna_main(int argc, char** argv) { StringView target; - StringView link; + StringView linkpath; + + bool create_symlink { false }; os::ArgumentParser parser; - parser.add_description("Create symbolic links."); + parser.add_description("Create links between files."); parser.add_system_program_info("ln"_sv); parser.add_positional_argument(target, "target", true); - parser.add_positional_argument(link, "linkpath", true); + parser.add_positional_argument(linkpath, "linkpath", true); + parser.add_switch_argument(create_symlink, 's', "symbolic"_sv, "create symlinks instead of hard links"_sv); parser.parse(argc, argv); - if (symlink(target.chars(), link.chars()) < 0) + auto link_function = create_symlink ? symlink : link; + + if (link_function(target.chars(), linkpath.chars()) < 0) { perror("ln"); return 1; diff --git a/kernel/src/fs/Mount.h b/kernel/src/fs/Mount.h index c239b849..97f31758 100644 --- a/kernel/src/fs/Mount.h +++ b/kernel/src/fs/Mount.h @@ -62,6 +62,11 @@ class MountInode : public VFS::Inode, public LinkedListNode return m_mount_root_inode->gid(); } + nlink_t nlinks() const override + { + return m_mount_root_inode->nlinks(); + } + Result chmod(mode_t mode) override { return m_mount_root_inode->chmod(mode); diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index e5de6afa..0f9ca990 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -116,6 +116,11 @@ namespace VFS virtual mode_t mode() const = 0; + virtual nlink_t nlinks() const + { + return 1; + } + virtual u32 uid() const { return 0; diff --git a/kernel/src/fs/tmpfs/FileSystem.h b/kernel/src/fs/tmpfs/FileSystem.h index d082d436..2e39cead 100644 --- a/kernel/src/fs/tmpfs/FileSystem.h +++ b/kernel/src/fs/tmpfs/FileSystem.h @@ -94,6 +94,11 @@ namespace TmpFS return m_gid; } + nlink_t nlinks() const override + { + return (nlink_t)m_nlinks; + } + Result chmod(mode_t mode) override { m_mode = mode; @@ -200,6 +205,11 @@ namespace TmpFS return m_gid; } + nlink_t nlinks() const override + { + return (nlink_t)m_nlinks; + } + Result chmod(mode_t) override { return {}; @@ -329,6 +339,11 @@ namespace TmpFS return m_gid; } + nlink_t nlinks() const override + { + return (nlink_t)m_nlinks; + } + Result chmod(mode_t mode) override { m_mode = mode; diff --git a/kernel/src/sys/link.cpp b/kernel/src/sys/link.cpp index 6fed234d..4828ca75 100644 --- a/kernel/src/sys/link.cpp +++ b/kernel/src/sys/link.cpp @@ -2,6 +2,7 @@ #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" +#include #include Result sys_unlinkat(Registers*, SyscallArgs args) @@ -88,3 +89,36 @@ Result sys_readlinkat(Registers*, SyscallArgs args) 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; +} diff --git a/kernel/src/sys/stat.cpp b/kernel/src/sys/stat.cpp index 055a83a2..5d878d3f 100644 --- a/kernel/src/sys/stat.cpp +++ b/kernel/src/sys/stat.cpp @@ -38,7 +38,7 @@ Result sys_fstatat(Registers*, SyscallArgs args) kstat.st_ino = inode->inode_number(); kstat.st_mode = make_mode(inode->mode(), inode->type()); - kstat.st_nlink = 1; // FIXME: Count hard links to files. + kstat.st_nlink = inode->nlinks(); kstat.st_uid = inode->uid(); kstat.st_gid = inode->gid(); kstat.st_size = inode->size(); diff --git a/libc/include/unistd.h b/libc/include/unistd.h index fca34c81..fd1be7f2 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -145,6 +145,12 @@ extern "C" /* Read the contents of a symbolic link relative to a file descriptor. */ ssize_t readlinkat(int dirfd, const char* path, char* buf, size_t max); + /* Create a hard link. */ + int link(const char* oldpath, const char* newpath); + + /* Create a hard link relative to a file descriptor. */ + int linkat(int olddirfd, const char* oldpath, int newdirfd, const char* newpath, int flags); + #ifdef __cplusplus } #endif diff --git a/libc/src/unistd.cpp b/libc/src/unistd.cpp index 5be9a16e..14898410 100644 --- a/libc/src/unistd.cpp +++ b/libc/src/unistd.cpp @@ -430,4 +430,16 @@ extern "C" long rc = syscall(SYS_readlinkat, dirfd, path, buf, max); __errno_return(rc, ssize_t); } + + int link(const char* oldpath, const char* newpath) + { + long rc = syscall(SYS_linkat, AT_FDCWD, oldpath, AT_FDCWD, newpath, 0); + __errno_return(rc, int); + } + + int linkat(int olddirfd, const char* oldpath, int newdirfd, const char* newpath, int flags) + { + long rc = syscall(SYS_linkat, olddirfd, oldpath, newdirfd, newpath, flags); + __errno_return(rc, int); + } } diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 0a9583a4..f9928f15 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -5,7 +5,7 @@ _e(lseek) _e(mkdir) _e(execve) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents) _e(getuid) _e(geteuid) \ _e(getgid) _e(getegid) _e(setuid) _e(setgid) _e(seteuid) _e(setegid) _e(fchmodat) _e(fchownat) _e(ioctl) \ _e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \ - _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) + _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) enum Syscalls {