Skip to content

Commit

Permalink
feat(kernel): add symlink systemcall
Browse files Browse the repository at this point in the history
Signed-off-by: Anhad Singh <[email protected]>
  • Loading branch information
Andy-Python-Programmer committed Mar 29, 2024
1 parent 7d054db commit 4fc918b
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 16 deletions.
39 changes: 36 additions & 3 deletions patches/mlibc/jinx-working-patch.patch
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ index 0000000..1b61d5a
+ ret
+.section .note.GNU-stack,"",%progbits
diff --git mlibc-clean/sysdeps/aero/generic/filesystem.cpp mlibc-workdir/sysdeps/aero/generic/filesystem.cpp
index 33a11f4..8795382 100644
index 33a11f4..fe5773d 100644
--- mlibc-clean/sysdeps/aero/generic/filesystem.cpp
+++ mlibc-workdir/sysdeps/aero/generic/filesystem.cpp
@@ -102,31 +102,24 @@ int sys_access(const char *filename, int mode) {
Expand All @@ -127,15 +127,15 @@ index 33a11f4..8795382 100644
+ case fsfd_target::fd_path:
break;
- }
-
- default: {
- mlibc::infoLogger()
- << "mlibc warning: sys_stat: unsupported fsfd target"
- << frg::endlog;
- return EINVAL;
- }
- }
-
- if (result < 0) {
- return -result;
+ default:
Expand All @@ -149,6 +149,39 @@ index 33a11f4..8795382 100644
return 0;
}

@@ -212,6 +205,17 @@ int sys_unlinkat(int fd, const char *path, int flags) {
return 0;
}

