diff --git a/kernel/api/dirent.h b/kernel/api/dirent.h index 236fc771..37905802 100644 --- a/kernel/api/dirent.h +++ b/kernel/api/dirent.h @@ -1,5 +1,7 @@ #pragma once +#include + #define DT_UNKNOWN 0 #define DT_FIFO 1 #define DT_CHR 2 @@ -18,3 +20,11 @@ struct linux_dirent { // char pad; // Zero padding byte // char d_type; // File type }; + +struct linux_dirent64 { + uint64_t d_ino; + int64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[]; +}; diff --git a/kernel/api/sys/stat.h b/kernel/api/sys/stat.h index 7d0b002b..1cd8d810 100644 --- a/kernel/api/sys/stat.h +++ b/kernel/api/sys/stat.h @@ -55,3 +55,36 @@ struct linux_stat { unsigned long __unused4; unsigned long __unused5; }; + +struct linux_stat64 { + unsigned long long st_dev; + unsigned char __pad0[4]; + + unsigned long __st_ino; + + unsigned int st_mode; + unsigned int st_nlink; + + unsigned long st_uid; + unsigned long st_gid; + + unsigned long long st_rdev; + unsigned char __pad3[4]; + + long long st_size; + unsigned long st_blksize; + + /* Number 512-byte blocks allocated. */ + unsigned long long st_blocks; + + unsigned long st_atime; + unsigned long st_atime_nsec; + + unsigned long st_mtime; + unsigned int st_mtime_nsec; + + unsigned long st_ctime; + unsigned long st_ctime_nsec; + + unsigned long long st_ino; +}; diff --git a/kernel/api/sys/syscall.h b/kernel/api/sys/syscall.h index 439d86b3..6aef6b4c 100644 --- a/kernel/api/sys/syscall.h +++ b/kernel/api/sys/syscall.h @@ -46,14 +46,20 @@ #define SYS_getpgid 132 #define SYS_getdents 141 #define SYS_sched_yield 158 +#define SYS_nanosleep 162 #define SYS_poll 168 #define SYS_getcwd 183 #define SYS_mmap2 192 +#define SYS_stat64 195 +#define SYS_lstat64 196 +#define SYS_getdents64 220 #define SYS_gettid 224 #define SYS_set_thread_area 243 #define SYS_get_thread_area 244 #define SYS_exit_group 252 #define SYS_set_tid_address 258 +#define SYS_clock_gettime 265 +#define SYS_clock_nanosleep 267 #define SYS_socket 359 #define SYS_bind 361 #define SYS_connect 362 diff --git a/kernel/api/time.h b/kernel/api/time.h index 1a109d97..309cabe1 100644 --- a/kernel/api/time.h +++ b/kernel/api/time.h @@ -15,3 +15,10 @@ struct timespec { time_t tv_sec; long long tv_nsec; }; + +typedef int32_t time32_t; + +struct timespec32 { + time32_t tv_sec; + int32_t tv_nsec; +}; diff --git a/kernel/syscall/clock.c b/kernel/syscall/clock.c index 70a66a22..2273efce 100644 --- a/kernel/syscall/clock.c +++ b/kernel/syscall/clock.c @@ -17,6 +17,20 @@ int sys_clock_gettime(clockid_t clk_id, struct timespec* user_tp) { return 0; } +int sys_clock_gettime32(clockid_t clk_id, struct timespec32* user_tp) { + struct timespec tp; + int rc = time_now(clk_id, &tp); + if (IS_ERR(rc)) + return rc; + struct timespec32 tp32 = { + .tv_sec = tp.tv_sec, + .tv_nsec = tp.tv_nsec, + }; + if (copy_to_user(user_tp, &tp32, sizeof(struct timespec32))) + return -EINVAL; + return 0; +} + struct sleep_blocker { clockid_t clock_id; struct timespec deadline; @@ -28,13 +42,9 @@ static bool unblock_sleep(const struct sleep_blocker* blocker) { return timespec_compare(&now, &blocker->deadline) >= 0; } -int sys_clock_nanosleep(clockid_t clockid, int flags, - const struct timespec* user_request, - struct timespec* user_remain) { - struct timespec request; - if (copy_from_user(&request, user_request, sizeof(struct timespec))) - return -EFAULT; - +static int clock_nanosleep(clockid_t clockid, int flags, + const struct timespec* request, + struct timespec* remain) { struct timespec deadline = {0}; // Call time_now regardless of the flags to validate the clockid int rc = time_now(clockid, &deadline); @@ -43,10 +53,10 @@ int sys_clock_nanosleep(clockid_t clockid, int flags, switch (flags) { case 0: - timespec_add(&deadline, &request); + timespec_add(&deadline, request); break; case TIMER_ABSTIME: - deadline = request; + deadline = *request; break; default: return -EINVAL; @@ -56,15 +66,63 @@ int sys_clock_nanosleep(clockid_t clockid, int flags, rc = sched_block((unblock_fn)unblock_sleep, &blocker, 0); if (IS_ERR(rc)) return rc; - if (user_remain && flags != TIMER_ABSTIME) { - struct timespec remain = deadline; + if (remain && flags != TIMER_ABSTIME) { + *remain = deadline; struct timespec now; rc = time_now(clockid, &now); if (IS_ERR(rc)) return rc; - timespec_saturating_sub(&remain, &now); + timespec_saturating_sub(remain, &now); + } + return 0; +} + +int sys_clock_nanosleep(clockid_t clockid, int flags, + const struct timespec* user_request, + struct timespec* user_remain) { + struct timespec request; + if (copy_from_user(&request, user_request, sizeof(struct timespec))) + return -EFAULT; + struct timespec remain = {0}; + int rc = + clock_nanosleep(clockid, flags, &request, user_remain ? &remain : NULL); + if (IS_ERR(rc)) + return rc; + if (user_remain) { if (copy_to_user(user_remain, &remain, sizeof(struct timespec))) return -EFAULT; } return 0; } + +int sys_clock_nanosleep_time32(clockid_t clockid, int flags, + const struct timespec32* user_request, + struct timespec32* user_remain) { + struct timespec32 request32; + if (copy_from_user(&request32, user_request, sizeof(struct timespec32))) + return -EFAULT; + struct timespec request = { + .tv_sec = request32.tv_sec, + .tv_nsec = request32.tv_nsec, + }; + struct timespec remain = {0}; + int rc = + clock_nanosleep(clockid, flags, &request, user_remain ? &remain : NULL); + if (IS_ERR(rc)) + return rc; + if (user_remain) { + struct timespec32 rem = { + .tv_sec = remain.tv_sec, + .tv_nsec = remain.tv_nsec, + }; + if (copy_to_user(user_remain, &rem, sizeof(struct timespec32))) + return -EFAULT; + } + return 0; +} + +int sys_nanosleep_time32(const struct timespec32* user_duration, + struct timespec32* user_rem) { + return sys_clock_nanosleep_time32(CLOCK_MONOTONIC, 0, user_duration, + user_rem); +} diff --git a/kernel/syscall/fs.c b/kernel/syscall/fs.c index 802cf2ce..297aeb4b 100644 --- a/kernel/syscall/fs.c +++ b/kernel/syscall/fs.c @@ -140,13 +140,59 @@ static void stat_to_linux_stat(const struct stat* stat, }; } -int sys_newlstat(const char* user_pathname, struct linux_stat* user_buf) { +NODISCARD static int stat_to_linux_stat64(const struct stat* stat, + struct linux_stat64* linux_stat) { + *linux_stat = (struct linux_stat64){ + .st_dev = stat->st_dev, + .st_ino = stat->st_ino, + .__st_ino = stat->st_ino, + .st_mode = stat->st_mode, + .st_nlink = stat->st_nlink, + .st_uid = stat->st_uid, + .st_gid = stat->st_gid, + .st_rdev = stat->st_rdev, + .st_size = stat->st_size, + .st_blksize = stat->st_blksize, + .st_blocks = stat->st_blocks, + .st_atime = stat->st_atim.tv_sec, + .st_atime_nsec = stat->st_atim.tv_nsec, + .st_mtime = stat->st_mtim.tv_sec, + .st_mtime_nsec = stat->st_mtim.tv_nsec, + .st_ctime = stat->st_ctim.tv_sec, + .st_ctime_nsec = stat->st_ctim.tv_nsec, + }; + + if (sizeof(linux_stat->st_ino) < sizeof(stat->st_ino) && + linux_stat->st_ino != stat->st_ino) + return -EOVERFLOW; + return 0; +} + +static int lstat(const char* user_pathname, struct stat* buf) { char pathname[PATH_MAX]; int rc = copy_pathname_from_user(pathname, user_pathname); if (IS_ERR(rc)) return rc; + rc = vfs_stat(pathname, buf, O_NOFOLLOW | O_NOFOLLOW_NOERROR); + if (IS_ERR(rc)) + return rc; + return 0; +} + +static int stat(const char* user_pathname, struct stat* buf) { + char pathname[PATH_MAX]; + int rc = copy_pathname_from_user(pathname, user_pathname); + if (IS_ERR(rc)) + return rc; + rc = vfs_stat(pathname, buf, 0); + if (IS_ERR(rc)) + return rc; + return 0; +} + +int sys_newlstat(const char* user_pathname, struct linux_stat* user_buf) { struct stat buf; - rc = vfs_stat(pathname, &buf, O_NOFOLLOW | O_NOFOLLOW_NOERROR); + int rc = lstat(user_pathname, &buf); if (IS_ERR(rc)) return rc; struct linux_stat linux_stat; @@ -157,12 +203,8 @@ int sys_newlstat(const char* user_pathname, struct linux_stat* user_buf) { } int sys_newstat(const char* user_pathname, struct linux_stat* user_buf) { - char pathname[PATH_MAX]; - int rc = copy_pathname_from_user(pathname, user_pathname); - if (IS_ERR(rc)) - return rc; struct stat buf; - rc = vfs_stat(pathname, &buf, 0); + int rc = stat(user_pathname, &buf); if (IS_ERR(rc)) return rc; struct linux_stat linux_stat; @@ -172,6 +214,34 @@ int sys_newstat(const char* user_pathname, struct linux_stat* user_buf) { return 0; } +int sys_lstat64(const char* pathname, struct linux_stat64* user_buf) { + struct stat buf; + int rc = lstat(pathname, &buf); + if (IS_ERR(rc)) + return rc; + struct linux_stat64 linux_stat; + rc = stat_to_linux_stat64(&buf, &linux_stat); + if (IS_ERR(rc)) + return rc; + if (copy_to_user(user_buf, &linux_stat, sizeof(struct linux_stat64))) + return -EFAULT; + return 0; +} + +int sys_stat64(const char* pathname, struct linux_stat64* user_buf) { + struct stat buf; + int rc = stat(pathname, &buf); + if (IS_ERR(rc)) + return rc; + struct linux_stat64 linux_stat; + rc = stat_to_linux_stat64(&buf, &linux_stat); + if (IS_ERR(rc)) + return rc; + if (copy_to_user(user_buf, &linux_stat, sizeof(struct linux_stat64))) + return -EFAULT; + return 0; +} + int sys_symlink(const char* user_target, const char* user_linkpath) { char target[PATH_MAX]; int rc = copy_pathname_from_user(target, user_target); @@ -456,12 +526,31 @@ int sys_rmdir(const char* user_pathname) { } struct fill_dir_ctx { - struct linux_dirent* user_dirp; + void* user_dirp; unsigned remaining_count; long nwritten; int rc; }; +static ssize_t getdents(int fd, void* user_dirp, size_t count, + bool (*fill_dir)(const char* name, uint8_t type, + void* ctx)) { + struct file* file = task_get_file(fd); + if (IS_ERR(file)) + return PTR_ERR(file); + + struct fill_dir_ctx ctx = { + .user_dirp = user_dirp, + .remaining_count = count, + }; + int rc = file_getdents(file, fill_dir, &ctx); + if (IS_ERR(rc)) + return rc; + if (IS_ERR(ctx.rc)) + return ctx.rc; + return ctx.nwritten; +} + static bool fill_dir(const char* name, uint8_t type, void* raw_ctx) { struct fill_dir_ctx* ctx = (struct fill_dir_ctx*)raw_ctx; size_t name_len = strlen(name); @@ -493,28 +582,52 @@ static bool fill_dir(const char* name, uint8_t type, void* raw_ctx) { return false; } - ctx->user_dirp = - (struct linux_dirent*)((unsigned char*)user_dent + rec_len); + ctx->user_dirp = (unsigned char*)user_dent + rec_len; ctx->remaining_count -= rec_len; ctx->nwritten += rec_len; return true; } long sys_getdents(int fd, struct linux_dirent* user_dirp, size_t count) { - struct file* file = task_get_file(fd); - if (IS_ERR(file)) - return PTR_ERR(file); + return getdents(fd, user_dirp, count, fill_dir); +} - struct fill_dir_ctx ctx = { - .user_dirp = user_dirp, - .remaining_count = count, +static bool fill_dir64(const char* name, uint8_t type, void* raw_ctx) { + struct fill_dir_ctx* ctx = (struct fill_dir_ctx*)raw_ctx; + size_t name_len = strlen(name); + size_t name_size = name_len + 1; + size_t rec_len = offsetof(struct linux_dirent64, d_name) // + + name_size; // d_name + rec_len = round_up(rec_len, alignof(struct linux_dirent64)); + if (ctx->remaining_count < rec_len) { + if (ctx->nwritten == 0) + ctx->rc = -EINVAL; + return false; + } + + struct linux_dirent64* user_dent = ctx->user_dirp; + struct linux_dirent64 dent = { + .d_reclen = rec_len, + .d_type = type, }; - int rc = file_getdents(file, fill_dir, &ctx); - if (IS_ERR(rc)) - return rc; - if (IS_ERR(ctx.rc)) - return ctx.rc; - return ctx.nwritten; + if (copy_to_user(user_dent, &dent, + offsetof(struct linux_dirent64, d_name))) { + ctx->rc = -EFAULT; + return false; + } + if (copy_to_user(user_dent->d_name, name, name_size)) { + ctx->rc = -EFAULT; + return false; + } + + ctx->user_dirp = (unsigned char*)user_dent + rec_len; + ctx->remaining_count -= rec_len; + ctx->nwritten += rec_len; + return true; +} + +ssize_t sys_getdents64(int fd, struct linux_dirent* dirp, size_t count) { + return getdents(fd, dirp, count, fill_dir64); } int sys_fcntl(int fd, int cmd, uintptr_t arg) { diff --git a/kernel/syscall/syscall.h b/kernel/syscall/syscall.h index 633566a4..736eec1f 100644 --- a/kernel/syscall/syscall.h +++ b/kernel/syscall/syscall.h @@ -60,14 +60,20 @@ F(getpgid, sys_getpgid, 0) \ F(getdents, sys_getdents, 0) \ F(sched_yield, sys_sched_yield, 0) \ + F(nanosleep, sys_nanosleep_time32, 0) \ F(poll, sys_poll, 0) \ F(getcwd, sys_getcwd, 0) \ F(mmap2, sys_mmap_pgoff, 0) \ + F(stat64, sys_stat64, 0) \ + F(lstat64, sys_lstat64, 0) \ + F(getdents64, sys_getdents64, 0) \ F(gettid, sys_gettid, 0) \ F(set_thread_area, sys_set_thread_area, 0) \ F(get_thread_area, sys_get_thread_area, 0) \ F(exit_group, sys_exit_group, 0) \ F(set_tid_address, sys_set_tid_address, 0) \ + F(clock_gettime, sys_clock_gettime32, 0) \ + F(clock_nanosleep, sys_clock_nanosleep_time32, 0) \ F(socket, sys_socket, 0) \ F(bind, sys_bind, 0) \ F(connect, sys_connect, 0) \ @@ -85,6 +91,7 @@ struct user_desc; struct utsname; struct linux_dirent; struct linux_stat; +struct linux_stat64; void sys_exit(int status); pid_t sys_fork(struct registers*); @@ -132,15 +139,24 @@ int sys_sigprocmask(int how, const sigset_t* set, sigset_t* oldset); pid_t sys_getpgid(pid_t pid); long sys_getdents(int fd, struct linux_dirent* dirp, size_t count); int sys_sched_yield(void); +int sys_nanosleep_time32(const struct timespec32* duration, + struct timespec32* rem); int sys_poll(struct pollfd* fds, nfds_t nfds, int timeout); int sys_getcwd(char* buf, size_t size); void* sys_mmap_pgoff(void* addr, size_t length, int prot, int flags, int fd, unsigned long pgoff); +int sys_stat64(const char* pathname, struct linux_stat64* buf); +int sys_lstat64(const char* pathname, struct linux_stat64* buf); +ssize_t sys_getdents64(int fd, struct linux_dirent* dirp, size_t count); pid_t sys_gettid(void); int sys_get_thread_area(struct user_desc* u_info); int sys_set_thread_area(struct user_desc* u_info); void sys_exit_group(int status); pid_t sys_set_tid_address(int* tidptr); +int sys_clock_gettime32(clockid_t clk_id, struct timespec32* tp); +int sys_clock_nanosleep_time32(clockid_t clockid, int flags, + const struct timespec32* request, + struct timespec32* remain); int sys_socket(int domain, int type, int protocol); int sys_bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen); int sys_connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen); diff --git a/kernel/syscall/unimplemented.h b/kernel/syscall/unimplemented.h index c2f4f771..96380f1a 100644 --- a/kernel/syscall/unimplemented.h +++ b/kernel/syscall/unimplemented.h @@ -123,7 +123,6 @@ F(159, sched_get_priority_max) \ F(160, sched_get_priority_min) \ F(161, sched_rr_get_interval) \ - F(162, nanosleep) \ F(163, mremap) \ F(164, setresuid) \ F(165, getresuid) \ @@ -153,8 +152,6 @@ F(191, ugetrlimit) \ F(193, truncate64) \ F(194, ftruncate64) \ - F(195, stat64) \ - F(196, lstat64) \ F(197, fstat64) \ F(198, lchown32) \ F(199, getuid32) \ @@ -178,7 +175,6 @@ F(217, pivot_root) \ F(218, mincore) \ F(219, madvise) \ - F(220, getdents64) \ F(221, fcntl64) \ F(225, readahead) \ F(226, setxattr) \ @@ -215,9 +211,7 @@ F(262, timer_getoverrun) \ F(263, timer_delete) \ F(264, clock_settime) \ - F(265, clock_gettime) \ F(266, clock_getres) \ - F(267, clock_nanosleep) \ F(268, statfs64) \ F(269, fstatfs64) \ F(270, tgkill) \