1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/device/timer.hpp"
4#include "mos/ipc/ipc_io.hpp"
5#include "mos/ipc/memfd.hpp"
6#include "mos/ipc/pipe.hpp"
7#include "mos/misc/power.hpp"
8#include "mos/mm/dma.hpp"
9#include "mos/tasks/signal.hpp"
10
11#include <bits/posix/iovec.h>
12#include <errno.h>
13#include <fcntl.h>
14#include <mos/filesystem/fs_types.h>
15#include <mos/filesystem/vfs.hpp>
16#include <mos/io/io.hpp>
17#include <mos/locks/futex.hpp>
18#include <mos/mm/mmap.hpp>
19#include <mos/mm/paging/paging.hpp>
20#include <mos/platform/platform.hpp>
21#include <mos/syscall/decl.h>
22#include <mos/syscall/number.h>
23#include <mos/syslog/printk.hpp>
24#include <mos/tasks/elf.hpp>
25#include <mos/tasks/process.hpp>
26#include <mos/tasks/schedule.hpp>
27#include <mos/tasks/task_types.hpp>
28#include <mos/tasks/thread.hpp>
29#include <mos/types.hpp>
30#include <mos_stdlib.hpp>
31#include <mos_string.hpp>
32#include <sys/poll.h>
33
34#define DEFINE_SYSCALL(ret, name) \
35 MOS_STATIC_ASSERT(SYSCALL_DEFINED(name)); \
36 ret define_syscall(name)
37
38// taken from mlibc
39constexpr static int days_from_civil(int y, unsigned m, unsigned d) noexcept
40{
41 y -= m <= 2;
42 const int era = (y >= 0 ? y : y - 399) / 400;
43 const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
44 const unsigned doy = (153 * (m > 2 ? m - 3 : m + 9) + 2) / 5 + d - 1; // [0, 365]
45 const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
46 return era * 146097 + static_cast<int>(doe) - 719468;
47}
48
49DEFINE_SYSCALL(void, poweroff)(bool reboot, u32 magic)
50{
51#define POWEROFF_MAGIC MOS_FOURCC('G', 'B', 'y', 'e')
52 if (magic != POWEROFF_MAGIC)
53 {
54 mos_warn("poweroff syscall called with wrong magic number (0x%x)", magic);
55 return;
56 }
57
58 if (!reboot)
59 {
60 pr_info("Meow, see ya~ :3");
61 power_shutdown();
62 }
63 else
64 {
65 mos_warn("reboot is not implemented yet");
66 }
67}
68
69DEFINE_SYSCALL(fd_t, vfs_openat)(fd_t dirfd, const char *path, u64 flags)
70{
71 if (path == NULL)
72 return -1;
73
74 auto f = vfs_openat(fd: dirfd, path, flags: (open_flags) flags);
75 if (f.isErr())
76 return f.getErr();
77 return process_attach_ref_fd(current_process, file: f.get(), flags: FD_FLAGS_NONE);
78}
79
80DEFINE_SYSCALL(long, vfs_fstatat)(fd_t fd, const char *path, file_stat_t *stat_buf, u64 flags)
81{
82 return vfs_fstatat(fd, path, stat: stat_buf, flags: (fstatat_flags) flags);
83}
84
85DEFINE_SYSCALL(size_t, io_read)(fd_t fd, void *buf, size_t count)
86{
87 if (buf == NULL)
88 return -EFAULT;
89
90 IO *io = process_get_fd(current_process, fd);
91 if (!io)
92 return -EBADF;
93
94 return io->read(buf, count);
95}
96
97DEFINE_SYSCALL(size_t, io_write)(fd_t fd, const void *buf, size_t count)
98{
99 if (buf == NULL)
100 {
101 pr_warn("io_write called with invalid arguments (fd=%d, buf=%p, count=%zd)", fd, buf, count);
102 return -EFAULT;
103 }
104
105 IO *io = process_get_fd(current_process, fd);
106 if (!io)
107 {
108 pr_warn("io_write called with invalid fd %d", fd);
109 return -EBADF;
110 }
111
112 return io->write(buf, count);
113}
114
115DEFINE_SYSCALL(bool, io_close)(fd_t fd)
116{
117 process_detach_fd(current_process, fd);
118 return true;
119}
120
121DEFINE_SYSCALL([[noreturn]] void, exit)(u32 exit_code)
122{
123 // only use the lower 8 bits
124 exit_code &= 0xff;
125 process_exit(proc: std::move(current_process), exit_code, signal: 0);
126 __builtin_unreachable();
127}
128
129DEFINE_SYSCALL(void, yield_cpu)(void)
130{
131 spinlock_acquire(&current_thread->state_lock);
132 reschedule();
133}
134
135DEFINE_SYSCALL(pid_t, fork)(void)
136{
137 const auto parent = current_process;
138 const auto child = process_do_fork(process: parent);
139 if (unlikely(!child))
140 return -1;
141 return child->pid; // return 0 for child, pid for parent
142}
143
144DEFINE_SYSCALL(pid_t, get_pid)(void)
145{
146 return current_process->pid;
147}
148
149DEFINE_SYSCALL(pid_t, get_parent_pid)(void)
150{
151 return current_process->parent->pid;
152}
153
154DEFINE_SYSCALL(pid_t, spawn)(const char *path, const char *const argv[], const char *const envp[])
155{
156 const stdio_t stdio = current_stdio();
157
158 mos::vector<mos::string> argv_vec;
159 for (size_t i = 0; argv[i] != NULL; i++)
160 argv_vec.push_back(value: argv[i]);
161
162 mos::vector<mos::string> envp_vec;
163 for (size_t i = 0; envp[i] != NULL; i++)
164 envp_vec.push_back(value: envp[i]);
165
166 const auto process = elf_create_process(path, current_process, argv: argv_vec, envp: envp_vec, ios: &stdio);
167
168 if (!process)
169 return -1;
170
171 return process->pid;
172}
173
174DEFINE_SYSCALL(tid_t, create_thread)(const char *name, thread_entry_t entry, void *arg, size_t stack_size, void *stack)
175{
176 const auto opt_thread = thread_new(current_process, mode: THREAD_MODE_USER, name, stack_size, stack);
177 if (opt_thread.isErr())
178 return -1;
179
180 const auto thread = opt_thread.get();
181
182 platform_context_setup_child_thread(thread, entry, arg);
183 thread_complete_init(thread);
184 scheduler_add_thread(thread);
185 return thread->tid;
186}
187
188DEFINE_SYSCALL(tid_t, get_tid)(void)
189{
190 return current_thread->tid;
191}
192
193DEFINE_SYSCALL([[noreturn]] void, thread_exit)(void)
194{
195 thread_exit(t: std::move(current_thread));
196 __builtin_unreachable();
197}
198
199DEFINE_SYSCALL(bool, wait_for_thread)(tid_t tid)
200{
201 return thread_wait_for_tid(tid);
202}
203
204DEFINE_SYSCALL(bool, futex_wait)(futex_word_t *futex, u32 val)
205{
206 return futex_wait(futex, expected: val);
207}
208
209DEFINE_SYSCALL(bool, futex_wake)(futex_word_t *futex, size_t count)
210{
211 return futex_wake(lock: futex, num_to_wake: count);
212}
213
214DEFINE_SYSCALL(fd_t, ipc_create)(const char *name, size_t max_pending_connections)
215{
216 auto io = ipc_create(name, max_pending_connections);
217 if (io.isErr())
218 return io.getErr();
219 return process_attach_ref_fd(current_process, file: io.get(), flags: FD_FLAGS_NONE);
220}
221
222DEFINE_SYSCALL(fd_t, ipc_accept)(fd_t listen_fd)
223{
224 IO *server = process_get_fd(current_process, fd: listen_fd);
225 if (server == NULL)
226 return -1;
227
228 auto client_io = ipc_accept(server);
229 if (client_io.isErr())
230 return client_io.getErr();
231
232 return process_attach_ref_fd(current_process, file: client_io.get(), flags: FD_FLAGS_NONE);
233}
234
235DEFINE_SYSCALL(fd_t, ipc_connect)(const char *server, size_t buffer_size)
236{
237 auto io = ipc_connect(name: server, buffer_size);
238 if (io.isErr())
239 return io.getErr();
240 return process_attach_ref_fd(current_process, file: io.get(), flags: FD_FLAGS_NONE);
241}
242
243DEFINE_SYSCALL(u64, arch_syscall)(u64 syscall, u64 arg1, u64 arg2, u64 arg3, u64 arg4)
244{
245 return platform_arch_syscall(syscall, arg1, arg2, arg3, arg4);
246}
247
248DEFINE_SYSCALL(long, vfs_mount)(const char *device, const char *mountpoint, const char *fs_type, const char *options)
249{
250 return vfs_mount(device, path: mountpoint, fs: fs_type, options).getErr();
251}
252
253DEFINE_SYSCALL(ssize_t, vfs_readlinkat)(fd_t dirfd, const char *path, char *buf, size_t buflen)
254{
255 return vfs_readlinkat(dirfd, path, buf, size: buflen);
256}
257
258DEFINE_SYSCALL(long, vfs_unlinkat)(fd_t dirfd, const char *path)
259{
260 return vfs_unlinkat(dirfd, path);
261}
262
263DEFINE_SYSCALL(long, vfs_symlink)(const char *target, const char *linkpath)
264{
265 return vfs_symlink(path: target, target: linkpath);
266}
267
268DEFINE_SYSCALL(long, vfs_mkdir)(const char *path)
269{
270 return vfs_mkdir(path).getErr();
271}
272
273DEFINE_SYSCALL(size_t, vfs_list_dir)(fd_t fd, char *buffer, size_t buffer_size)
274{
275 IO *io = process_get_fd(current_process, fd);
276 if (io == NULL)
277 return false;
278 return vfs_list_dir(io, buf: buffer, size: buffer_size);
279}
280
281DEFINE_SYSCALL(long, fd_manipulate)(fd_t fd, u64 op, void *arg)
282{
283 fd_type *fdt = &current_process->files[fd];
284 if (fdt->io == NULL)
285 return -EBADF;
286
287 switch (op)
288 {
289 case F_DUPFD:
290 {
291 fd_t fd2 = process_attach_ref_fd(current_process, file: fdt->io, flags: fdt->flags);
292 return fd2;
293 }
294 case F_DUPFD_CLOEXEC:
295 {
296 fd_t fd2 = process_attach_ref_fd(current_process, file: fdt->io, flags: fdt->flags | FD_FLAGS_CLOEXEC);
297 return fd2;
298 }
299 case F_GETFD:
300 {
301 return fdt->flags;
302 }
303 case F_SETFD:
304 {
305 // test if arg is a valid flag
306 const FDFlags flags = (FDFlag) (u64) arg;
307 if (flags.test_inverse(b: FD_FLAGS_CLOEXEC))
308 return -EINVAL;
309 fdt->flags = flags;
310 return 0;
311 }
312 case F_GETFL:
313 case F_SETFL:
314 {
315 return -ENOSYS; // not implemented
316 }
317 case F_GETLK:
318 case F_SETLK:
319 case F_SETLKW:
320 {
321 return -ENOSYS; // not implemented
322 }
323 case F_GETOWN:
324 case F_SETOWN:
325 case F_GETOWN_EX:
326 case F_SETOWN_EX:
327 case F_GETSIG:
328 case F_SETSIG:
329 case F_GETOWNER_UIDS:
330 case F_ADD_SEALS:
331 case F_GET_SEALS:
332 {
333 return -ENOSYS; // not implemented
334 }
335 }
336
337 return -EINVAL;
338}
339
340DEFINE_SYSCALL(void *, mmap_anonymous)(ptr_t hint_addr, size_t size, mem_perm_t perm, u64 flags)
341{
342 const VMFlags vmflags = VM_USER | (VMFlag) perm; // VMFlags shares the same values as mem_perm_t
343 const size_t n_pages = ALIGN_UP_TO_PAGE(size) / MOS_PAGE_SIZE;
344
345 ptr_t result = mmap_anonymous(current_mm, hint_addr, flags: (mmap_flags_t) flags, VMFlags: vmflags, n_pages);
346 return (void *) result;
347}
348
349DEFINE_SYSCALL(void *, mmap_file)(ptr_t hint_addr, size_t size, mem_perm_t perm, u64 mmap_flags, fd_t fd, off_t offset)
350{
351 const VMFlags vmflags = VM_USER | (VMFlag) perm; // VMFlags shares the same values as mem_perm_t
352 const size_t n_pages = ALIGN_UP_TO_PAGE(size) / MOS_PAGE_SIZE;
353
354 IO *io = process_get_fd(current_process, fd);
355 if (io == NULL)
356 return NULL;
357
358 ptr_t result = mmap_file(current_mm, hint_addr, flags: (mmap_flags_t) mmap_flags, VMFlags: vmflags, n_pages, io, offset);
359 return (void *) result;
360}
361
362DEFINE_SYSCALL(pid_t, wait_for_process)(pid_t pid, u32 *exit_code, u64 flags)
363{
364 return process_wait_for_pid(pid, exit_code, flags);
365}
366
367DEFINE_SYSCALL(bool, munmap)(void *addr, size_t size)
368{
369 return munmap(addr: (ptr_t) addr, size);
370}
371
372DEFINE_SYSCALL(long, vfs_chdirat)(fd_t dirfd, const char *path)
373{
374 return vfs_chdirat(dirfd, path);
375}
376
377DEFINE_SYSCALL(ssize_t, vfs_getcwd)(char *buf, size_t size)
378{
379 return vfs_getcwd(buf, size);
380}
381
382DEFINE_SYSCALL(off_t, io_seek)(fd_t fd, off_t offset, io_seek_whence_t whence)
383{
384 IO *io = process_get_fd(current_process, fd);
385 if (io == NULL)
386 return -1;
387 return io->seek(offset, whence);
388}
389
390DEFINE_SYSCALL(off_t, io_tell)(fd_t fd)
391{
392 IO *io = process_get_fd(current_process, fd);
393 if (io == NULL)
394 return -1;
395 return io->tell();
396}
397
398DEFINE_SYSCALL(bool, signal_register)(signal_t sig, const sigaction_t *action)
399{
400 return process_register_signal_handler(current_process, sig, sigaction: action);
401}
402
403DEFINE_SYSCALL(long, signal_process)(pid_t pid, signal_t sig)
404{
405 const auto process = process_get(pid);
406 if (!process)
407 return -ESRCH;
408 return signal_send_to_process(target: *process, signal: sig);
409}
410
411DEFINE_SYSCALL(long, signal_thread)(tid_t tid, signal_t sig)
412{
413 Thread *thread = thread_get(id: tid);
414 if (!thread)
415 return -ESRCH;
416 return signal_send_to_thread(target: thread, signal: sig);
417}
418
419DEFINE_SYSCALL([[noreturn]] void, signal_return)(void *sp)
420{
421 platform_restore_from_signal_handler(sp);
422}
423
424DEFINE_SYSCALL(bool, vm_protect)(void *addr, size_t size, mem_perm_t perm)
425{
426 return vm_protect(current_mm, addr: (ptr_t) addr, size, perm: (VMFlag) perm);
427}
428
429DEFINE_SYSCALL(int, io_poll)(struct pollfd *fds, nfds_t nfds, int timeout)
430{
431 if (timeout == 0) // poll with timeout 0 is just a check
432 return 0;
433
434 if (!fds || nfds == 0)
435 return -1;
436
437 for (nfds_t i = 0; i < nfds; i++)
438 {
439 if (fds[i].fd < 0)
440 fds[i].revents = 0;
441 pr_info2("io_poll: fd=%d, events=%d", fds[i].fd, fds[i].events);
442 }
443
444 pr_emerg("io_poll is not implemented yet\n");
445 signal_send_to_thread(current_thread, SIGKILL); // unimplemented
446 return 0;
447}
448
449#ifndef FD_CLR
450#define FD_CLR(__fd, __set) (__set->fds_bits[__fd / 8] &= ~(1 << (__fd % 8)))
451#endif
452
453#ifndef FD_ISSET
454#define FD_ISSET(__fd, __set) (__set->fds_bits[__fd / 8] & (1 << (__fd % 8)))
455#endif
456
457#ifndef FD_SET
458#define FD_SET(__fd, __set) (__set->fds_bits[__fd / 8] |= 1 << (__fd % 8))
459#endif
460
461#ifndef FD_ZERO
462#define FD_ZERO(__set) memset(__set->fds_bits, 0, sizeof(fd_set))
463#endif
464
465DEFINE_SYSCALL(int, io_pselect)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)
466{
467 MOS_UNUSED(timeout);
468 MOS_UNUSED(sigmask);
469
470 for (int i = 0; i < nfds; i++)
471 {
472 if (readfds && FD_ISSET(i, readfds))
473 {
474 // pr_info2("io_pselect: fd=%d, read", i);
475 }
476 if (writefds && FD_ISSET(i, writefds))
477 {
478 // pr_info2("io_pselect: fd=%d, write", i);
479 }
480 if (exceptfds && FD_ISSET(i, exceptfds))
481 {
482 // pr_info2("io_pselect: fd=%d, except", i);
483 }
484 }
485
486 return 1; // stub
487}
488
489DEFINE_SYSCALL(long, execveat)(fd_t dirfd, const char *path, const char *const argv[], const char *const envp[], u64 flags)
490{
491 return process_do_execveat(dirfd, path, argv, envp, flags);
492}
493
494DEFINE_SYSCALL(long, clock_msleep)(u64 ms)
495{
496 timer_msleep(ms);
497 return 0;
498}
499
500DEFINE_SYSCALL(fd_t, io_dup)(fd_t fd)
501{
502 IO *io = process_get_fd(current_process, fd);
503 if (io == NULL)
504 return -EBADF; // fd is not a valid file descriptor
505 return process_attach_ref_fd(current_process, file: io->ref(), current_process->files[fd].flags);
506}
507
508DEFINE_SYSCALL(fd_t, io_dup2)(fd_t oldfd, fd_t newfd)
509{
510 fd_type *old = &current_process->files[oldfd];
511 if (old->io == NULL)
512 return -EBADF; // oldfd is not a valid file descriptor
513
514 if (oldfd == newfd)
515 return newfd;
516
517 process_detach_fd(current_process, fd: newfd);
518
519 current_process->files[newfd].io = old->io->ref();
520 current_process->files[newfd].flags = old->flags;
521 return newfd;
522}
523
524DEFINE_SYSCALL(bool, dmabuf_alloc)(size_t n_pages, ptr_t *phys, ptr_t *virt)
525{
526 pfn_t pfn = dmabuf_allocate(n_pages, pages: virt);
527 *phys = pfn * MOS_PAGE_SIZE;
528 return !IS_ERR_VALUE(pfn);
529}
530
531DEFINE_SYSCALL(bool, dmabuf_free)(ptr_t vaddr, ptr_t paddr)
532{
533 return dmabuf_free(vaddr, paddr);
534}
535
536DEFINE_SYSCALL(bool, dmabuf_share)(void *buffer, size_t size, ptr_t *phyaddr)
537{
538 pfn_t pfn = dmabuf_share(buffer, size);
539
540 if (IS_ERR_VALUE(pfn))
541 return false;
542
543 *phyaddr = pfn * MOS_PAGE_SIZE;
544 return true;
545}
546
547DEFINE_SYSCALL(bool, dmabuf_unshare)(ptr_t phys, size_t size, void *buf)
548{
549 return dmabuf_unshare(phys, size, virt: buf);
550}
551
552DEFINE_SYSCALL(long, pipe)(fd_t *reader, fd_t *writer, u64 flags)
553{
554 auto pipe = pipe_create(MOS_PAGE_SIZE * 4);
555 if (pipe.isErr())
556 return pipe.getErr();
557
558 auto pipeio = pipeio_create(pipe: pipe.get());
559 *reader = process_attach_ref_fd(current_process, file: &pipeio->io_r, flags: (FDFlag) flags);
560 *writer = process_attach_ref_fd(current_process, file: &pipeio->io_w, flags: (FDFlag) flags);
561 return 0;
562}
563
564DEFINE_SYSCALL(ssize_t, io_readv)(fd_t fd, const struct iovec *iov, int iovcnt)
565{
566 if (fd < 0)
567 return -EBADF;
568
569 if (iov == NULL)
570 return -EFAULT;
571
572 IO *io = process_get_fd(current_process, fd);
573 if (!io)
574 return -EBADF;
575
576 for (int i = 0; i < iovcnt; i++)
577 {
578 if (iov[i].iov_base == NULL)
579 return -EFAULT;
580 }
581
582 ssize_t bytes_read = 0;
583
584 for (int i = 0; i < iovcnt; i++)
585 {
586 size_t ret = io->read(buf: iov[i].iov_base, count: iov[i].iov_len);
587 if (IS_ERR_VALUE(ret))
588 return ret;
589
590 bytes_read += ret;
591
592 if (ret != iov[i].iov_len)
593 break; // short read, leave
594 }
595
596 return bytes_read;
597}
598
599DEFINE_SYSCALL(long, vfs_unmount)(const char *path)
600{
601 return vfs_unmount(path);
602}
603
604DEFINE_SYSCALL(long, clock_gettimeofday)(struct timespec *ts)
605{
606#ifdef __x86_64__
607 timeval_t tv;
608 platform_get_time(val: &tv);
609 if (tv.day == 0)
610 return -ENOTSUP;
611
612 const auto days = days_from_civil(y: tv.year, m: tv.month, d: tv.day);
613 ts->tv_sec = (days * 86400) + (tv.hour * 60 * 60) + (tv.minute * 60) + tv.second;
614 ts->tv_nsec = 0;
615#elifdef __riscv
616 constexpr auto NSEC_PER_SEC = 1'000'000'000;
617 u64 timestamp = 0;
618 platform_get_unix_timestamp(&timestamp);
619 ts->tv_sec = timestamp / NSEC_PER_SEC;
620 ts->tv_nsec = timestamp % NSEC_PER_SEC;
621#endif
622 return 0;
623}
624
625DEFINE_SYSCALL(long, thread_setname)(tid_t tid, const char *name)
626{
627 Thread *thread = thread_get(id: tid);
628 if (!thread)
629 return -ESRCH;
630
631 thread->name = name;
632 return true;
633}
634
635DEFINE_SYSCALL(ssize_t, thread_getname)(tid_t tid, char *buf, size_t buflen)
636{
637 Thread *thread = thread_get(id: tid);
638
639 if (!thread)
640 return -ESRCH;
641
642 char *end = strncpy(dest: buf, src: thread->name.data(), n: buflen);
643 return end - buf;
644}
645
646DEFINE_SYSCALL(long, vfs_fchmodat)(fd_t dirfd, const char *path, int mode, u64 flags)
647{
648 return vfs_fchmodat(fd: dirfd, path, perm: mode, flags);
649}
650
651DEFINE_SYSCALL(long, io_pread)(fd_t fd, void *buf, size_t count, off_t offset)
652{
653 if (fd < 0)
654 return -EBADF;
655
656 if (buf == NULL)
657 return -EFAULT;
658
659 IO *io = process_get_fd(current_process, fd);
660 if (!io)
661 return -EBADF;
662
663 return io->pread(buf, count, offset);
664}
665
666DEFINE_SYSCALL(fd_t, memfd_create)(const char *name, u64 flags)
667{
668 auto io = memfd_create(name);
669 if (io.isErr())
670 return io.getErr();
671
672 return process_attach_ref_fd(current_process, file: io.get(), flags: (FDFlag) flags);
673}
674
675DEFINE_SYSCALL(long, signal_mask_op)(int how, const sigset_t *set, sigset_t *oldset)
676{
677 if (oldset)
678 *oldset = current_thread->signal_info.mask;
679
680 if (set)
681 {
682 switch (how)
683 {
684 case SIG_SETMASK: current_thread->signal_info.mask = *set; break;
685 case SIG_BLOCK:
686 {
687 char *ptr = (char *) set;
688 char *mask = (char *) &current_thread->signal_info.mask;
689 for (size_t i = 0; i < sizeof(sigset_t); i++)
690 mask[i] |= ptr[i];
691 break;
692 }
693 case SIG_UNBLOCK:
694 {
695 char *ptr = (char *) set;
696 char *mask = (char *) &current_thread->signal_info.mask;
697 for (size_t i = 0; i < sizeof(sigset_t); i++)
698 mask[i] &= ~ptr[i];
699 break;
700 }
701 default: return -EINVAL;
702 }
703 }
704
705 return 0;
706}
707
708DEFINE_SYSCALL(long, vfs_fsync)(fd_t fd, bool data_only)
709{
710 IO *io = process_get_fd(current_process, fd);
711 if (!io)
712 return -EBADF;
713
714 if (io->io_type != IO_FILE)
715 return -EBADF;
716
717 return vfs_fsync(io, sync_metadata: data_only, start: 0, end: (off_t) -1);
718}
719
720DEFINE_SYSCALL(long, vfs_rmdir)(const char *path)
721{
722 return vfs_rmdir(path).getErr();
723}
724