+int sys_symlink(const char *target_path, const char *link_path) {
+ return sys_symlinkat(target_path, AT_FDCWD, link_path);
+}
+
+int sys_symlinkat(const char *target_path, int dirfd, const char *link_path) {
+ auto ret = syscall(SYS_SYMLINK_AT, dirfd, target_path, strlen(target_path), link_path, strlen(link_path));
+ if (int e = sc_error(ret); e)
+ return e;
+ return 0;
+}
+
struct aero_dir_entry {
size_t inode;
size_t offset;
diff --git mlibc-clean/sysdeps/aero/include/aero/syscall.h mlibc-workdir/sysdeps/aero/include/aero/syscall.h
index 39c5b65..49533cc 100644
--- mlibc-clean/sysdeps/aero/include/aero/syscall.h
+++ mlibc-workdir/sysdeps/aero/include/aero/syscall.h
@@ -82,6 +82,10 @@
#define SYS_SOCK_SHUTDOWN 75
#define SYS_GETPEERNAME 76
#define SYS_GETSOCKNAME 77
+#define SYS_DEBUG 78
+#define SYS_SETSOCKOPT 79
+#define SYS_GETSOCKOPT 80
+#define SYS_SYMLINK_AT 81

// Invalid syscall used to trigger a log error in the kernel (as a hint)
// so, that we can implement the syscall in the kernel.
diff --git mlibc-clean/sysdeps/aero/meson.build mlibc-workdir/sysdeps/aero/meson.build
index 9d10701..3d2a883 100644
--- mlibc-clean/sysdeps/aero/meson.build
Expand Down
24 changes: 23 additions & 1 deletion src/aero_kernel/src/fs/ext2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use super::block::{self, BlockDevice, CachedAccess};

use super::cache::{DirCacheItem, INodeCacheItem};
use super::path::PathBuf;
use super::{cache, FileSystemError};
use super::{cache, FileSystemError, Path};

use super::inode::{DirEntry, INodeInterface, Metadata, PollFlags, PollTable};
use super::FileSystem;
Expand Down Expand Up @@ -529,6 +529,8 @@ impl INodeInterface for INode {
let path_len = inode.size();
let data_bytes: &[u8] = bytemuck::cast_slice(&inode.data_ptr);

// XXX: Symlinks under 60 bytes store data within the inode to avoid allocating a full
// block.
if path_len <= data_bytes.len() {
let path_bytes = &data_bytes[..path_len];
let path = core::str::from_utf8(path_bytes).or(Err(FileSystemError::InvalidPath))?;
Expand All @@ -545,6 +547,26 @@ impl INodeInterface for INode {
}
}

fn symlink(&self, target: &Path) -> super::Result<()> {
let mut inode = self.inode.write();
inode.set_file_type(FileType::Symlink);

let target_len = target.len();
let data_bytes: &mut [u8] = bytemuck::cast_slice_mut(&mut inode.data_ptr);

// XXX: Symlinks under 60 bytes store data within the inode to avoid allocating a full
// block.
if target_len <= data_bytes.len() {
data_bytes[..target_len].copy_from_slice(target.as_bytes());
inode.set_size(target_len);
} else {
drop(inode);
assert_eq!(self.write(0, target.as_bytes())?, target_len);
}

Ok(())
}

fn mmap(&self, offset: usize, size: usize, flags: MMapFlags) -> super::Result<PhysFrame> {
assert!(self.proxy.is_none());

Expand Down
6 changes: 5 additions & 1 deletion src/aero_kernel/src/fs/inode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use super::cache::{Cacheable, CachedINode, DirCacheItem, INodeCacheItem};
use super::devfs::DevINode;
use super::file_table::FileHandle;
use super::path::PathBuf;
use super::{cache, FileSystem, FileSystemError, Result};
use super::{cache, FileSystem, FileSystemError, Path, Result};

static DIR_CACHE_MARKER: AtomicUsize = AtomicUsize::new(0x00);

Expand Down Expand Up @@ -279,6 +279,10 @@ pub trait INodeInterface: Send + Sync {
fn link(&self, _name: &str, _src: DirCacheItem) -> Result<()> {
Err(FileSystemError::NotSupported)
}

fn symlink(&self, _target: &Path) -> Result<()> {
Err(FileSystemError::NotSupported)
}
}

/// Structure representing the crucial, characteristics of an inode. The metadata
Expand Down
30 changes: 26 additions & 4 deletions src/aero_kernel/src/syscall/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ pub fn fstat(fd: usize, path: &Path, flags: usize, stat: &mut Stat) -> Result<us

// TODO: derive(SysArg) for bitflags.
let flags = AtFlags::from_bits(flags).ok_or(SyscallError::EINVAL)?;
dbg!(flags);
assert!(!flags.intersects(AtFlags::EACCESS | AtFlags::REMOVEDIR));

if path.is_empty() {
if !flags.contains(AtFlags::EMPTY_PATH) {
Expand All @@ -470,9 +470,8 @@ pub fn fstat(fd: usize, path: &Path, flags: usize, stat: &mut Stat) -> Result<us
return Ok(0);
}

log::debug!("{}", at.absolute_path());

let ent = fs::lookup_path_with(at, path, LookupMode::None, true)?;
let resolve_last = !flags.contains(AtFlags::SYMLINK_NOFOLLOW);
let ent = fs::lookup_path_with(at, path, LookupMode::None, resolve_last)?;
*stat = ent.inode().stat()?;
Ok(0)
}
Expand Down Expand Up @@ -746,3 +745,26 @@ pub fn rename(src: &Path, dest: &Path) -> Result<usize, SyscallError> {
});
Ok(0)
}

#[syscall]
pub fn symlink(link_dirfd: usize, target: &Path, linkpath: &Path) -> Result<usize, SyscallError> {
// TODO(andypython): the following code is reused in a couple of places. Isolate this inside the
// syscall parsing for FileDescriptor with an argument as a generic specifing the allowance
// of this value.
//
// If the pathname given in `linkpath` is relative, then it is interpreted relative to the
// directory referred to by the file descriptor `link_dirfd`.
let at = match link_dirfd as isize {
AT_FDCWD if !linkpath.is_absolute() => scheduler::current_thread().cwd_dirent(),
_ if !linkpath.is_absolute() => FileDescriptor::from_usize(link_dirfd)
.handle()?
.inode
.clone(),
_ => fs::root_dir().clone(),
};

let ent = fs::lookup_path_with(at, linkpath, LookupMode::Create, false)?;
ent.inode().symlink(target)?;

Ok(0)
}
1 change: 1 addition & 0 deletions src/aero_kernel/src/syscall/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ pub fn generic_do_syscall(
SYS_LINK => fs::link(b, c, d, e),
SYS_POLL => fs::poll(b, c, d, e),
SYS_RENAME => fs::rename(b, c, d, e),
SYS_SYMLINK_AT => fs::symlink(b, c, d, e, f),

// epoll calls:
SYS_EPOLL_CREATE => fs::epoll_create(b),
Expand Down
1 change: 1 addition & 0 deletions src/aero_syscall/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ pub const SYS_GETSOCKNAME: usize = 77;
pub const SYS_DEBUG: usize = 78;
pub const SYS_SETSOCKOPT: usize = 79;
pub const SYS_GETSOCKOPT: usize = 80;
pub const SYS_SYMLINK_AT: usize = 81;

// constants for fcntl()'s command argument:
pub const F_DUPFD: usize = 1;
Expand Down
5 changes: 5 additions & 0 deletions src/aero_syscall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,10 +717,15 @@ pub struct Stat {
bitflags::bitflags! {
#[repr(transparent)]
pub struct AtFlags: usize {
/// Allow empty relative pathname.
const EMPTY_PATH = 1;
/// Follow symbolic links.
const SYMLINK_FOLLOW = 2;
/// Do not follow symbolic links.
const SYMLINK_NOFOLLOW = 4;
/// Remove directory instead of unlinking file.
const REMOVEDIR = 8;
/// Test access permitted for effective IDs, not real IDs.
const EACCESS = 512;
}
}
51 changes: 44 additions & 7 deletions userland/tests/utest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

#include <asm/unistd_64.h>
#include <cassert>
#include <fcntl.h>
#include <csetjmp>
#include <fstream>
#include <sys/stat.h>
#include <errno.h>
#include <iostream>
#include <stddef.h>
Expand Down Expand Up @@ -199,7 +202,7 @@ DEFINE_TEST(epoll_mod_active, ([] {

// Use mmap to change the protection flags instead of mprotect.
DEFINE_TEST(mmap_partial_remap, ([] {
enable_systrace();
//enable_systrace();

const int bytes = PAGE_SIZE * 2;

Expand Down Expand Up @@ -610,6 +613,42 @@ DEFINE_TEST(mprotect_check_whether_three_way_split_mappings_are_handled_correctl
});
}))

DEFINE_TEST(stat, ([] {
// SYM_B -> SYM_A -> /tmp/SYM_REAL

// TODO: make mknod()
FILE *sym_real = fopen("/tmp/SYM_REAL", "w");

if (symlink("/tmp/SYM_REAL", "/tmp/SYM_A") == -1)
assert(!"(1) symlink() failed");

if (symlink("/tmp/SYM_A", "/tmp/SYM_B") == -1)
assert(!"(2) symlink() failed");

struct stat statbuf;
if (fstatat(AT_FDCWD, "/tmp/SYM_B", &statbuf, AT_SYMLINK_NOFOLLOW) == -1)
assert(!"fstatat() failed");

// Check that the symlink is not followed.
assert(S_ISLNK(statbuf.st_mode));

if (fstatat(AT_FDCWD, "/tmp/SYM_B", &statbuf, 0) == -1)
assert(!"fstatat() failed");

// Check that the symlink is followed.
assert(S_ISREG(statbuf.st_mode));

if (unlink("/tmp/SYM_A") == -1)
assert(!"unlink() failed");

if (unlink("/tmp/SYM_B") == -1)
assert(!"unlink() failed");

fclose(sym_real);
if (unlink("/tmp/SYM_REAL") == -1)
assert(!"unlink() failed");
}))

static inline bool cpuid(uint32_t leaf, uint32_t subleaf,
uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
uint32_t cpuid_max;
Expand Down Expand Up @@ -724,10 +763,8 @@ void abstract_test_case::register_case(abstract_test_case *tcp) {

int main() {
// Go through all tests and run them.
// for(abstract_test_case *tcp : test_case_ptrs()) {
// std::cout << "tests: Running " << tcp->name() << std::endl;
// tcp->run();
// }
test_bad_sysenter.run();
test_sysenter_system_call.run();
for(abstract_test_case *tcp : test_case_ptrs()) {
std::cout << "tests: Running " << tcp->name() << std::endl;
tcp->run();
}
}

0 comments on commit 4fc918b

Please sign in to comment.