Skip to content

Commit 4fc918b

Browse files
feat(kernel): add symlink systemcall
Signed-off-by: Anhad Singh <[email protected]>
1 parent 7d054db commit 4fc918b

File tree

8 files changed

+141
-16
lines changed

8 files changed

+141
-16
lines changed

patches/mlibc/jinx-working-patch.patch

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ index 0000000..1b61d5a
102102
+ ret
103103
+.section .note.GNU-stack,"",%progbits
104104
diff --git mlibc-clean/sysdeps/aero/generic/filesystem.cpp mlibc-workdir/sysdeps/aero/generic/filesystem.cpp
105-
index 33a11f4..8795382 100644
105+
index 33a11f4..fe5773d 100644
106106
--- mlibc-clean/sysdeps/aero/generic/filesystem.cpp
107107
+++ mlibc-workdir/sysdeps/aero/generic/filesystem.cpp
108108
@@ -102,31 +102,24 @@ int sys_access(const char *filename, int mode) {
@@ -127,15 +127,15 @@ index 33a11f4..8795382 100644
127127
+ case fsfd_target::fd_path:
128128
break;
129129
- }
130-
-
130+
131131
- default: {
132132
- mlibc::infoLogger()
133133
- << "mlibc warning: sys_stat: unsupported fsfd target"
134134
- << frg::endlog;
135135
- return EINVAL;
136136
- }
137137
- }
138-
138+
-
139139
- if (result < 0) {
140140
- return -result;
141141
+ default:
@@ -149,6 +149,39 @@ index 33a11f4..8795382 100644
149149
return 0;
150150
}
151151

