1use core::ffi::c_char;
4
5use alloc::{string::ToString, sync::Arc};
6
7use crate::{
8 arch::trap::SumGuard,
9 kernel::{
10 current_cpu, current_task,
11 syscall::util::{
12 create_file_at, create_file_from_dentry, get_path_safe, resolve_at_path,
13 resolve_at_path_with_flags,
14 },
15 },
16 uapi::{
17 errno::{EACCES, EINVAL, ENOENT},
18 fs::{AtFlags, F_OK, FileSystemType, LinuxStatFs, R_OK, W_OK, X_OK},
19 time::TimeSpec,
20 },
21 vfs::{
22 DENTRY_CACHE, Dentry, FileMode, FsError, InodeType, OpenFlags, RegFile, SeekWhence, Stat,
23 split_path, vfs_lookup,
24 },
25};
26
27pub const AT_FDCWD: i32 = -100;
28pub const AT_SYMLINK_NOFOLLOW: u32 = 0x100;
29pub const AT_REMOVEDIR: u32 = 0x200;
30pub const O_CLOEXEC: u32 = 0o2000000;
31
32pub fn close(fd: usize) -> isize {
33 let task = current_cpu().lock().current_task.as_ref().unwrap().clone();
34 match task.lock().fd_table.close(fd) {
35 Ok(()) => 0,
36 Err(e) => e.to_errno(),
37 }
38}
39
40pub fn lseek(fd: usize, offset: isize, whence: usize) -> isize {
41 let task = current_cpu().lock().current_task.as_ref().unwrap().clone();
43 let file = match task.lock().fd_table.get(fd) {
44 Ok(f) => f,
45 Err(e) => return e.to_errno(),
46 };
47
48 let seek_whence = match SeekWhence::from_usize(whence) {
50 Some(w) => w,
51 None => return FsError::InvalidArgument.to_errno(),
52 };
53
54 match file.lseek(offset, seek_whence) {
56 Ok(new_pos) => new_pos as isize,
57 Err(e) => e.to_errno(),
58 }
59}
60
61pub fn openat(dirfd: i32, pathname: *const c_char, flags: u32, mode: u32) -> isize {
63 let _guard = SumGuard::new();
65 let path_str = match get_path_safe(pathname) {
66 Ok(s) => s.to_string(),
67 Err(_) => {
68 return FsError::InvalidArgument.to_errno();
69 }
70 };
71
72 let open_flags = match OpenFlags::from_bits(flags) {
74 Some(f) => f,
75 None => {
76 return FsError::InvalidArgument.to_errno();
77 }
78 };
79
80 let dentry = match resolve_at_path(dirfd, &path_str) {
84 Ok(Some(d)) => {
85 if open_flags.contains(OpenFlags::O_CREAT) && open_flags.contains(OpenFlags::O_EXCL) {
88 return FsError::AlreadyExists.to_errno();
89 }
90 d
91 }
92 Ok(None) => {
93 if !open_flags.contains(OpenFlags::O_CREAT) {
95 return FsError::NotFound.to_errno();
96 }
97
98 match create_file_at(dirfd, &path_str, mode) {
100 Ok(d) => d,
101 Err(e) => return e.to_errno(),
102 }
103 }
104 Err(e) => return e.to_errno(),
105 };
106
107 let meta = match dentry.inode.metadata() {
109 Ok(m) => m,
110 Err(e) => return e.to_errno(),
111 };
112
113 if open_flags.contains(OpenFlags::O_DIRECTORY) {
115 if meta.inode_type != InodeType::Directory {
116 return FsError::NotDirectory.to_errno();
117 }
118 }
119
120 if open_flags.contains(OpenFlags::O_TRUNC) && open_flags.writable() {
122 if meta.inode_type == InodeType::File {
123 if let Err(e) = dentry.inode.truncate(0) {
124 return e.to_errno();
125 }
126 }
127 }
128
129 let file = match create_file_from_dentry(dentry, open_flags) {
131 Ok(f) => f,
132 Err(e) => return e.to_errno(),
133 };
134
135 let task = current_cpu().lock().current_task.as_ref().unwrap().clone();
137 match task.lock().fd_table.alloc(file) {
138 Ok(fd) => fd as isize,
139 Err(e) => e.to_errno(),
140 }
141}
142
143pub fn mkdirat(dirfd: i32, pathname: *const c_char, mode: u32) -> isize {
144 let _guard = SumGuard::new();
146 let path_str = match get_path_safe(pathname) {
147 Ok(s) => s.to_string(),
148 Err(_) => {
149 return FsError::InvalidArgument.to_errno();
150 }
151 };
152
153 let (dir_path, dirname) = match split_path(&path_str) {
155 Ok(p) => p,
156 Err(e) => return e.to_errno(),
157 };
158
159 let parent_dentry = match resolve_at_path(dirfd, &dir_path) {
161 Ok(Some(d)) => d,
162 Ok(None) => return FsError::NotFound.to_errno(),
163 Err(e) => return e.to_errno(),
164 };
165
166 let dir_mode = FileMode::from_bits_truncate(mode) | FileMode::S_IFDIR;
168 match parent_dentry.inode.mkdir(&dirname, dir_mode) {
169 Ok(_) => 0,
170 Err(e) => e.to_errno(),
171 }
172}
173
174pub fn unlinkat(dirfd: i32, pathname: *const c_char, flags: u32) -> isize {
175 let _guard = SumGuard::new();
177 let path_str = match get_path_safe(pathname) {
178 Ok(s) => s.to_string(),
179 Err(_) => {
180 return FsError::InvalidArgument.to_errno();
181 }
182 };
183
184 let is_rmdir = (flags & AT_REMOVEDIR) != 0;
185
186 let (dir_path, filename) = match split_path(&path_str) {
188 Ok(p) => p,
189 Err(e) => return e.to_errno(),
190 };
191
192 let parent_dentry = match resolve_at_path(dirfd, &dir_path) {
194 Ok(Some(d)) => d,
195 Ok(None) => return FsError::NotFound.to_errno(),
196 Err(e) => return e.to_errno(),
197 };
198
199 let target_inode = match parent_dentry.inode.lookup(&filename) {
201 Ok(i) => i,
202 Err(e) => return e.to_errno(),
203 };
204
205 let meta = match target_inode.metadata() {
206 Ok(m) => m,
207 Err(e) => return e.to_errno(),
208 };
209
210 if is_rmdir {
212 if meta.inode_type != InodeType::Directory {
214 return FsError::NotDirectory.to_errno();
215 }
216 } else {
217 if meta.inode_type == InodeType::Directory {
219 return FsError::IsDirectory.to_errno();
220 }
221 }
222
223 match parent_dentry.inode.unlink(&filename) {
225 Ok(()) => {
226 parent_dentry.remove_child(&filename);
228 0
229 }
230 Err(e) => e.to_errno(),
231 }
232}
233
234pub fn chdir(path: *const c_char) -> isize {
235 let _guard = SumGuard::new();
237 let path_str = match get_path_safe(path) {
238 Ok(s) => s.to_string(),
239 Err(_) => {
240 return FsError::InvalidArgument.to_errno();
241 }
242 };
243
244 let dentry = match vfs_lookup(&path_str) {
246 Ok(d) => d,
247 Err(e) => return e.to_errno(),
248 };
249
250 let meta = match dentry.inode.metadata() {
252 Ok(m) => m,
253 Err(e) => return e.to_errno(),
254 };
255
256 if meta.inode_type != InodeType::Directory {
257 return FsError::NotDirectory.to_errno();
258 }
259
260 current_task().lock().fs.lock().cwd = Some(dentry);
262 0
263}
264
265pub fn getcwd(buf: *mut u8, size: usize) -> isize {
266 let cwd_dentry = match current_task().lock().fs.lock().cwd.clone() {
268 Some(d) => d,
269 None => return FsError::NotSupported.to_errno(),
270 };
271
272 let path = cwd_dentry.full_path();
274 let path_bytes = path.as_bytes();
275
276 if path_bytes.len() + 1 > size {
278 return FsError::InvalidArgument.to_errno();
279 }
280
281 {
283 let _guard = SumGuard::new();
284 unsafe {
285 core::ptr::copy_nonoverlapping(path_bytes.as_ptr(), buf, path_bytes.len());
286 *buf.add(path_bytes.len()) = 0; }
288 }
289
290 buf as isize
291}
292
293pub fn fstat(fd: usize, statbuf: *mut Stat) -> isize {
294 if statbuf.is_null() {
296 return FsError::InvalidArgument.to_errno();
297 }
298
299 let task = current_cpu().lock().current_task.as_ref().unwrap().clone();
301 let file = match task.lock().fd_table.get(fd) {
302 Ok(f) => f,
303 Err(e) => return e.to_errno(),
304 };
305
306 let metadata = match file.metadata() {
308 Ok(m) => m,
309 Err(e) => return e.to_errno(),
310 };
311
312 let stat = crate::vfs::Stat::from_metadata(&metadata);
314
315 {
317 let _guard = SumGuard::new();
318 unsafe {
319 core::ptr::write(statbuf, stat);
320 }
321 }
322
323 0
324}
325
326pub fn getdents64(fd: usize, dirp: *mut u8, count: usize) -> isize {
327 use crate::vfs::{LinuxDirent64, inode_type_to_d_type};
328
329 if dirp.is_null() || count == 0 {
331 return FsError::InvalidArgument.to_errno();
332 }
333
334 let task = current_cpu().lock().current_task.as_ref().unwrap().clone();
336 let file = match task.lock().fd_table.get(fd) {
337 Ok(f) => f,
338 Err(e) => return e.to_errno(),
339 };
340
341 let inode = match file.inode() {
343 Ok(i) => i,
344 Err(e) => return e.to_errno(),
345 };
346
347 let entries = match inode.readdir() {
351 Ok(e) => e,
352 Err(e) => return e.to_errno(),
353 };
354
355 let start_index = match file.lseek(0, SeekWhence::Cur) {
358 Ok(pos) => pos,
359 Err(e) => return e.to_errno(),
360 };
361
362 let mut written = 0usize;
364 let mut items_written = 0usize;
365
366 unsafe {
367 let _guard = SumGuard::new();
368
369 for (_, entry) in entries.iter().skip(start_index).enumerate() {
370 let dirent_len = LinuxDirent64::total_len(&entry.name);
372
373 if written + dirent_len > count {
375 break;
376 }
377
378 let current_off = (start_index + items_written + 1) as i64;
380
381 let dirent_ptr = dirp.add(written) as *mut LinuxDirent64;
383 core::ptr::write(
384 dirent_ptr,
385 LinuxDirent64 {
386 d_ino: entry.inode_no as u64,
387 d_off: current_off,
388 d_reclen: dirent_len as u16,
389 d_type: inode_type_to_d_type(entry.inode_type),
390 },
391 );
392
393 let name_ptr = dirp.add(written + 19);
398 let name_bytes = entry.name.as_bytes();
399 core::ptr::copy_nonoverlapping(name_bytes.as_ptr(), name_ptr, name_bytes.len());
400 core::ptr::write(name_ptr.add(name_bytes.len()), 0);
402
403 written += dirent_len;
404 items_written += 1;
405 }
406 }
407
408 if items_written > 0 {
410 if let Err(e) = file.lseek((start_index + items_written) as isize, SeekWhence::Set) {
412 crate::pr_warn!(
413 "[getdents64] failed to update file offset for fd {}: {:?}",
414 fd,
415 e
416 );
417 }
420 }
421
422 written as isize
424}
425
426pub fn statfs(path: *const c_char, buf: *mut LinuxStatFs) -> isize {
427 if buf.is_null() {
429 return -(EINVAL as isize);
430 }
431
432 let _guard = SumGuard::new();
434 let path_str = match get_path_safe(path) {
435 Ok(s) => s.to_string(),
436 Err(_) => {
437 return -(EINVAL as isize);
438 }
439 };
440
441 if vfs_lookup(&path_str).is_err() {
443 return -(EINVAL as isize);
444 }
445
446 use crate::vfs::MOUNT_TABLE;
448 let mount_point = match MOUNT_TABLE.find_mount(&path_str) {
449 Some(mp) => mp,
450 None => return -(EINVAL as isize),
451 };
452
453 let fs_stat = match mount_point.fs.statfs() {
455 Ok(s) => s,
456 Err(e) => return e.to_errno(),
457 };
458
459 let fs_type = FileSystemType::from_str(mount_point.fs.fs_type());
461
462 let statfs_buf = LinuxStatFs {
463 f_type: fs_type.magic(),
464 f_bsize: fs_stat.block_size as i64,
465 f_blocks: fs_stat.total_blocks as u64,
466 f_bfree: fs_stat.free_blocks as u64,
467 f_bavail: fs_stat.available_blocks as u64,
468 f_files: fs_stat.total_inodes as u64,
469 f_ffree: fs_stat.free_inodes as u64,
470 f_fsid: [
471 (fs_stat.fsid & 0xFFFFFFFF) as i32,
472 (fs_stat.fsid >> 32) as i32,
473 ],
474 f_namelen: fs_stat.max_filename_len as i64,
475 f_frsize: fs_stat.block_size as i64, f_flags: 0, f_spare: [0; 4],
478 };
479
480 {
482 let _guard = SumGuard::new();
483 unsafe {
484 core::ptr::write(buf, statfs_buf);
485 }
486 }
487
488 0
489}
490
491pub fn faccessat(dirfd: i32, pathname: *const c_char, mode: i32, flags: u32) -> isize {
492 let _guard = SumGuard::new();
494 let path_str = match get_path_safe(pathname) {
495 Ok(s) => s.to_string(),
496 Err(_) => {
497 return -(EINVAL as isize);
498 }
499 };
500
501 let at_flags = match AtFlags::from_bits(flags) {
503 Some(f) => f,
504 None => return -(EINVAL as isize),
505 };
506
507 let follow_symlink = !at_flags.contains(AtFlags::SYMLINK_NOFOLLOW);
509 let dentry = match resolve_at_path_with_flags(dirfd, &path_str, follow_symlink) {
510 Ok(d) => d,
511 Err(e) => return e.to_errno(),
512 };
513
514 let meta = match dentry.inode.metadata() {
516 Ok(m) => m,
517 Err(e) => return e.to_errno(),
518 };
519
520 if mode == F_OK {
522 return 0;
523 }
524
525 if (mode & R_OK) != 0 {
536 if !meta.mode.contains(FileMode::S_IRUSR)
538 && !meta.mode.contains(FileMode::S_IRGRP)
539 && !meta.mode.contains(FileMode::S_IROTH)
540 {
541 return -(EACCES as isize);
542 }
543 }
544
545 if (mode & W_OK) != 0 {
546 if !meta.mode.contains(FileMode::S_IWUSR)
548 && !meta.mode.contains(FileMode::S_IWGRP)
549 && !meta.mode.contains(FileMode::S_IWOTH)
550 {
551 return -(EACCES as isize);
552 }
553 }
554
555 if (mode & X_OK) != 0 {
556 if !meta.mode.contains(FileMode::S_IXUSR)
558 && !meta.mode.contains(FileMode::S_IXGRP)
559 && !meta.mode.contains(FileMode::S_IXOTH)
560 {
561 return -(EACCES as isize);
562 }
563 }
564
565 0
566}
567
568pub fn readlinkat(dirfd: i32, pathname: *const c_char, buf: *mut u8, bufsiz: usize) -> isize {
569 if buf.is_null() || bufsiz == 0 {
571 return -(EINVAL as isize);
572 }
573
574 let _guard = SumGuard::new();
576 let path_str = match get_path_safe(pathname) {
577 Ok(s) => s.to_string(),
578 Err(_) => {
579 return -(EINVAL as isize);
580 }
581 };
582
583 let dentry = match resolve_at_path_with_flags(dirfd, &path_str, false) {
585 Ok(d) => d,
586 Err(e) => return e.to_errno(),
587 };
588
589 let meta = match dentry.inode.metadata() {
591 Ok(m) => m,
592 Err(e) => return e.to_errno(),
593 };
594
595 if meta.inode_type != InodeType::Symlink {
596 return -(EINVAL as isize);
597 }
598
599 let read_size = core::cmp::min(meta.size, bufsiz);
601 let mut temp_buf = alloc::vec![0u8; read_size];
602
603 let bytes_read = match dentry.inode.read_at(0, &mut temp_buf) {
604 Ok(n) => n,
605 Err(e) => return e.to_errno(),
606 };
607
608 {
610 let _guard = SumGuard::new();
611 unsafe {
612 core::ptr::copy_nonoverlapping(temp_buf.as_ptr(), buf, bytes_read);
613 }
614 }
615
616 bytes_read as isize
617}
618
619pub fn newfstatat(dirfd: i32, pathname: *const c_char, statbuf: *mut Stat, flags: u32) -> isize {
620 if statbuf.is_null() {
622 return -(EINVAL as isize);
623 }
624
625 let _guard = SumGuard::new();
627 let path_str = match get_path_safe(pathname) {
628 Ok(s) => s.to_string(),
629 Err(_) => {
630 return -(EINVAL as isize);
631 }
632 };
633
634 let at_flags = match AtFlags::from_bits(flags) {
636 Some(f) => f,
637 None => return -(EINVAL as isize),
638 };
639
640 if path_str.is_empty() && at_flags.contains(AtFlags::EMPTY_PATH) {
642 if dirfd == AT_FDCWD {
643 return -(EINVAL as isize);
644 }
645 return fstat(dirfd as usize, statbuf);
647 }
648
649 let follow_symlink = !at_flags.contains(AtFlags::SYMLINK_NOFOLLOW);
651 let dentry = match resolve_at_path_with_flags(dirfd, &path_str, follow_symlink) {
652 Ok(d) => d,
653 Err(e) => return e.to_errno(),
654 };
655
656 let metadata = match dentry.inode.metadata() {
658 Ok(m) => m,
659 Err(e) => return e.to_errno(),
660 };
661
662 let stat = Stat::from_metadata(&metadata);
664
665 {
667 let _guard = SumGuard::new();
668 unsafe {
669 core::ptr::write(statbuf, stat);
670 }
671 }
672
673 0
674}
675
676pub fn utimensat(dirfd: i32, pathname: *const c_char, times: *const TimeSpec, flags: u32) -> isize {
677 let _guard = SumGuard::new();
679 let path_str = match get_path_safe(pathname) {
680 Ok(s) => s.to_string(),
681 Err(_) => {
682 return -(EINVAL as isize);
683 }
684 };
685
686 let at_flags = match AtFlags::from_bits(flags) {
688 Some(f) => f,
689 None => return -(EINVAL as isize),
690 };
691
692 let follow_symlink = !at_flags.contains(AtFlags::SYMLINK_NOFOLLOW);
694 let dentry = match resolve_at_path_with_flags(dirfd, &path_str, follow_symlink) {
695 Ok(d) => d,
696 Err(e) => return e.to_errno(),
697 };
698
699 let (atime_opt, mtime_opt) = if times.is_null() {
701 let now = TimeSpec::now();
703 (Some(now), Some(now))
704 } else {
705 unsafe {
706 let _guard = SumGuard::new();
707 let user_times = core::slice::from_raw_parts(times, 2);
708
709 if let Err(e) = user_times[0].validate() {
711 return -(e as isize);
712 }
713 if let Err(e) = user_times[1].validate() {
714 return -(e as isize);
715 }
716
717 let atime_opt = if user_times[0].is_omit() {
719 None } else if user_times[0].is_now() {
721 Some(TimeSpec::now())
722 } else {
723 Some(user_times[0])
724 };
725
726 let mtime_opt = if user_times[1].is_omit() {
728 None
729 } else if user_times[1].is_now() {
730 Some(TimeSpec::now())
731 } else {
732 Some(user_times[1])
733 };
734
735 (atime_opt, mtime_opt)
736 }
737 };
738
739 if let Err(e) = dentry.inode.set_times(atime_opt, mtime_opt) {
741 return e.to_errno();
742 }
743
744 0
745}
746
747pub fn renameat2(
749 olddirfd: i32,
750 oldpath: *const c_char,
751 newdirfd: i32,
752 newpath: *const c_char,
753 flags: u32,
754) -> isize {
755 use crate::uapi::{
756 errno::{EEXIST, ENOTDIR},
757 fs::RenameFlags,
758 };
759
760 let rename_flags = match RenameFlags::from_bits(flags) {
762 Some(f) => f,
763 None => return -(EINVAL as isize),
764 };
765
766 if !rename_flags.is_valid() {
768 return -(EINVAL as isize);
769 }
770
771 let _guard = SumGuard::new();
773 let old_path_str = match get_path_safe(oldpath) {
774 Ok(s) => s.to_string(),
775 Err(_) => {
776 return -(EINVAL as isize);
777 }
778 };
779
780 let new_path_str = match get_path_safe(newpath) {
782 Ok(s) => s.to_string(),
783 Err(_) => {
784 return -(EINVAL as isize);
785 }
786 };
787
788 let (old_dir_path, old_name) = match split_path(&old_path_str) {
790 Ok(p) => p,
791 Err(e) => return e.to_errno(),
792 };
793
794 let (new_dir_path, new_name) = match split_path(&new_path_str) {
795 Ok(p) => p,
796 Err(e) => return e.to_errno(),
797 };
798
799 let old_parent = match resolve_at_path(olddirfd, &old_dir_path) {
801 Ok(Some(d)) => d,
802 Ok(None) => return -(ENOENT as isize),
803 Err(e) => return e.to_errno(),
804 };
805
806 let new_parent = match resolve_at_path(newdirfd, &new_dir_path) {
807 Ok(Some(d)) => d,
808 Ok(None) => return -(ENOENT as isize),
809 Err(e) => return e.to_errno(),
810 };
811
812 let old_parent_meta = match old_parent.inode.metadata() {
814 Ok(m) => m,
815 Err(e) => return e.to_errno(),
816 };
817 if old_parent_meta.inode_type != InodeType::Directory {
818 return -(ENOTDIR as isize);
819 }
820
821 let new_parent_meta = match new_parent.inode.metadata() {
822 Ok(m) => m,
823 Err(e) => return e.to_errno(),
824 };
825 if new_parent_meta.inode_type != InodeType::Directory {
826 return -(ENOTDIR as isize);
827 }
828
829 let _old_inode = match old_parent.inode.lookup(&old_name) {
831 Ok(inode) => inode,
832 Err(e) => return e.to_errno(),
833 };
834
835 if rename_flags.contains(RenameFlags::EXCHANGE) {
837 crate::pr_warn!(
854 "[renameat2] EXCHANGE is non-atomic: {} <-> {} (no transaction support)",
855 old_name,
856 new_name
857 );
858
859 let _new_inode = match new_parent.inode.lookup(&new_name) {
861 Ok(inode) => inode,
862 Err(e) => {
863 crate::pr_err!(
864 "[renameat2] EXCHANGE failed: target '{}' does not exist (error: {:?})",
865 new_name,
866 e
867 );
868 return -(ENOENT as isize); }
870 };
871
872 let temp_name = alloc::format!(".rename_temp_{}_{}", old_name, new_name);
874
875 crate::pr_debug!(
876 "[renameat2] EXCHANGE step 1/3: '{}' -> '{}' (temp)",
877 old_name,
878 temp_name
879 );
880
881 if let Err(e) = old_parent
883 .inode
884 .rename(&old_name, old_parent.inode.clone(), &temp_name)
885 {
886 crate::pr_err!(
887 "[renameat2] EXCHANGE step 1/3 failed: '{}' -> '{}' (error: {:?})",
888 old_name,
889 temp_name,
890 e
891 );
892 return e.to_errno();
893 }
894
895 crate::pr_debug!(
896 "[renameat2] EXCHANGE step 2/3: '{}' -> '{}'",
897 new_name,
898 old_name
899 );
900
901 if let Err(e) = new_parent
903 .inode
904 .rename(&new_name, old_parent.inode.clone(), &old_name)
905 {
906 crate::pr_err!(
907 "[renameat2] EXCHANGE step 2/3 failed: '{}' -> '{}' (error: {:?})",
908 new_name,
909 old_name,
910 e
911 );
912
913 crate::pr_warn!(
915 "[renameat2] Attempting rollback: '{}' -> '{}'",
916 temp_name,
917 old_name
918 );
919
920 match old_parent
921 .inode
922 .rename(&temp_name, old_parent.inode.clone(), &old_name)
923 {
924 Ok(_) => {
925 crate::pr_info!("[renameat2] Rollback successful: restored '{}'", old_name);
926 }
927 Err(rollback_err) => {
928 crate::pr_err!(
929 "[renameat2] CRITICAL: Rollback failed! File '{}' may be lost or duplicated (error: {:?})",
930 old_name,
931 rollback_err
932 );
933 crate::pr_err!(
934 "[renameat2] File system may be in inconsistent state. Temp file '{}' exists.",
935 temp_name
936 );
937 }
938 }
939
940 return e.to_errno();
941 }
942
943 crate::pr_debug!(
944 "[renameat2] EXCHANGE step 3/3: '{}' (temp) -> '{}'",
945 temp_name,
946 new_name
947 );
948
949 if let Err(e) = old_parent
951 .inode
952 .rename(&temp_name, new_parent.inode.clone(), &new_name)
953 {
954 crate::pr_err!(
955 "[renameat2] EXCHANGE step 3/3 failed: '{}' -> '{}' (error: {:?})",
956 temp_name,
957 new_name,
958 e
959 );
960
961 crate::pr_warn!("[renameat2] Attempting full rollback (2 operations)");
963
964 let mut rollback_success = true;
965
966 crate::pr_debug!("[renameat2] Rollback 1/2: '{}' -> '{}'", old_name, new_name);
968 match old_parent
969 .inode
970 .rename(&old_name, new_parent.inode.clone(), &new_name)
971 {
972 Ok(_) => {
973 crate::pr_debug!("[renameat2] Rollback 1/2 successful");
974 }
975 Err(rollback_err) => {
976 crate::pr_err!(
977 "[renameat2] CRITICAL: Rollback 1/2 failed! '{}' -> '{}' (error: {:?})",
978 old_name,
979 new_name,
980 rollback_err
981 );
982 rollback_success = false;
983 }
984 }
985
986 crate::pr_debug!(
988 "[renameat2] Rollback 2/2: '{}' -> '{}'",
989 temp_name,
990 old_name
991 );
992 match old_parent
993 .inode
994 .rename(&temp_name, old_parent.inode.clone(), &old_name)
995 {
996 Ok(_) => {
997 crate::pr_debug!("[renameat2] Rollback 2/2 successful");
998 }
999 Err(rollback_err) => {
1000 crate::pr_err!(
1001 "[renameat2] CRITICAL: Rollback 2/2 failed! '{}' -> '{}' (error: {:?})",
1002 temp_name,
1003 old_name,
1004 rollback_err
1005 );
1006 rollback_success = false;
1007 }
1008 }
1009
1010 if rollback_success {
1011 crate::pr_info!(
1012 "[renameat2] Full rollback successful: files restored to original state"
1013 );
1014 } else {
1015 crate::pr_err!("[renameat2] CRITICAL: Partial or complete rollback failure!");
1016 crate::pr_err!(
1017 "[renameat2] File system is in INCONSISTENT STATE. Manual recovery may be required."
1018 );
1019 crate::pr_err!(
1020 "[renameat2] Affected files: '{}', '{}', temp '{}'",
1021 old_name,
1022 new_name,
1023 temp_name
1024 );
1025 }
1026
1027 return e.to_errno();
1028 }
1029
1030 crate::pr_info!(
1031 "[renameat2] EXCHANGE completed: '{}' <-> '{}'",
1032 old_name,
1033 new_name
1034 );
1035
1036 old_parent.remove_child(&old_name);
1038 old_parent.remove_child(&temp_name);
1039 new_parent.remove_child(&new_name);
1040 } else if rename_flags.contains(RenameFlags::NOREPLACE) {
1041 if new_parent.inode.lookup(&new_name).is_ok() {
1043 return -(EEXIST as isize);
1044 }
1045
1046 if let Err(e) = old_parent
1048 .inode
1049 .rename(&old_name, new_parent.inode.clone(), &new_name)
1050 {
1051 return e.to_errno();
1052 }
1053
1054 old_parent.remove_child(&old_name);
1056 } else if rename_flags.contains(RenameFlags::WHITEOUT) {
1057 return FsError::NotSupported.to_errno();
1059 } else {
1060 if let Err(e) = old_parent
1062 .inode
1063 .rename(&old_name, new_parent.inode.clone(), &new_name)
1064 {
1065 return e.to_errno();
1066 }
1067
1068 old_parent.remove_child(&old_name);
1070 new_parent.remove_child(&new_name);
1071 }
1072
1073 0
1074}
1075
1076pub fn mount(
1087 source: *const c_char,
1088 target: *const c_char,
1089 filesystemtype: *const c_char,
1090 _mountflags: u64,
1091 _data: *const core::ffi::c_void,
1092) -> isize {
1093 use crate::config::EXT4_BLOCK_SIZE;
1094 use crate::fs::ext4::Ext4FileSystem;
1095 use crate::fs::sysfs::find_block_device;
1096 use crate::fs::{init_dev, init_procfs, init_sysfs, mount_tmpfs};
1097 use crate::vfs::{MOUNT_TABLE, MountFlags as VfsMountFlags};
1098 use alloc::string::String;
1099
1100 let _guard = SumGuard::new();
1102
1103 let target_str = match get_path_safe(target) {
1105 Ok(s) => s.to_string(),
1106 Err(_) => {
1107 return FsError::InvalidArgument.to_errno();
1108 }
1109 };
1110
1111 let source_str = if !source.is_null() {
1113 match get_path_safe(source) {
1114 Ok(s) => s.to_string(),
1115 Err(_) => {
1116 return FsError::InvalidArgument.to_errno();
1117 }
1118 }
1119 } else {
1120 String::new()
1121 };
1122
1123 let fstype_str = if !filesystemtype.is_null() {
1125 match get_path_safe(filesystemtype) {
1126 Ok(s) => s.to_string(),
1127 Err(_) => {
1128 return FsError::InvalidArgument.to_errno();
1129 }
1130 }
1131 } else {
1132 String::new()
1133 };
1134
1135 crate::pr_info!(
1136 "[SYSCALL] mount: source='{}', target='{}', type='{}'",
1137 source_str,
1138 target_str,
1139 fstype_str
1140 );
1141
1142 match target_str.as_str() {
1144 "/proc" => {
1145 return match init_procfs() {
1146 Ok(_) => 0,
1147 Err(e) => e.to_errno(),
1148 };
1149 }
1150 "/sys" => {
1151 return match init_sysfs() {
1152 Ok(_) => 0,
1153 Err(e) => e.to_errno(),
1154 };
1155 }
1156 "/tmp" => {
1157 return match mount_tmpfs("/tmp", 0) {
1158 Ok(_) => 0,
1159 Err(e) => e.to_errno(),
1160 };
1161 }
1162 "/dev" => {
1163 if let Err(e) = mount_tmpfs("/dev", 0) {
1165 return e.to_errno();
1166 }
1167 return match init_dev() {
1169 Ok(_) => 0,
1170 Err(e) => e.to_errno(),
1171 };
1172 }
1173 _ => {}
1174 }
1175
1176 if fstype_str == "ext4" {
1178 let dev_info = match find_block_device(&source_str) {
1180 Some(info) => info,
1181 None => {
1182 crate::pr_err!("[SYSCALL] mount: block device '{}' not found", source_str);
1183 return -(ENOENT as isize);
1184 }
1185 };
1186
1187 let block_device = dev_info.device;
1188
1189 let block_size = EXT4_BLOCK_SIZE;
1191 let total_blocks = block_device.total_blocks();
1197
1198 let ext4_fs = match Ext4FileSystem::open(block_device.clone(), block_size, total_blocks, 0)
1199 {
1200 Ok(fs) => fs,
1201 Err(e) => {
1202 crate::pr_err!("[SYSCALL] mount: failed to open ext4: {:?}", e);
1203 return e.to_errno();
1204 }
1205 };
1206
1207 match MOUNT_TABLE.mount(
1209 ext4_fs,
1210 &target_str,
1211 VfsMountFlags::empty(),
1212 Some(source_str),
1213 ) {
1214 Ok(()) => {
1215 crate::pr_info!(
1216 "[SYSCALL] mount: successfully mounted ext4 at '{}'",
1217 target_str
1218 );
1219 return 0;
1220 }
1221 Err(e) => {
1222 crate::pr_err!("[SYSCALL] mount: failed: {:?}", e);
1223 return e.to_errno();
1224 }
1225 }
1226 }
1227
1228 crate::pr_err!(
1229 "[SYSCALL] mount: unsupported filesystem type '{}' or target '{}'",
1230 fstype_str,
1231 target_str
1232 );
1233 -(EINVAL as isize)
1234}
1235
1236pub fn umount2(target: *const c_char, _flags: i32) -> isize {
1246 use crate::vfs::MOUNT_TABLE;
1247
1248 let _guard = SumGuard::new();
1250
1251 let target_str = match get_path_safe(target) {
1253 Ok(s) => s.to_string(),
1254 Err(_) => {
1255 return FsError::InvalidArgument.to_errno();
1256 }
1257 };
1258
1259 crate::pr_info!("[SYSCALL] umount2: unmounting '{}'", target_str);
1260
1261 match MOUNT_TABLE.umount(&target_str) {
1265 Ok(()) => {
1266 crate::pr_info!("[SYSCALL] umount2: successfully unmounted '{}'", target_str);
1267 0
1268 }
1269 Err(e) => {
1270 crate::pr_err!("[SYSCALL] umount2: failed: {:?}", e);
1271 e.to_errno()
1272 }
1273 }
1274}
1275
1276pub fn sync() -> isize {
1282 use crate::kernel::syscall::util::flush_all_block_devices;
1283
1284 let _ = flush_all_block_devices();
1286 0
1287}
1288
1289pub fn syncfs(fd: usize) -> isize {
1291 use crate::kernel::syscall::util::flush_block_device_by_fd;
1292
1293 match flush_block_device_by_fd(fd) {
1294 Ok(()) => 0,
1295 Err(errno) => errno,
1296 }
1297}
1298
1299pub fn fsync(fd: usize) -> isize {
1304 syncfs(fd)
1305}
1306
1307pub fn fdatasync(fd: usize) -> isize {
1312 fsync(fd)
1313}
1314
1315pub fn fchownat(dirfd: i32, pathname: *const c_char, owner: u32, group: u32, flags: u32) -> isize {
1331 use crate::uapi::fs::AtFlags;
1332
1333 let _guard = SumGuard::new();
1335 let path_str = match get_path_safe(pathname) {
1336 Ok(s) => s.to_string(),
1337 Err(_) => {
1338 return -(EINVAL as isize);
1339 }
1340 };
1341
1342 let at_flags = AtFlags::from_bits_truncate(flags);
1344 let follow_symlink = !at_flags.contains(AtFlags::SYMLINK_NOFOLLOW);
1345 let empty_path = at_flags.contains(AtFlags::EMPTY_PATH);
1346
1347 if empty_path && path_str.is_empty() {
1349 if dirfd == AT_FDCWD {
1351 return -(EINVAL as isize);
1353 }
1354
1355 let task = current_task();
1356 let file = match task.lock().fd_table.get(dirfd as usize) {
1357 Ok(f) => f,
1358 Err(e) => return e.to_errno(),
1359 };
1360
1361 let dentry = match file.dentry() {
1362 Ok(d) => d,
1363 Err(e) => return e.to_errno(),
1364 };
1365
1366 return match dentry.inode.chown(owner, group) {
1367 Ok(()) => 0,
1368 Err(e) => e.to_errno(),
1369 };
1370 }
1371
1372 let dentry = match resolve_at_path_with_flags(dirfd, &path_str, follow_symlink) {
1374 Ok(d) => d,
1375 Err(e) => return e.to_errno(),
1376 };
1377
1378 match dentry.inode.chown(owner, group) {
1380 Ok(()) => 0,
1381 Err(e) => e.to_errno(),
1382 }
1383}
1384
1385pub fn fchmodat(dirfd: i32, pathname: *const c_char, mode: u32, flags: u32) -> isize {
1400 use crate::uapi::fs::AtFlags;
1401
1402 let _guard = SumGuard::new();
1404 let path_str = match get_path_safe(pathname) {
1405 Ok(s) => s.to_string(),
1406 Err(_) => {
1407 return -(EINVAL as isize);
1408 }
1409 };
1410
1411 let at_flags = AtFlags::from_bits_truncate(flags);
1413 let follow_symlink = !at_flags.contains(AtFlags::SYMLINK_NOFOLLOW);
1414
1415 let mode = mode & 0o7777; let file_mode = match FileMode::from_bits(mode) {
1418 Some(m) => m,
1419 None => return -(EINVAL as isize),
1420 };
1421
1422 let dentry = match resolve_at_path_with_flags(dirfd, &path_str, follow_symlink) {
1424 Ok(d) => d,
1425 Err(e) => return e.to_errno(),
1426 };
1427
1428 match dentry.inode.chmod(file_mode) {
1430 Ok(()) => 0,
1431 Err(e) => e.to_errno(),
1432 }
1433}
1434
1435pub fn mknodat(dirfd: i32, pathname: *const c_char, mode: u32, dev: u64) -> isize {
1447 let _guard = SumGuard::new();
1449 let path_str = match get_path_safe(pathname) {
1450 Ok(s) => s.to_string(),
1451 Err(_) => {
1452 return FsError::InvalidArgument.to_errno();
1453 }
1454 };
1455
1456 let (dir_path, filename) = match split_path(&path_str) {
1458 Ok(p) => p,
1459 Err(e) => return e.to_errno(),
1460 };
1461
1462 let parent_dentry = match resolve_at_path(dirfd, &dir_path) {
1464 Ok(Some(d)) => d,
1465 Ok(None) => return FsError::NotFound.to_errno(),
1466 Err(e) => return e.to_errno(),
1467 };
1468
1469 let file_mode = FileMode::from_bits_truncate(mode);
1471
1472 match parent_dentry.inode.mknod(&filename, file_mode, dev) {
1474 Ok(child_inode) => {
1475 let child_dentry = Dentry::new(filename.clone(), child_inode);
1477 parent_dentry.add_child(child_dentry.clone());
1478 DENTRY_CACHE.insert(&child_dentry);
1479 0
1480 }
1481 Err(e) => e.to_errno(),
1482 }
1483}
1484
1485pub fn symlinkat(target: *const c_char, newdirfd: i32, linkpath: *const c_char) -> isize {
1499 let _guard = SumGuard::new();
1501 let target_str = match get_path_safe(target) {
1502 Ok(s) => s.to_string(),
1503 Err(_) => {
1504 return FsError::InvalidArgument.to_errno();
1505 }
1506 };
1507
1508 let link_str = match get_path_safe(linkpath) {
1510 Ok(s) => s.to_string(),
1511 Err(_) => {
1512 return FsError::InvalidArgument.to_errno();
1513 }
1514 };
1515
1516 let (dir_path, link_name) = match split_path(&link_str) {
1518 Ok(p) => p,
1519 Err(e) => return e.to_errno(),
1520 };
1521
1522 let parent_dentry = match resolve_at_path(newdirfd, &dir_path) {
1524 Ok(Some(d)) => d,
1525 Ok(None) => return FsError::NotFound.to_errno(),
1526 Err(e) => return e.to_errno(),
1527 };
1528
1529 match parent_dentry.inode.symlink(&link_name, &target_str) {
1531 Ok(symlink_inode) => {
1532 let symlink_dentry = Dentry::new(link_name.clone(), symlink_inode);
1534 parent_dentry.add_child(symlink_dentry.clone());
1535 DENTRY_CACHE.insert(&symlink_dentry);
1536 0
1537 }
1538 Err(e) => e.to_errno(),
1539 }
1540}