152+
@@ -212,6 +205,17 @@ int sys_unlinkat(int fd, const char *path, int flags) {
153+
return 0;
154+
}
155+
156+
+int sys_symlink(const char *target_path, const char *link_path) {
157+
+ return sys_symlinkat(target_path, AT_FDCWD, link_path);
158+
+}
159+
+
160+
+int sys_symlinkat(const char *target_path, int dirfd, const char *link_path) {
161+
+ auto ret = syscall(SYS_SYMLINK_AT, dirfd, target_path, strlen(target_path), link_path, strlen(link_path));
162+
+ if (int e = sc_error(ret); e)
163+
+ return e;
164+
+ return 0;
165+
+}
166+
+
167+
struct aero_dir_entry {
168+
size_t inode;
169+
size_t offset;
170+
diff --git mlibc-clean/sysdeps/aero/include/aero/syscall.h mlibc-workdir/sysdeps/aero/include/aero/syscall.h
171+
index 39c5b65..49533cc 100644
172+
--- mlibc-clean/sysdeps/aero/include/aero/syscall.h
173+
+++ mlibc-workdir/sysdeps/aero/include/aero/syscall.h
174+
@@ -82,6 +82,10 @@
175+
#define SYS_SOCK_SHUTDOWN 75
176+
#define SYS_GETPEERNAME 76
177+
#define SYS_GETSOCKNAME 77
178+
+#define SYS_DEBUG 78
179+
+#define SYS_SETSOCKOPT 79
180+
+#define SYS_GETSOCKOPT 80
181+
+#define SYS_SYMLINK_AT 81
182+
183+
// Invalid syscall used to trigger a log error in the kernel (as a hint)
184+
// so, that we can implement the syscall in the kernel.
152185
diff --git mlibc-clean/sysdeps/aero/meson.build mlibc-workdir/sysdeps/aero/meson.build
153186
index 9d10701..3d2a883 100644
154187
--- mlibc-clean/sysdeps/aero/meson.build

src/aero_kernel/src/fs/ext2/mod.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use super::block::{self, BlockDevice, CachedAccess};
4141

4242
use super::cache::{DirCacheItem, INodeCacheItem};
4343
use super::path::PathBuf;
44-
use super::{cache, FileSystemError};
44+
use super::{cache, FileSystemError, Path};
4545

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

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

550+
fn symlink(&self, target: &Path) -> super::Result<()> {
551+
let mut inode = self.inode.write();
552+
inode.set_file_type(FileType::Symlink);
553+
554+
let target_len = target.len();
555+
let data_bytes: &mut [u8] = bytemuck::cast_slice_mut(&mut inode.data_ptr);
556+
557+
// XXX: Symlinks under 60 bytes store data within the inode to avoid allocating a full
558+
// block.
559+
if target_len <= data_bytes.len() {
560+
data_bytes[..target_len].copy_from_slice(target.as_bytes());
561+
inode.set_size(target_len);
562+
} else {
563+
drop(inode);
564+
assert_eq!(self.write(0, target.as_bytes())?, target_len);
565+
}
566+
567+
Ok(())
568+
}
569+
548570
fn mmap(&self, offset: usize, size: usize, flags: MMapFlags) -> super::Result<PhysFrame> {
549571
assert!(self.proxy.is_none());
550572

src/aero_kernel/src/fs/inode.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use super::cache::{Cacheable, CachedINode, DirCacheItem, INodeCacheItem};
3737
use super::devfs::DevINode;
3838
use super::file_table::FileHandle;
3939
use super::path::PathBuf;
40-
use super::{cache, FileSystem, FileSystemError, Result};
40+
use super::{cache, FileSystem, FileSystemError, Path, Result};
4141

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

@@ -279,6 +279,10 @@ pub trait INodeInterface: Send + Sync {
279279
fn link(&self, _name: &str, _src: DirCacheItem) -> Result<()> {
280280
Err(FileSystemError::NotSupported)
281281
}
282+
283+
fn symlink(&self, _target: &Path) -> Result<()> {
284+
Err(FileSystemError::NotSupported)
285+
}
282286
}
283287

284288
/// Structure representing the crucial, characteristics of an inode. The metadata

src/aero_kernel/src/syscall/fs.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ pub fn fstat(fd: usize, path: &Path, flags: usize, stat: &mut Stat) -> Result<us
459459

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

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

473-
log::debug!("{}", at.absolute_path());
474-
475-
let ent = fs::lookup_path_with(at, path, LookupMode::None, true)?;
473+
let resolve_last = !flags.contains(AtFlags::SYMLINK_NOFOLLOW);
474+
let ent = fs::lookup_path_with(at, path, LookupMode::None, resolve_last)?;
476475
*stat = ent.inode().stat()?;
477476
Ok(0)
478477
}
@@ -746,3 +745,26 @@ pub fn rename(src: &Path, dest: &Path) -> Result<usize, SyscallError> {
746745
});
747746
Ok(0)
748747
}
748+
749+
#[syscall]
750+
pub fn symlink(link_dirfd: usize, target: &Path, linkpath: &Path) -> Result<usize, SyscallError> {
751+
// TODO(andypython): the following code is reused in a couple of places. Isolate this inside the
752+
// syscall parsing for FileDescriptor with an argument as a generic specifing the allowance
753+
// of this value.
754+
//
755+
// If the pathname given in `linkpath` is relative, then it is interpreted relative to the
756+
// directory referred to by the file descriptor `link_dirfd`.
757+
let at = match link_dirfd as isize {
758+
AT_FDCWD if !linkpath.is_absolute() => scheduler::current_thread().cwd_dirent(),
759+
_ if !linkpath.is_absolute() => FileDescriptor::from_usize(link_dirfd)
760+
.handle()?
761+
.inode
762+
.clone(),
763+
_ => fs::root_dir().clone(),
764+
};
765+
766+
let ent = fs::lookup_path_with(at, linkpath, LookupMode::Create, false)?;
767+
ent.inode().symlink(target)?;
768+
769+
Ok(0)
770+
}

src/aero_kernel/src/syscall/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ pub fn generic_do_syscall(
230230
SYS_LINK => fs::link(b, c, d, e),
231231
SYS_POLL => fs::poll(b, c, d, e),
232232
SYS_RENAME => fs::rename(b, c, d, e),
233+
SYS_SYMLINK_AT => fs::symlink(b, c, d, e, f),
233234

234235
// epoll calls:
235236
SYS_EPOLL_CREATE => fs::epoll_create(b),

src/aero_syscall/src/consts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ pub const SYS_GETSOCKNAME: usize = 77;
101101
pub const SYS_DEBUG: usize = 78;
102102
pub const SYS_SETSOCKOPT: usize = 79;
103103
pub const SYS_GETSOCKOPT: usize = 80;
104+
pub const SYS_SYMLINK_AT: usize = 81;
104105

105106
// constants for fcntl()'s command argument:
106107
pub const F_DUPFD: usize = 1;

src/aero_syscall/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,10 +717,15 @@ pub struct Stat {
717717
bitflags::bitflags! {
718718
#[repr(transparent)]
719719
pub struct AtFlags: usize {
720+
/// Allow empty relative pathname.
720721
const EMPTY_PATH = 1;
722+
/// Follow symbolic links.
721723
const SYMLINK_FOLLOW = 2;
724+
/// Do not follow symbolic links.
722725
const SYMLINK_NOFOLLOW = 4;
726+
/// Remove directory instead of unlinking file.
723727
const REMOVEDIR = 8;
728+
/// Test access permitted for effective IDs, not real IDs.
724729
const EACCESS = 512;
725730
}
726731
}

userland/tests/utest.cc

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
#include <asm/unistd_64.h>
66
#include <cassert>
7+
#include <fcntl.h>
78
#include <csetjmp>
9+
#include <fstream>
10+
#include <sys/stat.h>
811
#include <errno.h>
912
#include <iostream>
1013
#include <stddef.h>
@@ -199,7 +202,7 @@ DEFINE_TEST(epoll_mod_active, ([] {
199202

200203
// Use mmap to change the protection flags instead of mprotect.
201204
DEFINE_TEST(mmap_partial_remap, ([] {
202-
enable_systrace();
205+
//enable_systrace();
203206

204207
const int bytes = PAGE_SIZE * 2;
205208

@@ -610,6 +613,42 @@ DEFINE_TEST(mprotect_check_whether_three_way_split_mappings_are_handled_correctl
610613
});
611614
}))
612615

616+
DEFINE_TEST(stat, ([] {
617+
// SYM_B -> SYM_A -> /tmp/SYM_REAL
618+
619+
// TODO: make mknod()
620+
FILE *sym_real = fopen("/tmp/SYM_REAL", "w");
621+
622+
if (symlink("/tmp/SYM_REAL", "/tmp/SYM_A") == -1)
623+
assert(!"(1) symlink() failed");
624+
625+
if (symlink("/tmp/SYM_A", "/tmp/SYM_B") == -1)
626+
assert(!"(2) symlink() failed");
627+
628+
struct stat statbuf;
629+
if (fstatat(AT_FDCWD, "/tmp/SYM_B", &statbuf, AT_SYMLINK_NOFOLLOW) == -1)
630+
assert(!"fstatat() failed");
631+
632+
// Check that the symlink is not followed.
633+
assert(S_ISLNK(statbuf.st_mode));
634+
635+
if (fstatat(AT_FDCWD, "/tmp/SYM_B", &statbuf, 0) == -1)
636+
assert(!"fstatat() failed");
637+
638+
// Check that the symlink is followed.
639+
assert(S_ISREG(statbuf.st_mode));
640+
641+
if (unlink("/tmp/SYM_A") == -1)
642+
assert(!"unlink() failed");
643+
644+
if (unlink("/tmp/SYM_B") == -1)
645+
assert(!"unlink() failed");
646+
647+
fclose(sym_real);
648+
if (unlink("/tmp/SYM_REAL") == -1)
649+
assert(!"unlink() failed");
650+
}))
651+
613652
static inline bool cpuid(uint32_t leaf, uint32_t subleaf,
614653
uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
615654
uint32_t cpuid_max;
@@ -724,10 +763,8 @@ void abstract_test_case::register_case(abstract_test_case *tcp) {
724763

725764
int main() {
726765
// Go through all tests and run them.
727-
// for(abstract_test_case *tcp : test_case_ptrs()) {
728-
// std::cout << "tests: Running " << tcp->name() << std::endl;
729-
// tcp->run();
730-
// }
731-
test_bad_sysenter.run();
732-
test_sysenter_system_call.run();
766+
for(abstract_test_case *tcp : test_case_ptrs()) {
767+
std::cout << "tests: Running " << tcp->name() << std::endl;
768+
tcp->run();
769+
}
733770
}

0 commit comments

Comments
 (0)