kernel/abi/linux/riscv64/
fs.rs

1use crate::{
2    abi::linux::riscv64::LinuxRiscv64Abi,
3    arch::Trapframe,
4    device::manager::DeviceManager,
5    executor::TransparentExecutor,
6    fs::{DirectoryEntry, FileType, SeekFrom},
7    library::std::string::{
8        cstring_to_string, parse_c_string_from_userspace, parse_string_array_from_userspace,
9    },
10    object::capability::StreamError,
11    sched::scheduler::get_scheduler,
12    task::mytask,
13};
14use alloc::{
15    string::{String, ToString},
16    vec,
17    vec::Vec,
18};
19use core::sync::atomic::{AtomicU64, Ordering};
20
21use super::errno;
22
23static XORSHIFT_STATE: AtomicU64 = AtomicU64::new(0);
24
25fn remap_shm_path(path: &str) -> String {
26    if let Some(rest) = path.strip_prefix("/dev/shm/") {
27        alloc::format!("/tmp/{}", rest)
28    } else if path == "/dev/shm" || path == "/dev/shm/" {
29        "/tmp".to_string()
30    } else {
31        path.to_string()
32    }
33}
34
35fn is_wl_shm_path(path: &str) -> bool {
36    path.starts_with("/tmp/wl_shm-")
37}
38
39/// Linux stat structure for RISC-V 64-bit
40/// Matches asm-generic struct stat layout used by musl on 64-bit.
41#[derive(Debug, Clone, Copy)]
42#[repr(C)]
43pub struct LinuxStat {
44    pub st_dev: u64,        // Device ID of device containing file
45    pub st_ino: u64,        // Inode number
46    pub st_mode: u32,       // File type and mode
47    pub st_nlink: u32,      // Number of hard links
48    pub st_uid: u32,        // User ID of owner
49    pub st_gid: u32,        // Group ID of owner
50    pub st_rdev: u64,       // Device ID (if special file)
51    pub __pad1: u64,        // Padding for alignment
52    pub st_size: i64,       // Total size, in bytes
53    pub st_blksize: i32,    // Block size for filesystem I/O
54    pub __pad2: i32,        // Padding for alignment
55    pub st_blocks: i64,     // Number of 512B blocks allocated
56    pub st_atime: i64,      // Time of last access (seconds)
57    pub st_atime_nsec: u64, // Time of last access (nanoseconds)
58    pub st_mtime: i64,      // Time of last modification (seconds)
59    pub st_mtime_nsec: u64, // Time of last modification (nanoseconds)
60    pub st_ctime: i64,      // Time of last status change (seconds)
61    pub st_ctime_nsec: u64, // Time of last status change (nanoseconds)
62    pub __unused4: u32,     // Reserved
63    pub __unused5: u32,     // Reserved
64}
65
66/// Linux statx timestamp structure
67#[derive(Debug, Clone, Copy, Default)]
68#[repr(C)]
69pub struct LinuxStatxTimestamp {
70    pub tv_sec: i64,
71    pub tv_nsec: u32,
72    pub __reserved: i32,
73}
74
75/// Linux statx structure (matches Linux UAPI layout)
76#[derive(Debug, Clone, Copy, Default)]
77#[repr(C)]
78pub struct LinuxStatx {
79    pub stx_mask: u32,
80    pub stx_blksize: u32,
81    pub stx_attributes: u64,
82    pub stx_nlink: u32,
83    pub stx_uid: u32,
84    pub stx_gid: u32,
85    pub stx_mode: u16,
86    pub __spare0: [u16; 1],
87    pub stx_ino: u64,
88    pub stx_size: u64,
89    pub stx_blocks: u64,
90    pub stx_attributes_mask: u64,
91    pub stx_atime: LinuxStatxTimestamp,
92    pub stx_btime: LinuxStatxTimestamp,
93    pub stx_ctime: LinuxStatxTimestamp,
94    pub stx_mtime: LinuxStatxTimestamp,
95    pub stx_rdev_major: u32,
96    pub stx_rdev_minor: u32,
97    pub stx_dev_major: u32,
98    pub stx_dev_minor: u32,
99    pub stx_mnt_id: u64,
100    pub stx_dio_mem_align: u32,
101    pub stx_dio_offset_align: u32,
102    pub stx_subvol: u64,
103    pub stx_atomic_write_unit_min: u32,
104    pub stx_atomic_write_unit_max: u32,
105    pub stx_atomic_write_segments_max: u32,
106    pub stx_dio_read_offset_align: u32,
107    pub __spare3: [u64; 9],
108}
109
110// Linux file type constants for st_mode field
111#[allow(dead_code)]
112pub const S_IFMT: u32 = 0o170000; // Bit mask for the file type bit field
113pub const S_IFSOCK: u32 = 0o140000; // Socket
114pub const S_IFLNK: u32 = 0o120000; // Symbolic link
115pub const S_IFREG: u32 = 0o100000; // Regular file
116pub const S_IFBLK: u32 = 0o060000; // Block device
117pub const S_IFDIR: u32 = 0o040000; // Directory
118pub const S_IFCHR: u32 = 0o020000; // Character device
119pub const S_IFIFO: u32 = 0o010000; // FIFO
120
121// Linux permission constants
122#[allow(dead_code)]
123pub const S_IRWXU: u32 = 0o0700; // User (file owner) has read, write, and execute permission
124pub const S_IRUSR: u32 = 0o0400; // User has read permission
125pub const S_IWUSR: u32 = 0o0200; // User has write permission
126pub const S_IXUSR: u32 = 0o0100; // User has execute permission
127#[allow(dead_code)]
128pub const S_IRWXG: u32 = 0o0070; // Group has read, write, and execute permission
129pub const S_IRGRP: u32 = 0o0040; // Group has read permission
130#[allow(dead_code)]
131pub const S_IWGRP: u32 = 0o0020; // Group has write permission
132pub const S_IXGRP: u32 = 0o0010; // Group has execute permission
133#[allow(dead_code)]
134pub const S_IRWXO: u32 = 0o0007; // Others have read, write, and execute permission
135pub const S_IROTH: u32 = 0o0004; // Others have read permission
136#[allow(dead_code)]
137pub const S_IWOTH: u32 = 0o0002; // Others have write permission
138pub const S_IXOTH: u32 = 0o0001; // Others have execute permission
139
140// Linux statx mask bits
141#[allow(dead_code)]
142pub const STATX_TYPE: u32 = 0x00000001;
143#[allow(dead_code)]
144pub const STATX_MODE: u32 = 0x00000002;
145#[allow(dead_code)]
146pub const STATX_NLINK: u32 = 0x00000004;
147#[allow(dead_code)]
148pub const STATX_UID: u32 = 0x00000008;
149#[allow(dead_code)]
150pub const STATX_GID: u32 = 0x00000010;
151#[allow(dead_code)]
152pub const STATX_ATIME: u32 = 0x00000020;
153#[allow(dead_code)]
154pub const STATX_MTIME: u32 = 0x00000040;
155#[allow(dead_code)]
156pub const STATX_CTIME: u32 = 0x00000080;
157#[allow(dead_code)]
158pub const STATX_INO: u32 = 0x00000100;
159#[allow(dead_code)]
160pub const STATX_SIZE: u32 = 0x00000200;
161#[allow(dead_code)]
162pub const STATX_BLOCKS: u32 = 0x00000400;
163pub const STATX_BASIC_STATS: u32 = 0x000007ff;
164#[allow(dead_code)]
165pub const STATX_BTIME: u32 = 0x00000800;
166
167// Linux getrandom flags
168#[allow(dead_code)]
169pub const GRND_NONBLOCK: u32 = 0x0001;
170#[allow(dead_code)]
171pub const GRND_RANDOM: u32 = 0x0002;
172#[allow(dead_code)]
173pub const GRND_INSECURE: u32 = 0x0004;
174
175// Linux fcntl command constants
176pub const F_DUPFD: u32 = 0; // Duplicate file descriptor
177pub const F_GETFD: u32 = 1; // Get file descriptor flags
178pub const F_SETFD: u32 = 2; // Set file descriptor flags
179pub const F_GETFL: u32 = 3; // Get file status flags
180pub const F_SETFL: u32 = 4; // Set file status flags
181pub const F_GETLK: u32 = 5; // Get record locking information
182pub const F_SETLK: u32 = 6; // Set record lock (non-blocking)
183pub const F_SETLKW: u32 = 7; // Set record lock (blocking)
184pub const F_SETOWN: u32 = 8; // Set owner (process receiving SIGIO/SIGURG)
185pub const F_GETOWN: u32 = 9; // Get owner (process receiving SIGIO/SIGURG)
186pub const F_SETSIG: u32 = 10; // Set signal sent when I/O is possible
187pub const F_GETSIG: u32 = 11; // Get signal sent when I/O is possible
188pub const F_SETLEASE: u32 = 1024; // Set a lease
189pub const F_GETLEASE: u32 = 1025; // Get current lease
190pub const F_NOTIFY: u32 = 1026; // Request notifications on a directory
191pub const F_DUPFD_CLOEXEC: u32 = 1030; // Duplicate with close-on-exec
192
193// Linux file descriptor flags
194pub const FD_CLOEXEC: u32 = 1; // Close-on-exec flag
195
196// Linux open flags
197#[allow(dead_code)]
198pub const O_RDONLY: i32 = 0o0; // Read only
199#[allow(dead_code)]
200pub const O_WRONLY: i32 = 0o1; // Write only
201#[allow(dead_code)]
202pub const O_RDWR: i32 = 0o2; // Read and write
203pub const O_CREAT: i32 = 0o100; // Create file if it doesn't exist
204pub const O_EXCL: i32 = 0o200; // Fail if file exists (with O_CREAT)
205#[allow(dead_code)]
206pub const O_NOCTTY: i32 = 0o400; // Don't assign controlling terminal
207pub const O_TRUNC: i32 = 0o1000; // Truncate file to zero length
208pub const O_APPEND: i32 = 0o2000; // Append mode
209#[allow(dead_code)]
210pub const O_NONBLOCK: i32 = 0o4000; // Non-blocking mode
211#[allow(dead_code)]
212pub const O_DSYNC: i32 = 0o10000; // Data sync
213#[allow(dead_code)]
214pub const O_ASYNC: i32 = 0o20000; // Asynchronous I/O
215#[allow(dead_code)]
216pub const O_DIRECT: i32 = 0o40000; // Direct I/O
217#[allow(dead_code)]
218pub const O_LARGEFILE: i32 = 0o100000; // Large file support
219pub const O_DIRECTORY: i32 = 0o200000; // Must be a directory
220#[allow(dead_code)]
221pub const O_NOFOLLOW: i32 = 0o400000; // Don't follow symlinks
222#[allow(dead_code)]
223pub const O_NOATIME: i32 = 0o1000000; // Don't update access time
224pub const O_CLOEXEC: i32 = 0o2000000; // Close-on-exec
225#[allow(dead_code)]
226pub const O_SYNC: i32 = O_DSYNC; // Data and metadata sync
227#[allow(dead_code)]
228pub const O_PATH: i32 = 0o10000000; // Path-based operations only
229#[allow(dead_code)]
230pub const O_TMPFILE: i32 = 0o20000000; // Create temporary file
231
232use crate::device::DeviceCapability;
233
234impl LinuxStat {
235    /// Create a new LinuxStat from Scarlet FileMetadata
236    pub fn from_metadata(metadata: &crate::fs::FileMetadata) -> Self {
237        let st_mode = match metadata.file_type {
238            FileType::RegularFile => S_IFREG,
239            FileType::Directory => S_IFDIR,
240            FileType::CharDevice(_) => S_IFCHR,
241            FileType::BlockDevice(_) => S_IFBLK,
242            FileType::SymbolicLink(_) => S_IFLNK,
243            FileType::Pipe => S_IFIFO,
244            FileType::Socket(_) => S_IFSOCK,
245            FileType::Unknown => S_IFREG, // Default to regular file
246        } | if metadata.permissions.read {
247            S_IRUSR | S_IRGRP | S_IXGRP | S_IROTH
248        } else {
249            0
250        } | if metadata.permissions.write {
251            S_IWUSR
252        } else {
253            0
254        } | if metadata.permissions.execute {
255            S_IXUSR | S_IXGRP | S_IXOTH
256        } else {
257            0
258        };
259
260        Self {
261            st_dev: 0, // Virtual device ID
262            st_ino: metadata.file_id,
263            st_mode,
264            st_nlink: metadata.link_count as u32,
265            st_uid: 0,  // Root user
266            st_gid: 0,  // Root group
267            st_rdev: 0, // Not a special file by default
268            __pad1: 0,
269            st_size: metadata.size as i64,
270            st_blksize: 4096, // Standard block size
271            __pad2: 0,
272            st_blocks: ((metadata.size + 511) / 512) as i64, // Number of 512-byte blocks
273            st_atime: metadata.accessed_time as i64,
274            st_atime_nsec: 0,
275            st_mtime: metadata.modified_time as i64,
276            st_mtime_nsec: 0,
277            st_ctime: metadata.created_time as i64,
278            st_ctime_nsec: 0,
279            __unused4: 0,
280            __unused5: 0,
281        }
282    }
283}
284
285fn statx_timestamp_from_secs(seconds: u64) -> LinuxStatxTimestamp {
286    LinuxStatxTimestamp {
287        tv_sec: seconds as i64,
288        tv_nsec: 0,
289        __reserved: 0,
290    }
291}
292
293fn next_pseudo_random_u64() -> u64 {
294    loop {
295        let mut state = XORSHIFT_STATE.load(Ordering::Relaxed);
296        if state == 0 {
297            state = crate::time::current_time() ^ 0x9e3779b97f4a7c15;
298            if state == 0 {
299                state = 0x4f1bbcdcb7a43413;
300            }
301        }
302        let mut x = state;
303        x ^= x << 13;
304        x ^= x >> 7;
305        x ^= x << 17;
306        if XORSHIFT_STATE
307            .compare_exchange(state, x, Ordering::Relaxed, Ordering::Relaxed)
308            .is_ok()
309        {
310            return x;
311        }
312    }
313}
314
315fn fill_pseudo_random(buffer: &mut [u8]) {
316    let mut offset = 0;
317    while offset < buffer.len() {
318        let bytes = next_pseudo_random_u64().to_le_bytes();
319        let to_copy = core::cmp::min(bytes.len(), buffer.len() - offset);
320        buffer[offset..offset + to_copy].copy_from_slice(&bytes[..to_copy]);
321        offset += to_copy;
322    }
323}
324
325fn fill_statx_from_stat(
326    statx: &mut LinuxStatx,
327    stat: &LinuxStat,
328    created_time: u64,
329    requested_mask: u32,
330) {
331    let supported_mask = STATX_BASIC_STATS | STATX_BTIME;
332    let effective_mask = if requested_mask == 0 {
333        STATX_BASIC_STATS
334    } else {
335        requested_mask
336    };
337
338    statx.stx_mask = supported_mask & effective_mask;
339    statx.stx_blksize = stat.st_blksize as u32;
340    statx.stx_attributes = 0;
341    statx.stx_nlink = stat.st_nlink;
342    statx.stx_uid = stat.st_uid;
343    statx.stx_gid = stat.st_gid;
344    statx.stx_mode = stat.st_mode as u16;
345    statx.__spare0 = [0; 1];
346    statx.stx_ino = stat.st_ino;
347    statx.stx_size = stat.st_size as u64;
348    statx.stx_blocks = stat.st_blocks as u64;
349    statx.stx_attributes_mask = 0;
350    statx.stx_atime = statx_timestamp_from_secs(stat.st_atime as u64);
351    statx.stx_btime = statx_timestamp_from_secs(created_time);
352    statx.stx_ctime = statx_timestamp_from_secs(stat.st_ctime as u64);
353    statx.stx_mtime = statx_timestamp_from_secs(stat.st_mtime as u64);
354    statx.stx_rdev_major = 0;
355    statx.stx_rdev_minor = 0;
356    statx.stx_dev_major = 0;
357    statx.stx_dev_minor = 0;
358    statx.stx_mnt_id = 0;
359    statx.stx_dio_mem_align = 0;
360    statx.stx_dio_offset_align = 0;
361    statx.stx_subvol = 0;
362    statx.stx_atomic_write_unit_min = 0;
363    statx.stx_atomic_write_unit_max = 0;
364    statx.stx_atomic_write_segments_max = 0;
365    statx.stx_dio_read_offset_align = 0;
366    statx.__spare3 = [0; 9];
367}
368
369// /// Convert Scarlet DirectoryEntry to Linux Dirent and write to buffer
370// fn read_directory_as_Linux_dirent(buf_ptr: *mut u8, count: usize, buffer_data: &[u8]) -> usize {
371//     if count < Dirent::DIRENT_SIZE {
372//         return 0; // Buffer too small for even one entry
373//     }
374
375//     // Parse DirectoryEntry from buffer data
376//     if let Some(dir_entry) = DirectoryEntry::parse(buffer_data) {
377//         // Convert Scarlet DirectoryEntry to Linux Dirent
378//         let inum = (dir_entry.file_id & 0xFFFF) as u16; // Use lower 16 bits as inode number
379//         let name = dir_entry.name_str().unwrap_or("");
380
381//         let Linux_dirent = Dirent::new(inum, name);
382
383//         // Check if we have enough space
384//         if count >= Dirent::DIRENT_SIZE {
385//             // Copy the dirent to the buffer
386//             let dirent_bytes = Linux_dirent.as_bytes();
387//             unsafe {
388//                 core::ptr::copy_nonoverlapping(
389//                     dirent_bytes.as_ptr(),
390//                     buf_ptr,
391//                     Dirent::DIRENT_SIZE
392//                 );
393//             }
394//             return Dirent::DIRENT_SIZE;
395//         }
396//     }
397
398//     0 // No data or error
399// }
400
401const MAX_PATH_LENGTH: usize = 1024; // Increased to handle long command lines
402const MAX_ARG_COUNT: usize = 64;
403
404/// Linux sys_exec system call implementation
405/// Executes a program specified by the given path, replacing the current process image with a new one.
406/// Also allows passing arguments and environment variables to the new program.
407///
408/// Arguments:
409/// - abi: LinuxRiscv64Abi context
410/// - trapframe: Trapframe containing syscall arguments
411///
412/// Returns:
413/// - 0 on success
414/// - usize::MAX (Linux -1) on error
415#[allow(dead_code)]
416pub fn sys_exec(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
417    let task = mytask().unwrap();
418
419    // Increment PC to avoid infinite loop if execve fails
420    trapframe.increment_pc_next(task);
421
422    // Get arguments from trapframe
423    let path_ptr = trapframe.get_arg(0);
424    let argv_ptr = trapframe.get_arg(1);
425
426    // Parse path
427    let path_str = match parse_c_string_from_userspace(task, path_ptr, MAX_PATH_LENGTH) {
428        Ok(path) => match to_absolute_path_v2(&task, &path) {
429            Ok(abs_path) => abs_path,
430            Err(_) => return usize::MAX, // Path error
431        },
432        Err(_) => return usize::MAX, // Path parsing error
433    };
434
435    // Parse argv and envp
436    let argv_strings =
437        match parse_string_array_from_userspace(task, argv_ptr, MAX_ARG_COUNT, MAX_PATH_LENGTH) {
438            Ok(args) => args,
439            Err(_) => return usize::MAX, // argv parsing error
440        };
441
442    // Convert Vec<String> to Vec<&str> for TransparentExecutor
443    let argv_refs: Vec<&str> = argv_strings.iter().map(|s| s.as_str()).collect();
444
445    // Use TransparentExecutor for cross-ABI execution
446    match TransparentExecutor::execute_binary(&path_str, &argv_refs, &[], task, trapframe, false) {
447        Ok(_) => {
448            // execve normally should not return on success - the process is replaced
449            // However, if ABI module sets trapframe return value and returns here,
450            // we should respect that value instead of hardcoding 0
451            trapframe.get_return_value()
452        }
453        Err(_) => {
454            // Execution failed - return error code
455            // The trap handler will automatically set trapframe return value from our return
456            usize::MAX // Error return value
457        }
458    }
459}
460
461#[repr(i32)]
462#[allow(dead_code)]
463enum OpenMode {
464    ReadOnly = 0x000,
465    WriteOnly = 0x001,
466    ReadWrite = 0x002,
467    Create = 0x200,
468    Truncate = 0x400,
469}
470
471/// Linux sys_openat implementation for Scarlet VFS v2
472///
473/// Opens a file relative to a directory file descriptor (dirfd) and path.
474/// If dirfd == AT_FDCWD, uses the current working directory as the base.
475/// Otherwise, resolves the base directory from the file descriptor.
476/// Uses VfsManager::open_relative for safe and efficient path resolution.
477///
478/// Arguments:
479/// - abi: LinuxRiscv64Abi context
480/// - trapframe: Trapframe containing syscall arguments
481///
482/// Returns:
483/// - File descriptor on success
484/// - usize::MAX (Linux -1) on error
485pub fn sys_openat(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
486    let task = mytask().unwrap();
487    let dirfd = trapframe.get_arg(0) as i32;
488    let path_ptr = task
489        .vm_manager
490        .translate_vaddr(trapframe.get_arg(1))
491        .unwrap() as *const u8;
492    let flags = trapframe.get_arg(2) as i32;
493
494    // Increment PC to avoid infinite loop if openat fails
495    trapframe.increment_pc_next(task);
496
497    // Parse path from user space
498    let path_str = match cstring_to_string(path_ptr, MAX_PATH_LENGTH) {
499        Ok((path, _)) => path,
500        Err(_) => return errno::to_result(errno::EFAULT), // Invalid UTF-8 or bad address
501    };
502
503    let path_str = remap_shm_path(&path_str);
504
505    // crate::println!("sys_openat: epc={:#x}, dirfd={}, path='{}', flags={:#o}", trapframe.epc, dirfd, path_str, flags);
506
507    let vfs_guard = task.vfs.read();
508    let vfs = vfs_guard.as_deref().unwrap();
509
510    // Determine base directory (entry and mount) for path resolution
511    use crate::fs::vfs_v2::core::VfsFileObject;
512
513    const AT_FDCWD: i32 = -100;
514    let (base_entry, base_mount) = if dirfd == AT_FDCWD {
515        // Use current working directory as base
516        vfs.get_cwd().unwrap_or_else(|| {
517            let root_mount = vfs.mount_tree.root_mount.read().clone();
518            (root_mount.root.clone(), root_mount)
519        })
520    } else {
521        // Use directory file descriptor as base
522        let handle = match abi.get_handle(dirfd as usize) {
523            Some(h) => h,
524            None => return errno::to_result(errno::EBADF), // Bad file descriptor
525        };
526        let kernel_obj = match task.handle_table.get(handle) {
527            Some(obj) => obj,
528            None => return errno::to_result(errno::EBADF), // Bad file descriptor
529        };
530        let file_obj = match kernel_obj.as_file() {
531            Some(f) => f,
532            None => return errno::to_result(errno::ENOTDIR), // Not a directory
533        };
534        let vfs_file_obj = file_obj
535            .as_any()
536            .downcast_ref::<VfsFileObject>()
537            .ok_or(())
538            .unwrap();
539        (
540            vfs_file_obj.get_vfs_entry().clone(),
541            vfs_file_obj.get_mount_point().clone(),
542        )
543    };
544
545    // Open the file using VfsManager::open_relative
546    // Apply a few Linux-compat path translations for devices
547    let mapped_path = if path_str == "/dev/tty" {
548        "/dev/tty0".to_string()
549    } else if let Some(rest) = path_str.strip_prefix("/dev/vc/") {
550        // Map /dev/vc/N -> /dev/ttyN; if ttyN doesn't exist, we may further alias below
551        alloc::format!("/dev/tty{}", rest)
552    } else if let Some(n) = path_str.strip_prefix("/dev/tty") {
553        // If requesting a numbered tty other than 0, alias to tty0 for minimal support
554        // This is a compatibility shim until multiple VTs are implemented
555        if n != "0" && n.chars().all(|c| c.is_ascii_digit()) {
556            "/dev/tty0".to_string()
557        } else {
558            path_str.clone()
559        }
560    } else {
561        path_str.clone()
562    };
563
564    // crate::println!("sys_openat: attempting to open '{}' with flags {:#o} (dirfd={})", mapped_path, flags, dirfd);
565
566    // // Log flags details
567    // let flags_table = [
568    //     (O_RDONLY, "O_RDONLY"),
569    //     (O_WRONLY, "O_WRONLY"),
570    //     (O_RDWR, "O_RDWR"),
571    //     (O_CREAT, "O_CREAT"),
572    //     (O_EXCL, "O_EXCL"),
573    //     (O_TRUNC, "O_TRUNC"),
574    //     (O_APPEND, "O_APPEND"),
575    //     (O_DIRECTORY, "O_DIRECTORY"),
576    //     (O_CLOEXEC, "O_CLOEXEC"),
577    // ];
578    // for (flag, name) in flags_table.iter() {
579    //     if (flags & *flag) != 0 {
580    //         crate::println!("  Flag set: {}", name);
581    //     }
582    // }
583
584    let file = vfs.open_from(&base_entry, &base_mount, &mapped_path, flags as u32);
585
586    let kernel_obj = match file {
587        Ok(obj) => {
588            // crate::println!("sys_openat: successfully opened '{}'", mapped_path);
589            obj
590        }
591        Err(e) => {
592            // crate::println!("sys_openat: failed to open '{}' -> {:?}", mapped_path, e);
593            // If open failed and O_CREAT flag is set, try to create the file
594            if flags & O_CREAT != 0 {
595                // crate::println!("sys_openat: O_CREAT flag set, attempting to create file '{}'", path_str);
596                // Build absolute path for file creation before getting mutable VFS reference
597                let absolute_path = if mapped_path.starts_with('/') {
598                    mapped_path.to_string()
599                } else {
600                    // Construct absolute path by resolving relative to current working directory
601                    match to_absolute_path_v2(&task, &mapped_path) {
602                        Ok(p) => p,
603                        Err(_) => return errno::to_result(errno::ENOENT), // Path resolution failed
604                    }
605                };
606
607                // Get mutable VFS reference for file creation
608                let vfs_mut = match task.vfs.read().clone() {
609                    Some(v) => v,
610                    None => return errno::to_result(errno::EIO), // VFS not available
611                };
612
613                // Create the file (regular file type)
614                match vfs_mut.create_file(&absolute_path, FileType::RegularFile) {
615                    Ok(_) => {
616                        // File created successfully, now try to open it
617                        // Get immutable VFS reference again for opening
618                        let vfs_guard = task.vfs.read();
619                        let vfs = vfs_guard.as_deref().unwrap();
620                        match vfs.open_from(&base_entry, &base_mount, &mapped_path, flags as u32) {
621                            Ok(obj) => obj,
622                            Err(err) => return errno::to_result(errno::from_fs_error(&err)),
623                        }
624                    }
625                    Err(create_err) => {
626                        // Check if file already exists and O_EXCL is set
627                        if flags & O_EXCL != 0
628                            && create_err.kind == crate::fs::FileSystemErrorKind::AlreadyExists
629                        {
630                            return errno::to_result(errno::EEXIST); // File exists and O_EXCL is set
631                        }
632                        // Try to open the existing file
633                        let vfs_guard = task.vfs.read();
634                        let vfs = vfs_guard.as_deref().unwrap();
635                        let reopen_flags = (flags as u32) & !((O_CREAT | O_EXCL) as u32);
636                        match vfs.open_from(&base_entry, &base_mount, &mapped_path, reopen_flags) {
637                            Ok(obj) => obj,
638                            Err(open_err) => {
639                                return errno::to_result(errno::from_fs_error(&open_err));
640                            }
641                        }
642                    }
643                }
644            } else {
645                return errno::to_result(errno::from_fs_error(&e)); // Return appropriate error based on VFS error
646            }
647        }
648    };
649
650    // Post-open flag handling (O_DIRECTORY, O_TRUNC, O_APPEND)
651    if let Some(file_obj) = kernel_obj.as_file() {
652        if (flags & O_DIRECTORY) != 0 {
653            if let Ok(meta) = file_obj.metadata() {
654                if !matches!(meta.file_type, FileType::Directory) {
655                    return errno::to_result(errno::ENOTDIR);
656                }
657            }
658        }
659        if (flags & O_TRUNC) != 0 {
660            let _ = file_obj.truncate(0);
661        }
662        if (flags & O_APPEND) != 0 {
663            let _ = file_obj.seek(SeekFrom::End(0));
664        }
665    }
666
667    // Register the file with the task using HandleTable
668    let handle = task.handle_table.insert(kernel_obj);
669    match handle {
670        Ok(handle) => {
671            match abi.allocate_fd(handle as u32) {
672                Ok(fd) => {
673                    // crate::println!("sys_openat: allocated fd {} for '{}'", fd, path_str);
674                    // Initialize file status flags (e.g., O_NONBLOCK) from open flags
675                    let mut status_flags: u32 = 0;
676                    if (flags & O_NONBLOCK) != 0 {
677                        status_flags |= O_NONBLOCK as u32;
678                    }
679                    let _ = abi.set_file_status_flags(fd, status_flags);
680
681                    // Propagate non-blocking to the underlying object Selectable if available
682                    if let Some(obj) = task.handle_table.get(handle) {
683                        if let Some(sel) = obj.as_selectable() {
684                            sel.set_nonblocking(((status_flags as i32) & O_NONBLOCK) != 0);
685                        }
686                    }
687                    fd
688                }
689                Err(_) => errno::to_result(errno::EMFILE), // Too many open files
690            }
691        }
692        Err(_) => errno::to_result(errno::ENFILE), // Handle table full
693    }
694}
695
696pub fn sys_dup(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
697    let task = mytask().unwrap();
698    let fd = trapframe.get_arg(0) as usize;
699    trapframe.increment_pc_next(task);
700
701    // Get handle from Linux fd
702    if let Some(old_handle) = abi.get_handle(fd) {
703        // Use clone_for_dup to get proper dup() semantics (increments Pipe reader/writer counts etc.)
704        if let Some(kernel_obj) = task.handle_table.clone_for_dup(old_handle) {
705            let handle = task.handle_table.insert(kernel_obj);
706            match handle {
707                Ok(new_handle) => {
708                    match abi.allocate_fd(new_handle as u32) {
709                        Ok(fd) => fd,
710                        Err(_) => errno::to_result(errno::EMFILE), // Too many open files
711                    }
712                }
713                Err(_) => errno::to_result(errno::ENFILE), // Handle table full
714            }
715        } else {
716            errno::to_result(errno::EBADF) // Handle not found in handle table
717        }
718    } else {
719        errno::to_result(errno::EBADF) // Invalid file descriptor
720    }
721}
722
723pub fn sys_dup3(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
724    let task = mytask().unwrap();
725    let oldfd = trapframe.get_arg(0) as usize;
726    let newfd = trapframe.get_arg(1) as usize;
727    let flags = trapframe.get_arg(2) as u32;
728    trapframe.increment_pc_next(task);
729
730    // dup3 does not allow oldfd and newfd to be the same
731    if oldfd == newfd {
732        return usize::MAX; // EINVAL
733    }
734
735    // Only O_CLOEXEC flag is valid for dup3
736    if flags != 0 && flags != (O_CLOEXEC as u32) {
737        return usize::MAX; // EINVAL
738    }
739
740    // Get handle from old fd
741    if let Some(old_handle) = abi.get_handle(oldfd) {
742        // Use clone_for_dup to get proper dup() semantics (increments Pipe reader/writer counts etc.)
743        if let Some(kernel_obj) = task.handle_table.clone_for_dup(old_handle) {
744            let handle = task.handle_table.insert(kernel_obj);
745            match handle {
746                Ok(new_handle) => {
747                    // Close newfd if it's already open
748                    if abi.get_handle(newfd).is_some() {
749                        if let Some(old_new_handle) = abi.remove_fd(newfd) {
750                            let _ = task.handle_table.remove(old_new_handle);
751                        }
752                    }
753
754                    // Allocate specific fd
755                    match abi.allocate_specific_fd(newfd, new_handle as u32) {
756                        Ok(()) => {
757                            // Set flags if O_CLOEXEC is specified
758                            if flags & (O_CLOEXEC as u32) != 0 {
759                                let _ = abi.set_fd_flags(newfd, FD_CLOEXEC);
760                            }
761                            newfd
762                        }
763                        Err(_) => usize::MAX, // Cannot allocate specific fd
764                    }
765                }
766                Err(_) => usize::MAX, // Handle table full
767            }
768        } else {
769            usize::MAX // Handle not found in handle table
770        }
771    } else {
772        usize::MAX // Invalid old file descriptor
773    }
774}
775
776pub fn sys_close(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
777    let task = mytask().unwrap();
778    let fd = trapframe.get_arg(0) as usize;
779    trapframe.increment_pc_next(task);
780
781    // Get handle from Linux fd and remove mapping
782    if let Some(handle) = abi.remove_fd(fd) {
783        if task.handle_table.remove(handle).is_some() {
784            0 // Success
785        } else {
786            usize::MAX // Handle not found in handle table
787        }
788    } else {
789        usize::MAX // Invalid file descriptor
790    }
791}
792
793pub fn sys_read(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
794    let task = mytask().unwrap();
795    let fd = trapframe.get_arg(0) as usize;
796    let buf_ptr = task
797        .vm_manager
798        .translate_vaddr(trapframe.get_arg(1))
799        .unwrap() as *mut u8;
800    let count = trapframe.get_arg(2) as usize;
801
802    // Get handle from Linux fd
803    let handle = match abi.get_handle(fd) {
804        Some(h) => h,
805        None => {
806            trapframe.increment_pc_next(task);
807            return usize::MAX; // Invalid file descriptor
808        }
809    };
810
811    let kernel_obj = match task.handle_table.get(handle) {
812        Some(obj) => obj,
813        None => {
814            trapframe.increment_pc_next(task);
815            return usize::MAX; // Invalid file descriptor
816        }
817    };
818
819    // Determine non-blocking mode
820    let nonblocking = abi
821        .get_file_status_flags(fd)
822        .map(|f| ((f as i32) & O_NONBLOCK) != 0)
823        .unwrap_or(false);
824
825    // Check if this is a directory by getting file metadata
826    let is_directory = if let Some(file_obj) = kernel_obj.as_file() {
827        if let Ok(metadata) = file_obj.metadata() {
828            matches!(metadata.file_type, FileType::Directory)
829        } else {
830            false
831        }
832    } else {
833        false
834    };
835
836    let stream = match kernel_obj.as_stream() {
837        Some(stream) => stream,
838        None => {
839            trapframe.increment_pc_next(task);
840            return usize::MAX; // Not a stream object
841        }
842    };
843
844    if is_directory {
845        // // For directories, we need a larger buffer to read DirectoryEntry, then convert to Dirent
846        // let directory_entry_size = core::mem::size_of::<DirectoryEntry>();
847        // let mut temp_buffer = vec![0u8; directory_entry_size];
848
849        // match stream.read(&mut temp_buffer) {
850        //     Ok(n) => {
851        //         trapframe.increment_pc_next(task); // Increment PC to avoid infinite loop
852        //         if n > 0 && n >= directory_entry_size {
853        //             // Convert DirectoryEntry to Linux Dirent
854        //             let converted_bytes = read_directory_as_Linux_dirent(buf_ptr, count, &temp_buffer[..n]);
855        //             if converted_bytes > 0 {
856        //                 return converted_bytes; // Return converted Linux dirent size
857        //             }
858        //         }
859        //         0 // EOF or no valid directory entry
860        //     },
861        //     Err(e) => {
862        //         match e {
863        //             StreamError::EndOfStream => {
864        //                 trapframe.increment_pc_next(task); // Increment PC to avoid infinite loop
865        //                 0 // EOF
866        //             },
867        //             StreamError::WouldBlock => {
868        //                 // If the stream would block, we need to set the trapframe's EPC
869        //                 // trapframe.epc = epc;
870        //                 // task.vcpu.store(trapframe); // Store the trapframe in the task's vcpu
871        //                 get_scheduler().schedule(trapframe); // Yield to the scheduler
872        //             },
873        //             _ => {
874        //                 trapframe.increment_pc_next(task);
875        //                 usize::MAX // Other errors
876        //             }
877        //         }
878        //     }
879        // }
880        trapframe.increment_pc_next(task);
881        return usize::MAX; // Directory reading not implemented yet
882    } else {
883        // For regular files, use the user-provided buffer directly
884        let mut buffer = unsafe { core::slice::from_raw_parts_mut(buf_ptr, count) };
885
886        match stream.read(&mut buffer) {
887            Ok(n) => {
888                trapframe.increment_pc_next(task); // Increment PC to avoid infinite loop
889                n
890            } // Return original read size for regular files
891            Err(e) => {
892                match e {
893                    StreamError::EndOfStream => {
894                        trapframe.increment_pc_next(task); // Increment PC to avoid infinite loop
895                        0 // EOF
896                    }
897                    StreamError::WouldBlock => {
898                        if nonblocking {
899                            trapframe.increment_pc_next(task);
900                            return errno::to_result(errno::EAGAIN);
901                        } else {
902                            get_scheduler().schedule(trapframe); // Yield to the scheduler
903                            usize::MAX // Unreachable, but needed to satisfy return type
904                        }
905                    }
906                    _ => {
907                        // Other errors, return -1
908                        trapframe.increment_pc_next(task); // Increment PC to avoid infinite loop
909                        usize::MAX
910                    }
911                }
912            }
913        }
914    }
915}
916
917pub fn sys_write(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
918    let task = mytask().unwrap();
919    let fd = trapframe.get_arg(0) as usize;
920    let buf_ptr = task
921        .vm_manager
922        .translate_vaddr(trapframe.get_arg(1))
923        .unwrap() as *const u8;
924    let count = trapframe.get_arg(2) as usize;
925
926    // Increment PC to avoid infinite loop if write fails
927    trapframe.increment_pc_next(task);
928
929    // Get handle from Linux fd
930    let handle = match abi.get_handle(fd) {
931        Some(h) => h,
932        None => return usize::MAX, // Invalid file descriptor
933    };
934
935    let kernel_obj = match task.handle_table.get(handle) {
936        Some(obj) => obj,
937        None => return usize::MAX, // Invalid file descriptor
938    };
939
940    let stream = match kernel_obj.as_stream() {
941        Some(stream) => stream,
942        None => return usize::MAX, // Not a stream object
943    };
944
945    // Determine non-blocking mode
946    let nonblocking = abi
947        .get_file_status_flags(fd)
948        .map(|f| ((f as i32) & O_NONBLOCK) != 0)
949        .unwrap_or(false);
950
951    let buffer = unsafe { core::slice::from_raw_parts(buf_ptr, count) };
952
953    match stream.write(buffer) {
954        Ok(n) => n,
955        Err(StreamError::WouldBlock) => {
956            if nonblocking {
957                return errno::to_result(errno::EAGAIN);
958            } else {
959                get_scheduler().schedule(trapframe);
960                usize::MAX
961            }
962        }
963        Err(_) => usize::MAX, // Write error
964    }
965}
966
967pub fn sys_pread64(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
968    let task = mytask().unwrap();
969    let fd = trapframe.get_arg(0) as usize;
970    let buf_addr = trapframe.get_arg(1);
971    let count = trapframe.get_arg(2) as usize;
972    let position = trapframe.get_arg(3) as i64;
973
974    if position < 0 {
975        trapframe.increment_pc_next(task);
976        return errno::to_result(errno::EINVAL);
977    }
978
979    if count == 0 {
980        trapframe.increment_pc_next(task);
981        return 0;
982    }
983
984    let buf_ptr = match task.vm_manager.translate_vaddr(buf_addr) {
985        Some(ptr) => ptr as *mut u8,
986        None => {
987            trapframe.increment_pc_next(task);
988            return errno::to_result(errno::EFAULT);
989        }
990    };
991
992    if buf_ptr.is_null() {
993        trapframe.increment_pc_next(task);
994        return errno::to_result(errno::EFAULT);
995    }
996
997    let handle = match abi.get_handle(fd) {
998        Some(h) => h,
999        None => {
1000            trapframe.increment_pc_next(task);
1001            return errno::to_result(errno::EBADF);
1002        }
1003    };
1004
1005    let kernel_obj = match task.handle_table.get(handle) {
1006        Some(obj) => obj,
1007        None => {
1008            trapframe.increment_pc_next(task);
1009            return errno::to_result(errno::EBADF);
1010        }
1011    };
1012
1013    let file = match kernel_obj.as_file() {
1014        Some(file) => file,
1015        None => {
1016            trapframe.increment_pc_next(task);
1017            if kernel_obj.as_stream().is_some() {
1018                return errno::to_result(errno::ESPIPE);
1019            }
1020            return errno::to_result(errno::EBADF);
1021        }
1022    };
1023
1024    let mut buffer = unsafe { core::slice::from_raw_parts_mut(buf_ptr, count) };
1025
1026    let nonblocking = abi
1027        .get_file_status_flags(fd)
1028        .map(|f| ((f as i32) & O_NONBLOCK) != 0)
1029        .unwrap_or(false);
1030
1031    match file.read_at(position as u64, &mut buffer) {
1032        Ok(n) => {
1033            trapframe.increment_pc_next(task);
1034            n
1035        }
1036        Err(StreamError::EndOfStream) => {
1037            trapframe.increment_pc_next(task);
1038            0
1039        }
1040        Err(StreamError::WouldBlock) => {
1041            if nonblocking {
1042                trapframe.increment_pc_next(task);
1043                errno::to_result(errno::EAGAIN)
1044            } else {
1045                get_scheduler().schedule(trapframe);
1046                usize::MAX
1047            }
1048        }
1049        Err(err) => {
1050            trapframe.increment_pc_next(task);
1051            errno::to_result(stream_error_to_errno(err))
1052        }
1053    }
1054}
1055
1056pub fn sys_pwrite64(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1057    let task = mytask().unwrap();
1058    let fd = trapframe.get_arg(0) as usize;
1059    let buf_addr = trapframe.get_arg(1);
1060    let count = trapframe.get_arg(2) as usize;
1061    let position = trapframe.get_arg(3) as i64;
1062
1063    if position < 0 {
1064        trapframe.increment_pc_next(task);
1065        return errno::to_result(errno::EINVAL);
1066    }
1067
1068    if count == 0 {
1069        trapframe.increment_pc_next(task);
1070        return 0;
1071    }
1072
1073    let buf_ptr = match task.vm_manager.translate_vaddr(buf_addr) {
1074        Some(ptr) => ptr as *const u8,
1075        None => {
1076            trapframe.increment_pc_next(task);
1077            return errno::to_result(errno::EFAULT);
1078        }
1079    };
1080
1081    if buf_ptr.is_null() {
1082        trapframe.increment_pc_next(task);
1083        return errno::to_result(errno::EFAULT);
1084    }
1085
1086    let handle = match abi.get_handle(fd) {
1087        Some(h) => h,
1088        None => {
1089            trapframe.increment_pc_next(task);
1090            return errno::to_result(errno::EBADF);
1091        }
1092    };
1093
1094    let kernel_obj = match task.handle_table.get(handle) {
1095        Some(obj) => obj,
1096        None => {
1097            trapframe.increment_pc_next(task);
1098            return errno::to_result(errno::EBADF);
1099        }
1100    };
1101
1102    let file = match kernel_obj.as_file() {
1103        Some(file) => file,
1104        None => {
1105            trapframe.increment_pc_next(task);
1106            if kernel_obj.as_stream().is_some() {
1107                return errno::to_result(errno::ESPIPE);
1108            }
1109            return errno::to_result(errno::EBADF);
1110        }
1111    };
1112
1113    let buffer = unsafe { core::slice::from_raw_parts(buf_ptr, count) };
1114
1115    let nonblocking = abi
1116        .get_file_status_flags(fd)
1117        .map(|f| ((f as i32) & O_NONBLOCK) != 0)
1118        .unwrap_or(false);
1119
1120    match file.write_at(position as u64, buffer) {
1121        Ok(n) => {
1122            trapframe.increment_pc_next(task);
1123            n
1124        }
1125        Err(StreamError::WouldBlock) => {
1126            if nonblocking {
1127                trapframe.increment_pc_next(task);
1128                errno::to_result(errno::EAGAIN)
1129            } else {
1130                get_scheduler().schedule(trapframe);
1131                usize::MAX
1132            }
1133        }
1134        Err(err) => {
1135            trapframe.increment_pc_next(task);
1136            errno::to_result(stream_error_to_errno(err))
1137        }
1138    }
1139}
1140
1141fn stream_error_to_errno(err: StreamError) -> usize {
1142    match err {
1143        StreamError::EndOfStream => errno::SUCCESS,
1144        StreamError::WouldBlock => errno::EAGAIN,
1145        StreamError::IoError => errno::EIO,
1146        StreamError::Closed => errno::EBADF,
1147        StreamError::InvalidArgument => errno::EINVAL,
1148        StreamError::Interrupted => errno::EINTR,
1149        StreamError::PermissionDenied => errno::EACCES,
1150        StreamError::DeviceError => errno::EIO,
1151        StreamError::NotSupported | StreamError::SeekError => errno::ESPIPE,
1152        StreamError::NoSpace => errno::ENOSPC,
1153        StreamError::BrokenPipe => errno::EPIPE,
1154        StreamError::FileSystemError(fs_err) => errno::from_fs_error(&fs_err),
1155        StreamError::Other(_) => errno::EIO,
1156    }
1157}
1158
1159/// Linux writev system call implementation
1160///
1161/// This system call writes data from multiple buffers (I/O vectors) to a file descriptor.
1162/// It provides scatter-gather I/O functionality, allowing efficient writes from multiple
1163/// non-contiguous memory regions in a single system call.
1164///
1165/// # Arguments
1166/// - fd: File descriptor
1167/// - iovec: Array of iovec structures describing the buffers
1168/// - iovcnt: Number of iovec structures in the array
1169///
1170/// # Returns
1171/// - Number of bytes written on success
1172/// - usize::MAX on error
1173pub fn sys_writev(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1174    let task = mytask().unwrap();
1175    let fd = trapframe.get_arg(0) as usize;
1176    let iovec_ptr = trapframe.get_arg(1);
1177    let iovcnt = trapframe.get_arg(2) as usize;
1178
1179    // Increment PC to avoid infinite loop if writev fails
1180    trapframe.increment_pc_next(task);
1181
1182    // Validate parameters
1183    if iovcnt == 0 {
1184        return 0; // Nothing to write
1185    }
1186
1187    // Linux typically limits iovcnt to prevent resource exhaustion
1188    const IOV_MAX: usize = 1024;
1189    if iovcnt > IOV_MAX {
1190        return usize::MAX; // Too many vectors
1191    }
1192
1193    // Get handle from Linux fd
1194    let handle = match abi.get_handle(fd) {
1195        Some(h) => h,
1196        None => return usize::MAX, // Invalid file descriptor
1197    };
1198
1199    let kernel_obj = match task.handle_table.get(handle) {
1200        Some(obj) => obj,
1201        None => return usize::MAX, // Invalid file descriptor
1202    };
1203
1204    let stream = match kernel_obj.as_stream() {
1205        Some(stream) => stream,
1206        None => return usize::MAX, // Not a stream object
1207    };
1208
1209    let nonblocking = abi
1210        .get_file_status_flags(fd)
1211        .map(|f| ((f as i32) & O_NONBLOCK) != 0)
1212        .unwrap_or(false);
1213
1214    // Translate and validate iovec array pointer
1215    let iovec_vaddr = match task.vm_manager.translate_vaddr(iovec_ptr) {
1216        Some(addr) => addr as *const IoVec,
1217        None => return usize::MAX, // Invalid address
1218    };
1219
1220    if iovec_vaddr.is_null() {
1221        return usize::MAX; // NULL pointer
1222    }
1223
1224    // Read iovec structures from user space
1225    let iovecs = unsafe { core::slice::from_raw_parts(iovec_vaddr, iovcnt) };
1226
1227    let mut total_written = 0usize;
1228
1229    // Process each iovec
1230    for iovec in iovecs {
1231        if iovec.iov_len == 0 {
1232            continue; // Skip empty buffers
1233        }
1234
1235        // Translate buffer address
1236        let buf_vaddr = match task.vm_manager.translate_vaddr(iovec.iov_base as usize) {
1237            Some(addr) => addr as *const u8,
1238            None => return usize::MAX, // Invalid buffer address
1239        };
1240
1241        if buf_vaddr.is_null() {
1242            return usize::MAX; // NULL buffer pointer
1243        }
1244
1245        // Create a slice from the user buffer
1246        let buffer = unsafe { core::slice::from_raw_parts(buf_vaddr, iovec.iov_len) };
1247
1248        // Write data from this buffer
1249        match stream.write(buffer) {
1250            Ok(n) => {
1251                total_written = total_written.saturating_add(n);
1252
1253                // If partial write occurred, stop processing remaining vectors
1254                // This matches Linux behavior for writev
1255                if n < iovec.iov_len {
1256                    break;
1257                }
1258            }
1259            Err(StreamError::WouldBlock) => {
1260                if nonblocking {
1261                    // If some bytes were written, return them; otherwise, EAGAIN
1262                    if total_written == 0 {
1263                        return errno::to_result(errno::EAGAIN);
1264                    } else {
1265                        break;
1266                    }
1267                } else {
1268                    get_scheduler().schedule(trapframe);
1269                    return usize::MAX;
1270                }
1271            }
1272            Err(_) => {
1273                // If no bytes were written at all, return error
1274                // If some bytes were written, return the count
1275                if total_written == 0 {
1276                    return usize::MAX;
1277                } else {
1278                    break;
1279                }
1280            }
1281        }
1282    }
1283
1284    total_written
1285}
1286
1287pub fn sys_lseek(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1288    let task = mytask().unwrap();
1289    let fd = trapframe.get_arg(0) as usize;
1290    let offset = trapframe.get_arg(1) as i64;
1291    let whence = trapframe.get_arg(2) as i32;
1292
1293    // Increment PC to avoid infinite loop if lseek fails
1294    trapframe.increment_pc_next(task);
1295
1296    // Get handle from Linux fd
1297    let handle = match abi.get_handle(fd) {
1298        Some(h) => h,
1299        None => return usize::MAX, // Invalid file descriptor
1300    };
1301
1302    let kernel_obj = match task.handle_table.get(handle) {
1303        Some(obj) => obj,
1304        None => return usize::MAX, // Invalid file descriptor
1305    };
1306
1307    let file = match kernel_obj.as_file() {
1308        Some(file) => file,
1309        None => return usize::MAX, // Not a file object
1310    };
1311
1312    let whence = match whence {
1313        0 => SeekFrom::Start(offset as u64),
1314        1 => SeekFrom::Current(offset),
1315        2 => SeekFrom::End(offset),
1316        _ => return usize::MAX, // Invalid whence
1317    };
1318
1319    match file.seek(whence) {
1320        Ok(pos) => pos as usize,
1321        Err(e) => {
1322            crate::println!("sys_lseek: seek error: {:?}", e);
1323            usize::MAX // Seek error
1324        }
1325    }
1326}
1327
1328// // Create device file
1329// pub fn sys_mknod(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1330//     let task = mytask().unwrap();
1331//     trapframe.increment_pc_next(task);
1332//     let name_ptr = task.vm_manager.translate_vaddr(trapframe.get_arg(0)).unwrap() as *const u8;
1333//     let name = get_path_str_v2(name_ptr).unwrap();
1334//     let path = to_absolute_path_v2(&task, &name).unwrap();
1335
1336//     let major = trapframe.get_arg(1) as u32;
1337//     let minor = trapframe.get_arg(2) as u32;
1338
1339//     match (major, minor) {
1340//         (1, 0) => {
1341//             // Create a console device
1342//             let console_dev = Some(DeviceManager::get_manager().register_device(Arc::new(
1343//                 crate::abi::Linux::drivers::console::ConsoleDevice::new(0, "console")
1344//             )));
1345
1346//             let vfs = task.vfs.read().clone().unwrap();
1347//             let _res = vfs.create_file(&path, FileType::CharDevice(
1348//                 DeviceFileInfo {
1349//                     device_id: console_dev.unwrap(),
1350//                     device_type: crate::device::DeviceType::Char,
1351//                 }
1352//             ));
1353//             // crate::println!("Created console device at {}", path);
1354//         },
1355//         _ => {},
1356//     }
1357//     0
1358// }
1359
1360// pub fn sys_fstat(abi: &mut LinuxRiscv64Abi, trapframe: &mut crate::arch::Trapframe) -> usize {
1361//     let fd = trapframe.get_arg(0) as usize;
1362
1363//     let task = mytask()
1364//         .expect("sys_fstat: No current task found");
1365//     trapframe.increment_pc_next(task); // Increment the program counter
1366
1367//     let stat_ptr = task.vm_manager.translate_vaddr(trapframe.get_arg(1) as usize)
1368//         .expect("sys_fstat: Failed to translate stat pointer") as *mut Stat;
1369
1370//     // Get handle from Linux fd
1371//     let handle = match abi.get_handle(fd) {
1372//         Some(h) => h,
1373//         None => return usize::MAX, // Invalid file descriptor
1374//     };
1375
1376//     let kernel_obj = match task.handle_table.get(handle) {
1377//         Some(obj) => obj,
1378//         None => return usize::MAX, // Return -1 on error
1379//     };
1380
1381//     let file = match kernel_obj.as_file() {
1382//         Some(file) => file,
1383//         None => return usize::MAX, // Not a file object
1384//     };
1385
1386//     let metadata = file.metadata()
1387//         .expect("sys_fstat: Failed to get file metadata");
1388
1389//     if stat_ptr.is_null() {
1390//         return usize::MAX; // Return -1 if stat pointer is null
1391//     }
1392
1393//     let stat = unsafe { &mut *stat_ptr };
1394
1395//     *stat = Stat {
1396//         dev: 0,
1397//         ino: metadata.file_id as u32,
1398//         file_type: match metadata.file_type {
1399//             FileType::Directory => 1, // T_DIR
1400//             FileType::RegularFile => 2,      // T_FILE
1401//             FileType::CharDevice(_) => 3, // T_DEVICE
1402//             FileType::BlockDevice(_) => 3, // T_DEVICE
1403//             _ => 0, // Unknown type
1404//         },
1405//         nlink: 1,
1406//         size: metadata.size as u64,
1407//     };
1408
1409//     0
1410// }
1411
1412/// Linux sys_newfstatat implementation for Scarlet VFS v2
1413///
1414/// Gets file status relative to a directory file descriptor (dirfd) and path.
1415/// If dirfd == AT_FDCWD, uses the current working directory as the base.
1416/// Otherwise, resolves the base directory from the file descriptor.
1417/// Uses VfsManager::resolve_path_from for safe and efficient path resolution.
1418///
1419/// Arguments:
1420/// - abi: LinuxRiscv64Abi context
1421/// - trapframe: Trapframe containing syscall arguments
1422///
1423/// Returns:
1424/// - 0 on success
1425/// - usize::MAX (Linux -1) on error
1426pub fn sys_newfstatat(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1427    let task = mytask().unwrap();
1428    let dirfd = trapframe.get_arg(0) as i32;
1429    let path_ptr = task
1430        .vm_manager
1431        .translate_vaddr(trapframe.get_arg(1))
1432        .unwrap() as *const u8;
1433    let stat_ptr = task
1434        .vm_manager
1435        .translate_vaddr(trapframe.get_arg(2))
1436        .unwrap() as *mut u8;
1437    let flags = trapframe.get_arg(3) as i32;
1438
1439    // Increment PC to avoid infinite loop if fstatat fails
1440    trapframe.increment_pc_next(task);
1441
1442    // Parse path from user space
1443    let path_str = match cstring_to_string(path_ptr, MAX_PATH_LENGTH) {
1444        Ok((path, _)) => path,
1445        Err(_) => return usize::MAX, // Invalid UTF-8
1446    };
1447
1448    let path_str = remap_shm_path(&path_str);
1449
1450    // crate::println!(
1451    //     "sys_newfstatat: dirfd={}, path='{}', flags={:#o}",
1452    //     dirfd,
1453    //     path_str,
1454    //     flags
1455    // );
1456
1457    let vfs_guard = task.vfs.read();
1458    let vfs = vfs_guard.as_deref().unwrap();
1459
1460    // Determine base directory (entry and mount) for path resolution
1461    use crate::fs::vfs_v2::core::VfsFileObject;
1462
1463    const AT_FDCWD: i32 = -100;
1464    const AT_SYMLINK_NOFOLLOW: i32 = 0x100;
1465
1466    // TODO: Handle AT_SYMLINK_NOFOLLOW flag properly
1467    // For now, we always follow symbolic links
1468    let _follow_symlinks = (flags & AT_SYMLINK_NOFOLLOW) == 0;
1469
1470    let (base_entry, base_mount) = if dirfd == AT_FDCWD {
1471        // Use current working directory as base
1472        vfs.get_cwd().unwrap_or_else(|| {
1473            let root_mount = vfs.mount_tree.root_mount.read().clone();
1474            (root_mount.root.clone(), root_mount)
1475        })
1476    } else {
1477        // Use directory file descriptor as base
1478        let handle = match abi.get_handle(dirfd as usize) {
1479            Some(h) => h,
1480            None => return usize::MAX,
1481        };
1482        let kernel_obj = match task.handle_table.get(handle) {
1483            Some(obj) => obj,
1484            None => return usize::MAX,
1485        };
1486        let file_obj = match kernel_obj.as_file() {
1487            Some(f) => f,
1488            None => return usize::MAX,
1489        };
1490        let vfs_file_obj = file_obj
1491            .as_any()
1492            .downcast_ref::<VfsFileObject>()
1493            .ok_or(())
1494            .unwrap();
1495        (
1496            vfs_file_obj.get_vfs_entry().clone(),
1497            vfs_file_obj.get_mount_point().clone(),
1498        )
1499    };
1500
1501    // Resolve the path from the base directory
1502    match vfs.resolve_path_from(&base_entry, &base_mount, &path_str) {
1503        Ok((entry, _mount_point)) => {
1504            // Get metadata from the resolved VfsEntry
1505            let node = entry.node();
1506            match node.metadata() {
1507                Ok(metadata) => {
1508                    if stat_ptr.is_null() {
1509                        return usize::MAX; // Return -1 if stat pointer is null
1510                    }
1511
1512                    let stat = unsafe { &mut *(stat_ptr as *mut LinuxStat) };
1513                    *stat = LinuxStat::from_metadata(&metadata);
1514                    // crate::println!(
1515                    //     "sys_newfstatat: path='{}' size={}",
1516                    //     path_str,
1517                    //     metadata.size
1518                    // );
1519                    0 // Success
1520                }
1521                Err(_) => usize::MAX, // Error getting metadata
1522            }
1523        }
1524        Err(_) => usize::MAX, // Error resolving path
1525    }
1526}
1527
1528/// Linux sys_statx implementation for Scarlet VFS v2
1529///
1530/// Gets extended file status relative to a directory file descriptor (dirfd) and path.
1531/// If dirfd == AT_FDCWD, uses the current working directory as the base.
1532/// Supports AT_EMPTY_PATH for file-descriptor based queries.
1533pub fn sys_statx(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1534    let task = match mytask() {
1535        Some(t) => t,
1536        None => return errno::to_result(errno::EIO),
1537    };
1538
1539    let dirfd = trapframe.get_arg(0) as i32;
1540    let pathname_ptr = trapframe.get_arg(1);
1541    let flags = trapframe.get_arg(2) as u32;
1542    let mask = trapframe.get_arg(3) as u32;
1543    let statx_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(4)) {
1544        Some(ptr) => ptr as *mut LinuxStatx,
1545        None => return errno::to_result(errno::EFAULT),
1546    };
1547
1548    // Increment PC to avoid infinite loop
1549    trapframe.increment_pc_next(task);
1550
1551    if statx_ptr.is_null() {
1552        return errno::to_result(errno::EFAULT);
1553    }
1554
1555    const AT_FDCWD: i32 = -100;
1556    const AT_SYMLINK_NOFOLLOW: u32 = 0x100;
1557    const AT_EMPTY_PATH: u32 = 0x1000;
1558
1559    let path_str = match parse_c_string_from_userspace(task, pathname_ptr, MAX_PATH_LENGTH) {
1560        Ok(s) => s,
1561        Err(_) => return errno::to_result(errno::EFAULT),
1562    };
1563    let path_str = remap_shm_path(&path_str);
1564    // crate::println!(
1565    //     "sys_statx: dirfd={} path='{}' flags={:#x} mask={:#x}",
1566    //     dirfd,
1567    //     path_str,
1568    //     flags,
1569    //     mask
1570    // );
1571
1572    let vfs = match task.vfs.read().clone() {
1573        Some(v) => v,
1574        None => return errno::to_result(errno::EIO),
1575    };
1576
1577    // Support AT_EMPTY_PATH for stat-by-fd
1578    if path_str.is_empty() && (flags & AT_EMPTY_PATH) != 0 {
1579        let handle = match abi.get_handle(dirfd as usize) {
1580            Some(h) => h,
1581            None => return errno::to_result(errno::EBADF),
1582        };
1583        let kernel_obj = match task.handle_table.get(handle) {
1584            Some(obj) => obj,
1585            None => return errno::to_result(errno::EBADF),
1586        };
1587        let file_obj = match kernel_obj.as_file() {
1588            Some(f) => f,
1589            None => return errno::to_result(errno::EBADF),
1590        };
1591
1592        use crate::fs::vfs_v2::core::VfsFileObject;
1593        if let Some(vfs_file_obj) = file_obj.as_any().downcast_ref::<VfsFileObject>() {
1594            let entry = vfs_file_obj.get_vfs_entry();
1595            let node = entry.node();
1596            let metadata = match node.metadata() {
1597                Ok(m) => m,
1598                Err(e) => return errno::to_result(errno::from_fs_error(&e)),
1599            };
1600            let stat = LinuxStat::from_metadata(&metadata);
1601            let statx_ref = unsafe { &mut *statx_ptr };
1602            fill_statx_from_stat(statx_ref, &stat, metadata.created_time, mask);
1603            // crate::println!(
1604            //     "sys_statx: empty_path name='{}' size={}",
1605            //     entry.name(),
1606            //     metadata.size
1607            // );
1608            return 0;
1609        }
1610
1611        let stat = LinuxStat {
1612            st_dev: 0,
1613            st_ino: handle as u64,
1614            st_mode: S_IFCHR | 0o666,
1615            st_nlink: 1,
1616            st_uid: 0,
1617            st_gid: 0,
1618            st_rdev: handle as u64,
1619            __pad1: 0,
1620            st_size: 0,
1621            st_blksize: 4096,
1622            __pad2: 0,
1623            st_blocks: 0,
1624            st_atime: 0,
1625            st_atime_nsec: 0,
1626            st_mtime: 0,
1627            st_mtime_nsec: 0,
1628            st_ctime: 0,
1629            st_ctime_nsec: 0,
1630            __unused4: 0,
1631            __unused5: 0,
1632        };
1633        let statx_ref = unsafe { &mut *statx_ptr };
1634        fill_statx_from_stat(statx_ref, &stat, 0, mask);
1635        return 0;
1636    }
1637
1638    // Determine base directory (entry and mount) for path resolution
1639    use crate::fs::vfs_v2::core::VfsFileObject;
1640
1641    let (base_entry, base_mount) = if dirfd == AT_FDCWD {
1642        vfs.get_cwd().unwrap_or_else(|| {
1643            let root_mount = vfs.mount_tree.root_mount.read().clone();
1644            (root_mount.root.clone(), root_mount)
1645        })
1646    } else {
1647        let handle = match abi.get_handle(dirfd as usize) {
1648            Some(h) => h,
1649            None => return errno::to_result(errno::EBADF),
1650        };
1651        let kernel_obj = match task.handle_table.get(handle) {
1652            Some(obj) => obj,
1653            None => return errno::to_result(errno::EBADF),
1654        };
1655        let file_obj = match kernel_obj.as_file() {
1656            Some(f) => f,
1657            None => return errno::to_result(errno::ENOTDIR),
1658        };
1659        let vfs_file_obj = match file_obj.as_any().downcast_ref::<VfsFileObject>() {
1660            Some(vfs_obj) => vfs_obj,
1661            None => return errno::to_result(errno::ENOTDIR),
1662        };
1663        (
1664            vfs_file_obj.get_vfs_entry().clone(),
1665            vfs_file_obj.get_mount_point().clone(),
1666        )
1667    };
1668
1669    // TODO: Handle AT_SYMLINK_NOFOLLOW flag properly; for now, always follow.
1670    let _follow_symlinks = (flags & AT_SYMLINK_NOFOLLOW) == 0;
1671
1672    let (entry, _mount_point) = match vfs.resolve_path_from(&base_entry, &base_mount, &path_str) {
1673        Ok(v) => v,
1674        Err(e) => return errno::to_result(errno::from_fs_error(&e)),
1675    };
1676
1677    let node = entry.node();
1678    let metadata = match node.metadata() {
1679        Ok(m) => m,
1680        Err(e) => return errno::to_result(errno::from_fs_error(&e)),
1681    };
1682
1683    let stat = LinuxStat::from_metadata(&metadata);
1684    let statx_ref = unsafe { &mut *statx_ptr };
1685    fill_statx_from_stat(statx_ref, &stat, metadata.created_time, mask);
1686    // crate::println!(
1687    //     "sys_statx: path='{}' size={}",
1688    //     path_str,
1689    //     metadata.size
1690    // );
1691    0
1692}
1693
1694#[allow(dead_code)]
1695pub fn sys_mkdir(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1696    let task = mytask().unwrap();
1697    trapframe.increment_pc_next(task);
1698
1699    let path_ptr = task
1700        .vm_manager
1701        .translate_vaddr(trapframe.get_arg(0))
1702        .unwrap() as *const u8;
1703    let path = match get_path_str_v2(path_ptr) {
1704        Ok(p) => to_absolute_path_v2(&task, &p).unwrap(),
1705        Err(_) => return usize::MAX, // Invalid path
1706    };
1707
1708    // Try to create the directory
1709    let vfs = task.vfs.read().clone().unwrap();
1710    match vfs.create_dir(&path) {
1711        Ok(_) => 0,           // Success
1712        Err(_) => usize::MAX, // Error
1713    }
1714}
1715
1716#[allow(dead_code)]
1717pub fn sys_unlink(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1718    let task = mytask().unwrap();
1719    trapframe.increment_pc_next(task);
1720
1721    let path_ptr = task
1722        .vm_manager
1723        .translate_vaddr(trapframe.get_arg(0))
1724        .unwrap() as *const u8;
1725    let path = match cstring_to_string(path_ptr, MAX_PATH_LENGTH) {
1726        Ok((p, _)) => to_absolute_path_v2(&task, &p).unwrap(),
1727        Err(_) => return usize::MAX, // Invalid path
1728    };
1729
1730    // Try to remove the file or directory
1731    let vfs = task.vfs.read().clone().unwrap();
1732    match vfs.remove(&path) {
1733        Ok(_) => 0,           // Success
1734        Err(_) => usize::MAX, // Error
1735    }
1736}
1737
1738#[allow(dead_code)]
1739pub fn sys_link(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1740    let task = mytask().unwrap();
1741    trapframe.increment_pc_next(task);
1742
1743    let src_path_ptr = task
1744        .vm_manager
1745        .translate_vaddr(trapframe.get_arg(0))
1746        .unwrap() as *const u8;
1747    let dst_path_ptr = task
1748        .vm_manager
1749        .translate_vaddr(trapframe.get_arg(1))
1750        .unwrap() as *const u8;
1751
1752    let src_path = match cstring_to_string(src_path_ptr, MAX_PATH_LENGTH) {
1753        Ok((p, _)) => to_absolute_path_v2(&task, &p).unwrap(),
1754        Err(_) => return usize::MAX, // Invalid path
1755    };
1756
1757    let dst_path = match cstring_to_string(dst_path_ptr, MAX_PATH_LENGTH) {
1758        Ok((p, _)) => to_absolute_path_v2(&task, &p).unwrap(),
1759        Err(_) => return usize::MAX, // Invalid path
1760    };
1761
1762    let vfs_guard = task.vfs.read();
1763    let vfs = vfs_guard.as_deref().unwrap();
1764    match vfs.create_hardlink(&src_path, &dst_path) {
1765        Ok(_) => 0, // Success
1766        Err(err) => {
1767            // Map VFS errors to appropriate errno values for Linux
1768            errno::to_result(errno::from_fs_error(&err))
1769        }
1770    }
1771}
1772
1773/// Linux sys_linkat implementation for Scarlet VFS v2
1774///
1775/// Creates a hard link to an existing file. Both oldpath and newpath
1776/// can be relative to their respective directory file descriptors.
1777///
1778/// Arguments:
1779/// - abi: LinuxRiscv64Abi context
1780/// - trapframe: Trapframe containing syscall arguments
1781///   - arg0: olddirfd (old directory file descriptor)
1782///   - arg1: oldpath_ptr (pointer to source path string)
1783///   - arg2: newdirfd (new directory file descriptor)
1784///   - arg3: newpath_ptr (pointer to destination path string)
1785///   - arg4: flags (link flags)
1786///
1787/// Returns:
1788/// - 0 on success
1789/// - usize::MAX (Linux -1) on error
1790pub fn sys_linkat(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1791    let task = match mytask() {
1792        Some(t) => t,
1793        None => return usize::MAX,
1794    };
1795
1796    let olddirfd = trapframe.get_arg(0) as i32;
1797    let oldpath_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(1)) {
1798        Some(ptr) => ptr as *const u8,
1799        None => return usize::MAX,
1800    };
1801    let newdirfd = trapframe.get_arg(2) as i32;
1802    let newpath_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(3)) {
1803        Some(ptr) => ptr as *const u8,
1804        None => return usize::MAX,
1805    };
1806    let flags = trapframe.get_arg(4) as i32;
1807
1808    // Increment PC to avoid infinite loop
1809    trapframe.increment_pc_next(task);
1810
1811    // Parse paths from user space
1812    let oldpath_str = match cstring_to_string(oldpath_ptr, MAX_PATH_LENGTH) {
1813        Ok((path, _)) => path,
1814        Err(_) => return usize::MAX, // Invalid UTF-8
1815    };
1816
1817    let newpath_str = match cstring_to_string(newpath_ptr, MAX_PATH_LENGTH) {
1818        Ok((path, _)) => path,
1819        Err(_) => return usize::MAX, // Invalid UTF-8
1820    };
1821
1822    // Linux constants for linkat
1823    const AT_FDCWD: i32 = -100;
1824    const AT_SYMLINK_FOLLOW: i32 = 0x400;
1825    const AT_EMPTY_PATH: i32 = 0x1000;
1826
1827    let vfs = match task.vfs.read().clone() {
1828        Some(v) => v,
1829        None => return usize::MAX,
1830    };
1831
1832    // Determine base directory for old path resolution
1833    use crate::fs::vfs_v2::core::VfsFileObject;
1834
1835    let (old_base_entry, old_base_mount) = if olddirfd == AT_FDCWD {
1836        // Use current working directory as base
1837        vfs.get_cwd().unwrap_or_else(|| {
1838            let root_mount = vfs.mount_tree.root_mount.read().clone();
1839            (root_mount.root.clone(), root_mount)
1840        })
1841    } else {
1842        // Use directory file descriptor as base
1843        let handle = match abi.get_handle(olddirfd as usize) {
1844            Some(h) => h,
1845            None => return usize::MAX,
1846        };
1847        let kernel_obj = match task.handle_table.get(handle) {
1848            Some(obj) => obj,
1849            None => return usize::MAX,
1850        };
1851        let file_obj = match kernel_obj.as_file() {
1852            Some(f) => f,
1853            None => return usize::MAX,
1854        };
1855        let vfs_file_obj = file_obj
1856            .as_any()
1857            .downcast_ref::<VfsFileObject>()
1858            .ok_or(())
1859            .unwrap();
1860        (
1861            vfs_file_obj.get_vfs_entry().clone(),
1862            vfs_file_obj.get_mount_point().clone(),
1863        )
1864    };
1865
1866    // Determine base directory for new path resolution
1867    let (_new_base_entry, _new_base_mount) = if newdirfd == AT_FDCWD {
1868        // Use current working directory as base
1869        vfs.get_cwd().unwrap_or_else(|| {
1870            let root_mount = vfs.mount_tree.root_mount.read().clone();
1871            (root_mount.root.clone(), root_mount)
1872        })
1873    } else {
1874        // Use directory file descriptor as base
1875        let handle = match abi.get_handle(newdirfd as usize) {
1876            Some(h) => h,
1877            None => return usize::MAX,
1878        };
1879        let kernel_obj = match task.handle_table.get(handle) {
1880            Some(obj) => obj,
1881            None => return usize::MAX,
1882        };
1883        let file_obj = match kernel_obj.as_file() {
1884            Some(f) => f,
1885            None => return usize::MAX,
1886        };
1887        let vfs_file_obj = file_obj
1888            .as_any()
1889            .downcast_ref::<VfsFileObject>()
1890            .ok_or(())
1891            .unwrap();
1892        (
1893            vfs_file_obj.get_vfs_entry().clone(),
1894            vfs_file_obj.get_mount_point().clone(),
1895        )
1896    };
1897
1898    // Resolve the source path to verify it exists
1899    let _source_entry = match vfs.resolve_path_from(&old_base_entry, &old_base_mount, &oldpath_str)
1900    {
1901        Ok((entry, _mount_point)) => entry,
1902        Err(_) => return usize::MAX, // Source file doesn't exist
1903    };
1904
1905    // For now, we'll implement a simplified version using absolute paths
1906    // since VFS v2 may not have direct hard link support yet
1907
1908    // Convert paths to absolute paths
1909    let _old_absolute_path = if oldpath_str.starts_with('/') {
1910        oldpath_str.to_string()
1911    } else {
1912        match to_absolute_path_v2(&task, &oldpath_str) {
1913            Ok(p) => p,
1914            Err(_) => return usize::MAX,
1915        }
1916    };
1917
1918    let _new_absolute_path = if newpath_str.starts_with('/') {
1919        newpath_str.to_string()
1920    } else {
1921        match to_absolute_path_v2(&task, &newpath_str) {
1922            Ok(p) => p,
1923            Err(_) => return usize::MAX,
1924        }
1925    };
1926
1927    // Get mutable VFS reference for link creation
1928    let _vfs_mut = match task.vfs.write().clone() {
1929        Some(v) => v,
1930        None => return usize::MAX,
1931    };
1932
1933    // TODO: Handle flags properly
1934    // AT_SYMLINK_FOLLOW: follow symbolic links in oldpath
1935    // AT_EMPTY_PATH: allow empty oldpath if olddirfd refers to a file
1936    let _follow_symlinks = (flags & AT_SYMLINK_FOLLOW) != 0;
1937    let _empty_path = (flags & AT_EMPTY_PATH) != 0;
1938
1939    // Try to create the hard link
1940    // Note: This is a simplified implementation. A full implementation would:
1941    // 1. Check if source and destination are on the same filesystem
1942    // 2. Verify the source is not a directory (unless allowed)
1943    // 3. Handle proper hard link semantics
1944    // 4. Update inode reference counts
1945
1946    // For now, we'll return success as a stub implementation
1947    // since VFS v2 might not support true hard links yet.
1948    // Real hard link functionality would require:
1949    // - Filesystem-level support for hard links
1950    // - Inode reference counting
1951    // - Cross-filesystem link prevention
1952
1953    // Stub implementation: just return success
1954    // This prevents applications from crashing when they use linkat
1955    // but doesn't provide true hard link semantics
1956    0 // Success (stub implementation)
1957}
1958
1959/// VFS v2 helper function for path absolutization using VfsManager
1960fn to_absolute_path_v2(task: &crate::task::Task, path: &str) -> Result<String, ()> {
1961    if path.starts_with('/') {
1962        Ok(path.to_string())
1963    } else {
1964        let vfs_guard = task.vfs.read();
1965        let vfs = vfs_guard.as_ref().ok_or(())?;
1966        Ok(vfs.resolve_path_to_absolute(path))
1967    }
1968}
1969
1970/// Helper function to replace the missing get_path_str function
1971/// TODO: This should be moved to a shared helper when VFS v2 provides public API
1972fn get_path_str_v2(ptr: *const u8) -> Result<String, ()> {
1973    const MAX_PATH_LENGTH: usize = 1024; // Match the global constant
1974    cstring_to_string(ptr, MAX_PATH_LENGTH)
1975        .map(|(s, _)| s)
1976        .map_err(|_| ())
1977}
1978
1979/// Linux ioctl system call implementation
1980///
1981/// This system call performs device-specific control operations on file descriptors,
1982/// similar to the POSIX ioctl system call. It acts as a bridge between Linux ABI
1983/// and Scarlet's native HandleControl functionality.
1984///
1985/// # Arguments
1986/// - fd: File descriptor
1987/// - request: Control operation command
1988/// - arg: Argument for the control operation (often a pointer)
1989///
1990/// # Returns
1991/// - 0 or positive value on success
1992/// - usize::MAX on error (-1 in Linux)
1993pub fn sys_ioctl(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
1994    let task = mytask().unwrap();
1995    let fd = trapframe.get_arg(0) as usize;
1996    let request = trapframe.get_arg(1) as u32;
1997    let arg = trapframe.get_arg(2);
1998
1999    // Increment PC to avoid infinite loop
2000    trapframe.increment_pc_next(task);
2001
2002    // Get handle from Linux fd
2003    let handle = match abi.get_handle(fd) {
2004        Some(h) => h,
2005        None => return usize::MAX, // Invalid file descriptor
2006    };
2007
2008    // Get the kernel object from the handle table
2009    let kernel_object = match task.handle_table.get(handle) {
2010        Some(obj) => obj,
2011        None => return usize::MAX, // Invalid handle
2012    };
2013
2014    // Determine device capabilities for per-device translation
2015    let mut caps: Option<&'static [DeviceCapability]> = None;
2016    if let Some(file_obj) = kernel_object.as_file() {
2017        if let Ok(metadata) = file_obj.metadata() {
2018            if let FileType::CharDevice(info) = metadata.file_type {
2019                if let Some(dev) = DeviceManager::get_manager().get_device(info.device_id) {
2020                    caps = Some(dev.capabilities());
2021                }
2022            }
2023        }
2024    }
2025
2026    if let Some(caps) = caps {
2027        // TTY translation
2028        if caps.iter().any(|c| *c == DeviceCapability::Tty) {
2029            match crate::abi::linux::device::tty::handle_ioctl(request, arg, &kernel_object) {
2030                Ok(Some(ret)) => return ret,
2031                Ok(None) => {
2032                    // Do NOT pass through unknown TTY ioctls to device-specific control.
2033                    // Return ENOTTY to match Linux behavior and avoid accidental derefs.
2034                    return errno::to_result(errno::ENOTTY);
2035                }
2036                Err(_) => return errno::to_result(errno::ENOTTY),
2037            }
2038        }
2039        // Future: match on other capabilities here
2040    }
2041
2042    // Default path: pass-through to ControlOps if available
2043    let result = match kernel_object.as_control() {
2044        Some(control_ops) => control_ops.control(request, arg),
2045        None => Err("Inappropriate ioctl for device"),
2046    };
2047
2048    match result {
2049        Ok(value) => {
2050            if value >= 0 {
2051                value as usize
2052            } else {
2053                errno::to_result(errno::EINVAL)
2054            }
2055        }
2056        Err(_) => errno::to_result(errno::ENOTTY),
2057    }
2058}
2059
2060/// Linux execve system call implementation
2061///
2062/// This system call executes a program specified by the given path, replacing the
2063/// current process image with a new one. It also allows passing arguments and
2064/// environment variables to the new program.
2065///
2066/// # Arguments
2067/// - abi: LinuxRiscv64Abi context
2068/// - trapframe: Trapframe containing syscall arguments
2069///
2070/// # Returns
2071/// - 0 on success
2072/// - usize::MAX (Linux -1) on error
2073pub fn sys_execve(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2074    let task = mytask().unwrap();
2075
2076    // Increment PC to avoid infinite loop if execve fails
2077    trapframe.increment_pc_next(task);
2078
2079    // Get arguments from trapframe
2080    let path_ptr = trapframe.get_arg(0);
2081    let argv_ptr = trapframe.get_arg(1);
2082    let envp_ptr = trapframe.get_arg(2);
2083
2084    // Parse path
2085    let path_str = match parse_c_string_from_userspace(task, path_ptr, MAX_PATH_LENGTH) {
2086        Ok(path) => match to_absolute_path_v2(&task, &path) {
2087            Ok(abs_path) => abs_path,
2088            Err(_) => return usize::MAX, // Path error
2089        },
2090        Err(_) => return usize::MAX, // Path parsing error
2091    };
2092
2093    // Parse argv
2094    let argv_strings =
2095        match parse_string_array_from_userspace(task, argv_ptr, MAX_ARG_COUNT, MAX_PATH_LENGTH) {
2096            Ok(args) => args,
2097            Err(_) => return usize::MAX, // argv parsing error
2098        };
2099
2100    // Parse envp (optional)
2101    let envp_strings =
2102        match parse_string_array_from_userspace(task, envp_ptr, MAX_ARG_COUNT, MAX_PATH_LENGTH) {
2103            Ok(envs) => envs,
2104            Err(_) => return usize::MAX, // envp parsing error
2105        };
2106
2107    crate::println!(
2108        "sys_execve: path: {}, argv: {:?}, envp: {:?}",
2109        path_str,
2110        argv_strings,
2111        envp_strings
2112    );
2113
2114    // Debug: Print each argv element individually
2115    for (i, arg) in argv_strings.iter().enumerate() {
2116        crate::println!("  argv[{}]: \"{}\" (len={})", i, arg, arg.len());
2117        for (j, byte) in arg.bytes().enumerate() {
2118            if byte < 32 || byte > 126 {
2119                crate::println!("    byte[{}]: 0x{:02x} (non-printable)", j, byte);
2120            }
2121        }
2122    }
2123
2124    // Convert Vec<String> to Vec<&str> for TransparentExecutor
2125    let argv_refs: Vec<&str> = argv_strings.iter().map(|s| s.as_str()).collect();
2126    let envp_refs: Vec<&str> = envp_strings.iter().map(|s| s.as_str()).collect();
2127
2128    // Use TransparentExecutor for cross-ABI execution
2129    match TransparentExecutor::execute_binary(
2130        &path_str, &argv_refs, &envp_refs, task, trapframe, false,
2131    ) {
2132        Ok(_) => {
2133            // execve normally should not return on success - the process is replaced
2134            // However, if ABI module sets trapframe return value and returns here,
2135            // we should respect that value instead of hardcoding 0
2136            trapframe.get_return_value()
2137        }
2138        Err(_) => {
2139            // Execution failed - return error code
2140            // The trap handler will automatically set trapframe return value from our return
2141            usize::MAX // Error return value
2142        }
2143    }
2144}
2145
2146/// Linux iovec structure for vectored I/O operations
2147/// This structure matches the Linux kernel's definition for struct iovec
2148#[repr(C)]
2149#[derive(Debug, Clone, Copy)]
2150pub struct IoVec {
2151    /// Base address of the buffer
2152    pub iov_base: *mut u8,
2153    /// Length of the buffer
2154    pub iov_len: usize,
2155}
2156
2157/// Linux sys_fcntl implementation for Scarlet VFS v2
2158/// Currently provides basic logging of commands to understand usage patterns
2159///
2160/// This is a minimal implementation that logs the fcntl commands being used
2161/// to help understand what functionality needs to be implemented.
2162const LOG_FCNTL: bool = false;
2163pub fn sys_fcntl(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2164    let task = mytask().unwrap();
2165    let fd = trapframe.get_arg(0) as usize;
2166    let cmd = trapframe.get_arg(1) as u32;
2167    let arg = trapframe.get_arg(2);
2168
2169    // Increment PC to avoid infinite loop
2170    trapframe.increment_pc_next(task);
2171
2172    // Log the fcntl command to understand usage patterns
2173    match cmd {
2174        F_DUPFD => {
2175            if LOG_FCNTL {
2176                crate::println!(
2177                    "[sys_fcntl] F_DUPFD: fd={}, arg={} - NOT IMPLEMENTED",
2178                    fd,
2179                    arg
2180                );
2181            }
2182            if fd >= super::MAX_FDS {
2183                return errno::to_result(errno::EBADF);
2184            }
2185            let min_fd = arg as usize;
2186            if min_fd >= super::MAX_FDS {
2187                return errno::to_result(errno::EMFILE);
2188            }
2189            let old_handle = match abi.get_handle(fd) {
2190                Some(h) => h,
2191                None => return errno::to_result(errno::EBADF),
2192            };
2193            let kernel_obj = match task.handle_table.clone_for_dup(old_handle) {
2194                Some(obj) => obj,
2195                None => return errno::to_result(errno::EBADF),
2196            };
2197            let mut new_fd = None;
2198            for candidate in min_fd..super::MAX_FDS {
2199                if abi.get_handle(candidate).is_none() {
2200                    new_fd = Some(candidate);
2201                    break;
2202                }
2203            }
2204            let new_fd = match new_fd {
2205                Some(fd) => fd,
2206                None => return errno::to_result(errno::EMFILE),
2207            };
2208            let new_handle = match task.handle_table.insert(kernel_obj) {
2209                Ok(handle) => handle,
2210                Err(_) => return errno::to_result(errno::ENFILE),
2211            };
2212            if abi.allocate_specific_fd(new_fd, new_handle as u32).is_err() {
2213                let _ = task.handle_table.remove(new_handle as u32);
2214                return errno::to_result(errno::EMFILE);
2215            }
2216            if let Some(flags) = abi.get_file_status_flags(fd) {
2217                let _ = abi.set_file_status_flags(new_fd, flags);
2218            }
2219            let _ = abi.set_fd_flags(new_fd, 0);
2220            return new_fd;
2221        }
2222        F_GETFD => {
2223            // Get file descriptor flags (IMPLEMENTED)
2224            if let Some(_handle) = abi.get_handle(fd) {
2225                if let Some(flags) = abi.get_fd_flags(fd) {
2226                    return flags as usize; // Return the flags
2227                } else {
2228                    return usize::MAX; // Invalid file descriptor
2229                }
2230            } else {
2231                return usize::MAX; // Invalid file descriptor
2232            }
2233        }
2234        F_SETFD => {
2235            // Set file descriptor flags (IMPLEMENTED)
2236            if let Some(_handle) = abi.get_handle(fd) {
2237                match abi.set_fd_flags(fd, arg as u32) {
2238                    Ok(()) => return 0,          // Success
2239                    Err(_) => return usize::MAX, // Error
2240                }
2241            } else {
2242                return usize::MAX; // Invalid file descriptor
2243            }
2244        }
2245        F_GETFL => {
2246            if let Some(_handle) = abi.get_handle(fd) {
2247                if let Some(flags) = abi.get_file_status_flags(fd) {
2248                    return flags as usize;
2249                } else {
2250                    return usize::MAX;
2251                }
2252            } else {
2253                return usize::MAX;
2254            }
2255        }
2256        F_SETFL => {
2257            // Only honor a subset (currently O_NONBLOCK). Preserve other bits as-is.
2258            if let Some(_handle) = abi.get_handle(fd) {
2259                // Get current status flags and update O_NONBLOCK bit only
2260                let curr = abi.get_file_status_flags(fd).unwrap_or(0);
2261                let mut new_flags = curr;
2262                const O_NONBLOCK_U32: u32 = O_NONBLOCK as u32;
2263                if (arg as u32) & O_NONBLOCK_U32 != 0 {
2264                    new_flags |= O_NONBLOCK_U32;
2265                } else {
2266                    new_flags &= !O_NONBLOCK_U32;
2267                }
2268                if abi.set_file_status_flags(fd, new_flags).is_err() {
2269                    return usize::MAX;
2270                }
2271                // Also propagate O_NONBLOCK to the object-level Selectable if available
2272                if let Some(handle) = abi.get_handle(fd) {
2273                    if let Some(obj) = task.handle_table.get(handle) {
2274                        if let Some(sel) = obj.as_selectable() {
2275                            sel.set_nonblocking(((new_flags as i32) & O_NONBLOCK) != 0);
2276                        }
2277                    }
2278                }
2279
2280                return 0;
2281            } else {
2282                return usize::MAX;
2283            }
2284        }
2285        F_GETLK => {
2286            if LOG_FCNTL {
2287                crate::println!(
2288                    "[sys_fcntl] F_GETLK: fd={}, lock_ptr={:#x} - NOT IMPLEMENTED",
2289                    fd,
2290                    arg
2291                );
2292            }
2293            // TODO: Implement file locking
2294        }
2295        F_SETLK => {
2296            if LOG_FCNTL {
2297                crate::println!(
2298                    "[sys_fcntl] F_SETLK: fd={}, lock_ptr={:#x} - NOT IMPLEMENTED",
2299                    fd,
2300                    arg
2301                );
2302            }
2303            // TODO: Implement file locking
2304        }
2305        F_SETLKW => {
2306            if LOG_FCNTL {
2307                crate::println!(
2308                    "[sys_fcntl] F_SETLKW: fd={}, lock_ptr={:#x} - NOT IMPLEMENTED",
2309                    fd,
2310                    arg
2311                );
2312            }
2313            // TODO: Implement file locking
2314        }
2315        F_SETOWN => {
2316            if LOG_FCNTL {
2317                crate::println!(
2318                    "[sys_fcntl] F_SETOWN: fd={}, owner={} - NOT IMPLEMENTED",
2319                    fd,
2320                    arg
2321                );
2322            }
2323            // TODO: Implement F_SETOWN
2324        }
2325        F_GETOWN => {
2326            if LOG_FCNTL {
2327                crate::println!("[sys_fcntl] F_GETOWN: fd={} - NOT IMPLEMENTED", fd);
2328            }
2329            // TODO: Implement F_GETOWN
2330        }
2331        F_SETSIG => {
2332            if LOG_FCNTL {
2333                crate::println!(
2334                    "[sys_fcntl] F_SETSIG: fd={}, sig={} - NOT IMPLEMENTED",
2335                    fd,
2336                    arg
2337                );
2338            }
2339            // TODO: Implement F_SETSIG
2340        }
2341        F_GETSIG => {
2342            if LOG_FCNTL {
2343                crate::println!("[sys_fcntl] F_GETSIG: fd={} - NOT IMPLEMENTED", fd);
2344            }
2345            // TODO: Implement F_GETSIG
2346        }
2347        F_SETLEASE => {
2348            if LOG_FCNTL {
2349                crate::println!(
2350                    "[sys_fcntl] F_SETLEASE: fd={}, lease_type={} - NOT IMPLEMENTED",
2351                    fd,
2352                    arg
2353                );
2354            }
2355            // TODO: Implement F_SETLEASE
2356        }
2357        F_GETLEASE => {
2358            if LOG_FCNTL {
2359                crate::println!("[sys_fcntl] F_GETLEASE: fd={} - NOT IMPLEMENTED", fd);
2360            }
2361            // TODO: Implement F_GETLEASE
2362        }
2363        F_NOTIFY => {
2364            if LOG_FCNTL {
2365                crate::println!(
2366                    "[sys_fcntl] F_NOTIFY: fd={}, events={:#x} - NOT IMPLEMENTED",
2367                    fd,
2368                    arg
2369                );
2370            }
2371            // TODO: Implement F_NOTIFY
2372        }
2373        F_DUPFD_CLOEXEC => {
2374            if LOG_FCNTL {
2375                crate::println!(
2376                    "[sys_fcntl] F_DUPFD_CLOEXEC: fd={}, arg={} - NOT IMPLEMENTED",
2377                    fd,
2378                    arg
2379                );
2380            }
2381            if fd >= super::MAX_FDS {
2382                return errno::to_result(errno::EBADF);
2383            }
2384            let min_fd = arg as usize;
2385            if min_fd >= super::MAX_FDS {
2386                return errno::to_result(errno::EMFILE);
2387            }
2388            let old_handle = match abi.get_handle(fd) {
2389                Some(h) => h,
2390                None => return errno::to_result(errno::EBADF),
2391            };
2392            let kernel_obj = match task.handle_table.clone_for_dup(old_handle) {
2393                Some(obj) => obj,
2394                None => return errno::to_result(errno::EBADF),
2395            };
2396            let mut new_fd = None;
2397            for candidate in min_fd..super::MAX_FDS {
2398                if abi.get_handle(candidate).is_none() {
2399                    new_fd = Some(candidate);
2400                    break;
2401                }
2402            }
2403            let new_fd = match new_fd {
2404                Some(fd) => fd,
2405                None => return errno::to_result(errno::EMFILE),
2406            };
2407            let new_handle = match task.handle_table.insert(kernel_obj) {
2408                Ok(handle) => handle,
2409                Err(_) => return errno::to_result(errno::ENFILE),
2410            };
2411            if abi.allocate_specific_fd(new_fd, new_handle as u32).is_err() {
2412                let _ = task.handle_table.remove(new_handle as u32);
2413                return errno::to_result(errno::EMFILE);
2414            }
2415            if let Some(flags) = abi.get_file_status_flags(fd) {
2416                let _ = abi.set_file_status_flags(new_fd, flags);
2417            }
2418            let _ = abi.set_fd_flags(new_fd, FD_CLOEXEC);
2419            return new_fd;
2420        }
2421        _ => {
2422            if LOG_FCNTL {
2423                crate::println!(
2424                    "[sys_fcntl] UNKNOWN_CMD: fd={}, cmd={}, arg={:#x} - NOT IMPLEMENTED",
2425                    fd,
2426                    cmd,
2427                    arg
2428                );
2429            }
2430        }
2431    }
2432
2433    // All unimplemented commands return ENOSYS (already logged above)
2434    errno::to_result(errno::ENOSYS)
2435}
2436
2437/// Linux sys_flock - Apply or remove an advisory lock on an open file
2438///
2439/// Apply or remove an advisory lock on the open file specified by fd.
2440/// This is a simplified implementation that always succeeds.
2441///
2442/// Arguments:
2443/// - abi: LinuxRiscv64Abi context
2444/// - trapframe: Trapframe containing syscall arguments
2445///   - arg0: fd (file descriptor)
2446///   - arg1: operation (LOCK_SH, LOCK_EX, LOCK_UN, etc.)
2447///
2448/// Returns:
2449/// - 0 on success
2450/// - usize::MAX (Linux -1) on error
2451pub fn sys_flock(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2452    let task = match mytask() {
2453        Some(t) => t,
2454        None => return usize::MAX,
2455    };
2456
2457    let fd = trapframe.get_arg(0) as i32;
2458    let _operation = trapframe.get_arg(1) as i32;
2459
2460    // Increment PC to avoid infinite loop
2461    trapframe.increment_pc_next(task);
2462
2463    // Verify fd is valid
2464    if abi.get_handle(fd as usize).is_none() {
2465        return usize::MAX;
2466    }
2467
2468    // Simplified implementation: always succeed
2469    // Real flock would require managing lock state per file descriptor
2470    // For Wayland SHM operations, advisory locks aren't critical
2471    0
2472}
2473
2474/// Linux sys_fallocate - Manipulate file space
2475///
2476/// This is used by glib/Wayland to extend shared memory file size.
2477/// When mode is 0 (default), it extends the file to at least offset + len.
2478///
2479/// Arguments:
2480/// - fd: File descriptor
2481/// - mode: Operation mode (0 = allocate, FALLOC_FL_KEEP_SIZE, etc.)
2482/// - offset: Starting offset
2483/// - len: Length of the allocation
2484///
2485/// Returns:
2486/// - 0 on success
2487/// - negative errno on failure
2488pub fn sys_fallocate(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2489    use super::errno;
2490
2491    let task = match mytask() {
2492        Some(t) => t,
2493        None => return errno::to_result(errno::EFAULT),
2494    };
2495
2496    let fd = trapframe.get_arg(0) as usize;
2497    let mode = trapframe.get_arg(1) as i32;
2498    let offset = trapframe.get_arg(2) as i64;
2499    let len = trapframe.get_arg(3) as i64;
2500
2501    trapframe.increment_pc_next(task);
2502
2503    if offset < 0 || len <= 0 {
2504        return errno::to_result(errno::EINVAL);
2505    }
2506
2507    let handle = match abi.get_handle(fd) {
2508        Some(h) => h,
2509        None => return errno::to_result(errno::EBADF),
2510    };
2511
2512    let kernel_obj = match task.handle_table.get(handle) {
2513        Some(obj) => obj,
2514        None => return errno::to_result(errno::EBADF),
2515    };
2516
2517    // Handle SharedMemory (memfd)
2518    if let Some(shared_memory) = kernel_obj.as_shared_memory() {
2519        // FALLOC_FL_KEEP_SIZE = 0x01 - don't change file size
2520        const FALLOC_FL_KEEP_SIZE: i32 = 0x01;
2521
2522        let new_size = (offset + len) as usize;
2523        let current_size = shared_memory.size();
2524
2525        // If mode doesn't have KEEP_SIZE, extend the file
2526        if (mode & FALLOC_FL_KEEP_SIZE) == 0 && new_size > current_size {
2527            if let Err(_e) = shared_memory.resize(new_size) {
2528                return errno::to_result(errno::ENOSPC);
2529            }
2530        }
2531
2532        return 0;
2533    }
2534
2535    // For regular files, just succeed (no-op for now)
2536    // Real implementation would preallocate disk space
2537    0
2538}
2539
2540/// Linux struct linux_dirent64 (for getdents64 syscall)
2541#[repr(C)]
2542pub struct LinuxDirent64 {
2543    pub d_ino: u64,
2544    pub d_off: i64,
2545    pub d_reclen: u16,
2546    pub d_type: u8,
2547    pub d_name: [u8; 256], // Linux allows up to 255 + null
2548}
2549
2550impl LinuxDirent64 {
2551    pub fn new(entry: &DirectoryEntry, d_off: i64) -> Self {
2552        let mut d_name = [0u8; 256];
2553        let name_len = entry.name_len as usize;
2554        d_name[..name_len].copy_from_slice(&entry.name[..name_len]);
2555        d_name[name_len] = 0; // null-terminated
2556        Self {
2557            d_ino: entry.file_id,
2558            d_off,
2559            d_reclen: (core::mem::size_of::<u64>()
2560                + core::mem::size_of::<i64>()
2561                + core::mem::size_of::<u16>()
2562                + core::mem::size_of::<u8>()
2563                + name_len
2564                + 1) as u16,
2565            d_type: entry.file_type,
2566            d_name,
2567        }
2568    }
2569    pub fn as_bytes(&self) -> &[u8] {
2570        let len = self.d_reclen as usize;
2571        unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, len) }
2572    }
2573}
2574
2575/// getdents64 syscall implementation
2576pub fn sys_getdents64(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2577    let task = mytask().unwrap();
2578    let fd = trapframe.get_arg(0) as usize;
2579    let buf_ptr = task
2580        .vm_manager
2581        .translate_vaddr(trapframe.get_arg(1))
2582        .unwrap() as *mut u8;
2583    let buf_size = trapframe.get_arg(2) as usize;
2584    trapframe.increment_pc_next(task);
2585
2586    // Get handle from Linux fd
2587    let handle = match abi.get_handle(fd) {
2588        Some(h) => h,
2589        None => return usize::MAX,
2590    };
2591    let kernel_obj = match task.handle_table.get(handle) {
2592        Some(obj) => obj,
2593        None => return usize::MAX,
2594    };
2595    let stream = match kernel_obj.as_stream() {
2596        Some(s) => s,
2597        None => return usize::MAX,
2598    };
2599
2600    let mut dir_buffer = vec![0u8; core::mem::size_of::<DirectoryEntry>()];
2601    let mut written = 0usize;
2602    let mut d_off = 0i64;
2603    while written + core::mem::size_of::<LinuxDirent64>() <= buf_size {
2604        match stream.read(&mut dir_buffer) {
2605            Ok(n) if n == dir_buffer.len() => {
2606                if let Some(entry) = DirectoryEntry::parse(&dir_buffer) {
2607                    let dirent = LinuxDirent64::new(&entry, d_off);
2608                    let dirent_bytes = dirent.as_bytes();
2609                    if written + dirent_bytes.len() > buf_size {
2610                        break;
2611                    }
2612                    unsafe {
2613                        core::ptr::copy_nonoverlapping(
2614                            dirent_bytes.as_ptr(),
2615                            buf_ptr.add(written),
2616                            dirent_bytes.len(),
2617                        );
2618                    }
2619                    written += dirent_bytes.len();
2620                    d_off += 1;
2621                } else {
2622                    break;
2623                }
2624            }
2625            Ok(0) => break, // EOF
2626            Ok(_) => break, // partial read, treat as error/EOF
2627            Err(StreamError::EndOfStream) => break,
2628            Err(StreamError::WouldBlock) => {
2629                get_scheduler().schedule(trapframe);
2630                return usize::MAX;
2631            }
2632            Err(_) => break,
2633        }
2634    }
2635    written
2636}
2637
2638/// Linux readv system call implementation
2639///
2640/// This system call reads data into multiple buffers (iovec) in a single call.
2641///
2642/// # Arguments
2643/// - fd: File descriptor
2644/// - iovec: Array of iovec structures
2645/// - iovcnt: Number of elements in the array
2646///
2647/// # Returns
2648/// - On success: number of bytes read
2649/// - On error: usize::MAX
2650pub fn sys_readv(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2651    let task = mytask().unwrap();
2652    let fd = trapframe.get_arg(0) as usize;
2653    let iovec_ptr = trapframe.get_arg(1);
2654    let iovcnt = trapframe.get_arg(2) as usize;
2655    trapframe.increment_pc_next(task);
2656
2657    if iovcnt == 0 {
2658        return 0;
2659    }
2660    const IOV_MAX: usize = 1024;
2661    if iovcnt > IOV_MAX {
2662        return usize::MAX;
2663    }
2664    let handle = match abi.get_handle(fd) {
2665        Some(h) => h,
2666        None => return usize::MAX,
2667    };
2668    let kernel_obj = match task.handle_table.get(handle) {
2669        Some(obj) => obj,
2670        None => return usize::MAX,
2671    };
2672    let stream = match kernel_obj.as_stream() {
2673        Some(s) => s,
2674        None => return usize::MAX, // Not a stream object
2675    };
2676
2677    let nonblocking = abi
2678        .get_file_status_flags(fd)
2679        .map(|f| ((f as i32) & O_NONBLOCK) != 0)
2680        .unwrap_or(false);
2681    let iovec_vaddr = match task.vm_manager.translate_vaddr(iovec_ptr) {
2682        Some(addr) => addr as *mut IoVec,
2683        None => return usize::MAX,
2684    };
2685    if iovec_vaddr.is_null() {
2686        return usize::MAX;
2687    }
2688    let iovecs = unsafe { core::slice::from_raw_parts_mut(iovec_vaddr, iovcnt) };
2689    let mut total_read = 0usize;
2690    for iovec in iovecs.iter_mut() {
2691        if iovec.iov_len == 0 {
2692            continue;
2693        }
2694        let buf_vaddr = match task.vm_manager.translate_vaddr(iovec.iov_base as usize) {
2695            Some(addr) => addr as *mut u8,
2696            None => return usize::MAX,
2697        };
2698        if buf_vaddr.is_null() {
2699            return usize::MAX;
2700        }
2701        let buffer = unsafe { core::slice::from_raw_parts_mut(buf_vaddr, iovec.iov_len) };
2702        match stream.read(buffer) {
2703            Ok(n) => {
2704                total_read = total_read.saturating_add(n);
2705                // If partial read occurred, stop processing remaining vectors
2706                // This matches Linux behavior for readv
2707                if n < iovec.iov_len {
2708                    break;
2709                }
2710            }
2711            Err(StreamError::EndOfStream) => break,
2712            Err(StreamError::WouldBlock) => {
2713                if nonblocking {
2714                    if total_read == 0 {
2715                        return errno::to_result(errno::EAGAIN);
2716                    } else {
2717                        break;
2718                    }
2719                } else {
2720                    get_scheduler().schedule(trapframe);
2721                    return usize::MAX;
2722                }
2723            }
2724            Err(_) => {
2725                if total_read == 0 {
2726                    return usize::MAX;
2727                } else {
2728                    break;
2729                }
2730            }
2731        }
2732    }
2733    total_read
2734}
2735
2736/// Linux sys_fsync system call implementation (stub)
2737/// Synchronize a file's in-core state with storage device
2738///
2739/// Arguments:
2740/// - fd: File descriptor to synchronize
2741///
2742/// Returns:
2743/// - 0 on success
2744/// - usize::MAX on error
2745pub fn sys_fsync(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2746    let task = mytask().unwrap();
2747    let _fd = trapframe.get_arg(0);
2748    trapframe.increment_pc_next(task);
2749
2750    // TODO: Implement actual file synchronization
2751    // For now, return success as a stub implementation
2752    0
2753}
2754
2755/// Linux sys_ftruncate implementation
2756///
2757/// Truncate a file to a specified length using a file descriptor.
2758///
2759/// Arguments:
2760/// - fd: File descriptor to truncate
2761/// - length: New file length
2762///
2763/// Returns:
2764/// - 0 on success
2765/// - negative errno on failure
2766pub fn sys_ftruncate(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2767    let task = match mytask() {
2768        Some(t) => t,
2769        None => return errno::to_result(errno::EFAULT),
2770    };
2771
2772    let fd = trapframe.get_arg(0) as usize;
2773    let length = trapframe.get_arg(1) as i64;
2774
2775    trapframe.increment_pc_next(task);
2776
2777    if length < 0 {
2778        return errno::to_result(errno::EINVAL);
2779    }
2780
2781    let handle = match abi.get_handle(fd) {
2782        Some(h) => h,
2783        None => return errno::to_result(errno::EBADF),
2784    };
2785
2786    let kernel_obj = match task.handle_table.get(handle) {
2787        Some(obj) => obj,
2788        None => return errno::to_result(errno::EBADF),
2789    };
2790
2791    if let Some(shared_memory) = kernel_obj.as_shared_memory() {
2792        if let Err(_err) = shared_memory.resize(length as usize) {
2793            return errno::to_result(errno::EINVAL);
2794        }
2795        return 0;
2796    }
2797
2798    let file_obj = match kernel_obj.as_file() {
2799        Some(f) => f,
2800        None => return errno::to_result(errno::EINVAL),
2801    };
2802
2803    let mut is_shm = false;
2804    let mut shm_path = None;
2805    if let Some(vfs_obj) = file_obj
2806        .as_any()
2807        .downcast_ref::<crate::fs::vfs_v2::core::VfsFileObject>()
2808    {
2809        let path = vfs_obj.get_original_path();
2810        if path.contains("wl_shm-") {
2811            is_shm = true;
2812            shm_path = Some(path);
2813            crate::println!("sys_ftruncate: shm path='{}' len={}", path, length);
2814        }
2815    }
2816
2817    match file_obj.truncate(length as u64) {
2818        Ok(()) => 0,
2819        Err(err) => {
2820            if is_shm {
2821                if let Ok(meta) = file_obj.metadata() {
2822                    crate::println!(
2823                        "sys_ftruncate: shm truncate failed len={} file_type={:?} size={}",
2824                        length,
2825                        meta.file_type,
2826                        meta.size
2827                    );
2828                }
2829                crate::println!("sys_ftruncate: shm truncate error={:?}", err);
2830            } else if length > 0 {
2831                let kind = match kernel_obj {
2832                    crate::object::KernelObject::File(_) => "File",
2833                    crate::object::KernelObject::Pipe(_) => "Pipe",
2834                    crate::object::KernelObject::Counter(_) => "Counter",
2835                    crate::object::KernelObject::EventChannel(_) => "EventChannel",
2836                    crate::object::KernelObject::EventSubscription(_) => "EventSubscription",
2837                    crate::object::KernelObject::SharedMemory(_) => "SharedMemory",
2838                    #[cfg(feature = "network")]
2839                    crate::object::KernelObject::Socket(_) => "Socket",
2840                };
2841                crate::println!(
2842                    "sys_ftruncate: fd={} kind={} path={:?} len={} err={:?}",
2843                    fd,
2844                    kind,
2845                    shm_path,
2846                    length,
2847                    err
2848                );
2849            }
2850            errno::to_result(errno::EIO)
2851        }
2852    }
2853}
2854
2855/// Linux sys_faccessat implementation (dummy: always returns 0)
2856///
2857/// Arguments:
2858/// - abi: LinuxRiscv64Abi context
2859/// - trapframe: Trapframe containing syscall arguments
2860///
2861/// Returns:
2862/// - 0 (success)
2863pub fn sys_faccessat(_abi: &mut LinuxRiscv64Abi, trapframe: &mut crate::arch::Trapframe) -> usize {
2864    let task = crate::task::mytask().unwrap();
2865    trapframe.increment_pc_next(task);
2866
2867    let dirfd = trapframe.get_arg(0) as i32;
2868    let path_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(1)) {
2869        Some(ptr) => ptr as *const u8,
2870        None => return usize::MAX,
2871    };
2872    let mode = trapframe.get_arg(2) as i32;
2873    let path_str = match get_path_str_v2(path_ptr) {
2874        Ok(p) => p,
2875        Err(_) => return usize::MAX,
2876    };
2877
2878    // crate::println!(
2879    //     "sys_faccessat: epc={:#x}, dirfd={}, path='{}', mode={:#o}",
2880    //     trapframe.epc,
2881    //     dirfd,
2882    //     path_str,
2883    //     mode
2884    // );
2885
2886    0
2887}
2888
2889/// Linux faccessat2 system call (syscall 439)
2890///
2891/// Checks user's permissions for a file. Similar to faccessat but with
2892/// additional flag support (AT_EACCESS, AT_SYMLINK_NOFOLLOW).
2893///
2894/// Signature: int faccessat2(int dirfd, const char *pathname, int mode, int flags);
2895///
2896/// Returns:
2897/// - 0 (success)
2898pub fn sys_faccessat2(_abi: &mut LinuxRiscv64Abi, trapframe: &mut crate::arch::Trapframe) -> usize {
2899    let task = crate::task::mytask().unwrap();
2900    trapframe.increment_pc_next(task);
2901
2902    let dirfd = trapframe.get_arg(0) as i32;
2903    let path_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(1)) {
2904        Some(ptr) => ptr as *const u8,
2905        None => return usize::MAX,
2906    };
2907    let mode = trapframe.get_arg(2) as i32;
2908    let flags = trapframe.get_arg(3) as i32;
2909    let path_str = match get_path_str_v2(path_ptr) {
2910        Ok(p) => p,
2911        Err(_) => return usize::MAX,
2912    };
2913
2914    // crate::println!(
2915    //     "sys_faccessat2: epc={:#x}, dirfd={}, path='{}', mode={:#o}, flags={:#x}",
2916    //     trapframe.epc,
2917    //     dirfd,
2918    //     path_str,
2919    //     mode,
2920    //     flags
2921    // );
2922
2923    0
2924}
2925
2926/// Linux sys_mkdirat implementation
2927///
2928/// Currently supports only AT_FDCWD (current working directory) as dirfd.
2929///
2930pub fn sys_mkdirat(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2931    let task = match mytask() {
2932        Some(t) => t,
2933        None => return usize::MAX,
2934    };
2935    trapframe.increment_pc_next(task);
2936    let dirfd = trapframe.get_arg(0) as i32;
2937    let path_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(1)) {
2938        Some(ptr) => ptr as *const u8,
2939        None => return usize::MAX,
2940    };
2941    let path = match cstring_to_string(path_ptr, 128) {
2942        Ok((p, _)) => p,
2943        Err(_) => return usize::MAX,
2944    };
2945    // NOTE: Currently only AT_FDCWD is supported
2946    if dirfd != -100 {
2947        // AT_FDCWD
2948        return usize::MAX;
2949    }
2950
2951    let abs_path = match to_absolute_path_v2(&task, &path) {
2952        Ok(p) => p,
2953        Err(_) => return usize::MAX,
2954    };
2955    let vfs = match task.vfs.write().clone() {
2956        Some(v) => v,
2957        None => return usize::MAX,
2958    };
2959    match vfs.create_dir(&abs_path) {
2960        Ok(_) => 0,
2961        Err(_) => usize::MAX,
2962    }
2963}
2964
2965/// Linux sys_newfstat implementation for Scarlet VFS v2
2966///
2967/// Gets file status information from a file descriptor.
2968/// This is equivalent to stat() but uses a file descriptor instead of a path.
2969///
2970/// Arguments:
2971/// - abi: LinuxRiscv64Abi context
2972/// - trapframe: Trapframe containing syscall arguments
2973///   - arg0: fd (file descriptor)
2974///   - arg1: stat_ptr (pointer to LinuxStat structure)
2975///
2976/// Returns:
2977/// - 0 on success
2978/// - usize::MAX (Linux -1) on error
2979pub fn sys_newfstat(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
2980    let task = match mytask() {
2981        Some(t) => t,
2982        None => return usize::MAX,
2983    };
2984
2985    let fd = trapframe.get_arg(0) as i32;
2986    let stat_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(1)) {
2987        Some(ptr) => ptr as *mut u8,
2988        None => return usize::MAX,
2989    };
2990
2991    // Increment PC to avoid infinite loop
2992    trapframe.increment_pc_next(task);
2993
2994    // Validate arguments
2995    if stat_ptr.is_null() {
2996        return usize::MAX; // Return -1 if stat pointer is null
2997    }
2998
2999    // Get handle from file descriptor
3000    let handle = match abi.get_handle(fd as usize) {
3001        Some(h) => h,
3002        None => return usize::MAX, // Invalid file descriptor
3003    };
3004
3005    // Get kernel object from handle
3006    let kernel_obj = match task.handle_table.get(handle) {
3007        Some(obj) => obj,
3008        None => return usize::MAX, // Handle not found
3009    };
3010
3011    // Get file object
3012    let file_obj = match kernel_obj.as_file() {
3013        Some(f) => f,
3014        None => return usize::MAX, // Not a file object
3015    };
3016
3017    // Get VFS file object to access metadata
3018    use crate::fs::vfs_v2::core::VfsFileObject;
3019    let vfs_file_obj = match file_obj.as_any().downcast_ref::<VfsFileObject>() {
3020        Some(vfs_obj) => vfs_obj,
3021        None => {
3022            // For non-VFS files (like devices), create a basic stat with minimal info
3023            let stat = unsafe { &mut *(stat_ptr as *mut LinuxStat) };
3024            *stat = LinuxStat {
3025                st_dev: 0,
3026                st_ino: handle as u64,    // Use handle as inode
3027                st_mode: S_IFCHR | 0o666, // Character device with rw-rw-rw- permissions
3028                st_nlink: 1,
3029                st_uid: 0,
3030                st_gid: 0,
3031                st_rdev: handle as u64,
3032                __pad1: 0,
3033                st_size: 0,
3034                st_blksize: 4096,
3035                __pad2: 0,
3036                st_blocks: 0,
3037                st_atime: 0,
3038                st_atime_nsec: 0,
3039                st_mtime: 0,
3040                st_mtime_nsec: 0,
3041                st_ctime: 0,
3042                st_ctime_nsec: 0,
3043                __unused4: 0,
3044                __unused5: 0,
3045            };
3046            return 0; // Success
3047        }
3048    };
3049
3050    // Get VFS entry and metadata
3051    let entry = vfs_file_obj.get_vfs_entry();
3052    let node = entry.node();
3053
3054    match node.metadata() {
3055        Ok(metadata) => {
3056            let stat = unsafe { &mut *(stat_ptr as *mut LinuxStat) };
3057            *stat = LinuxStat::from_metadata(&metadata);
3058            // crate::println!(
3059            //     "sys_newfstat: fd={} name='{}' size={}",
3060            //     fd,
3061            //     entry.name(),
3062            //     metadata.size
3063            // );
3064            0 // Success
3065        }
3066        Err(_) => usize::MAX, // Error getting metadata
3067    }
3068}
3069
3070/// Linux sys_unlinkat implementation for Scarlet VFS v2
3071///
3072/// Removes a file or directory relative to a directory file descriptor.
3073/// If dirfd == AT_FDCWD, uses the current working directory as the base.
3074/// Otherwise, resolves the base directory from the file descriptor.
3075///
3076/// Arguments:
3077/// - abi: LinuxRiscv64Abi context
3078/// - trapframe: Trapframe containing syscall arguments
3079///   - arg0: dirfd (directory file descriptor)
3080///   - arg1: path_ptr (pointer to path string)
3081///   - arg2: flags (unlink flags)
3082///
3083/// Returns:
3084/// - 0 on success
3085/// - usize::MAX (Linux -1) on error
3086pub fn sys_unlinkat(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
3087    let task = match mytask() {
3088        Some(t) => t,
3089        None => return usize::MAX,
3090    };
3091
3092    let dirfd = trapframe.get_arg(0) as i32;
3093    let path_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(1)) {
3094        Some(ptr) => ptr as *const u8,
3095        None => return usize::MAX,
3096    };
3097    let flags = trapframe.get_arg(2) as i32;
3098
3099    // Increment PC to avoid infinite loop
3100    trapframe.increment_pc_next(task);
3101
3102    // Parse path from user space
3103    let path_str = match cstring_to_string(path_ptr, MAX_PATH_LENGTH) {
3104        Ok((path, _)) => path,
3105        Err(_) => return usize::MAX, // Invalid UTF-8
3106    };
3107
3108    let path_str = remap_shm_path(&path_str);
3109    if is_wl_shm_path(&path_str) {
3110        crate::println!("sys_unlinkat: shm ignore path='{}'", path_str);
3111        return 0;
3112    }
3113
3114    // Linux constants for unlinkat
3115    const AT_FDCWD: i32 = -100;
3116    const AT_REMOVEDIR: i32 = 0x200;
3117
3118    let vfs = match task.vfs.read().clone() {
3119        Some(v) => v,
3120        None => return usize::MAX,
3121    };
3122
3123    // Determine base directory for path resolution
3124    use crate::fs::vfs_v2::core::VfsFileObject;
3125
3126    let (base_entry, base_mount) = if dirfd == AT_FDCWD {
3127        // Use current working directory as base
3128        vfs.get_cwd().unwrap_or_else(|| {
3129            let root_mount = vfs.mount_tree.root_mount.read().clone();
3130            (root_mount.root.clone(), root_mount)
3131        })
3132    } else {
3133        // Use directory file descriptor as base
3134        let handle = match abi.get_handle(dirfd as usize) {
3135            Some(h) => h,
3136            None => return usize::MAX,
3137        };
3138        let kernel_obj = match task.handle_table.get(handle) {
3139            Some(obj) => obj,
3140            None => return usize::MAX,
3141        };
3142        let file_obj = match kernel_obj.as_file() {
3143            Some(f) => f,
3144            None => return usize::MAX,
3145        };
3146        let vfs_file_obj = match file_obj.as_any().downcast_ref::<VfsFileObject>() {
3147            Some(vfs_obj) => vfs_obj,
3148            None => return usize::MAX,
3149        };
3150        (
3151            vfs_file_obj.get_vfs_entry().clone(),
3152            vfs_file_obj.get_mount_point().clone(),
3153        )
3154    };
3155
3156    // Resolve the target path and perform the removal operation
3157    match vfs.resolve_path_from(&base_entry, &base_mount, &path_str) {
3158        Ok((entry, _mount_point)) => {
3159            // Prepare absolute path before getting mutable VFS reference
3160            let absolute_path = if path_str.starts_with('/') {
3161                path_str.to_string()
3162            } else {
3163                // Construct absolute path by resolving relative to current working directory
3164                match to_absolute_path_v2(&task, &path_str) {
3165                    Ok(p) => p,
3166                    Err(_) => return usize::MAX,
3167                }
3168            };
3169
3170            // Get mutable reference to VFS for removal operations
3171            let vfs_mut = match task.vfs.write().clone() {
3172                Some(v) => v,
3173                None => return usize::MAX,
3174            };
3175
3176            // Check if AT_REMOVEDIR flag is set
3177            if flags & AT_REMOVEDIR != 0 {
3178                // Remove directory - check if it's actually a directory
3179                let node = entry.node();
3180                match node.metadata() {
3181                    Ok(metadata) => {
3182                        if metadata.file_type == FileType::Directory {
3183                            // Try to remove the directory using VFS remove operation
3184                            match vfs_mut.remove(&absolute_path) {
3185                                Ok(_) => 0,           // Success
3186                                Err(_) => usize::MAX, // Error removing directory
3187                            }
3188                        } else {
3189                            usize::MAX // Not a directory, cannot use AT_REMOVEDIR
3190                        }
3191                    }
3192                    Err(_) => usize::MAX, // Cannot get metadata
3193                }
3194            } else {
3195                // Remove file or directory (standard removal)
3196                match vfs_mut.remove(&absolute_path) {
3197                    Ok(_) => 0,           // Success
3198                    Err(_) => usize::MAX, // Error removing file
3199                }
3200            }
3201        }
3202        Err(_) => usize::MAX, // Path resolution failed
3203    }
3204}
3205
3206/// Linux epoll_create1 implementation (stub)
3207///
3208/// Creates an epoll file descriptor. This is a stub implementation that
3209/// simply returns a dummy file descriptor to prevent application crashes.
3210/// Real epoll functionality is not implemented.
3211///
3212/// Arguments:
3213/// - abi: LinuxRiscv64Abi context  
3214/// - trapframe: Trapframe containing syscall arguments
3215///   - arg0: flags (epoll creation flags)
3216///
3217/// Returns:
3218/// - file descriptor on success
3219/// - usize::MAX (Linux -1) on error
3220pub fn sys_epoll_create1(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
3221    let task = match mytask() {
3222        Some(t) => t,
3223        None => return usize::MAX,
3224    };
3225
3226    let _flags = trapframe.get_arg(0) as i32;
3227
3228    // Increment PC to avoid infinite loop
3229    trapframe.increment_pc_next(task);
3230
3231    // Create a dummy file handle to act as an epoll fd
3232    // This is a workaround since we don't have real epoll implementation
3233    // We'll use a simple placeholder handle
3234
3235    // Use a high handle number that's unlikely to conflict with real handles
3236    const EPOLL_DUMMY_HANDLE: u32 = 0x1000_0000;
3237
3238    // For now, just return a dummy fd number that doesn't conflict with real fds
3239    // This is not a proper implementation, but it prevents crashes
3240    match abi.allocate_fd(EPOLL_DUMMY_HANDLE) {
3241        Ok(fd) => fd,
3242        Err(_) => usize::MAX,
3243    }
3244}
3245
3246/// Linux epoll_ctl implementation (stub)
3247///
3248/// Controls an epoll file descriptor by adding, modifying, or removing
3249/// file descriptors from the epoll interest list. This is a stub implementation
3250/// that simply returns success without doing anything.
3251///
3252/// Arguments:
3253/// - abi: LinuxRiscv64Abi context
3254/// - trapframe: Trapframe containing syscall arguments
3255///   - arg0: epfd (epoll file descriptor)
3256///   - arg1: op (operation: EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL)
3257///   - arg2: fd (target file descriptor)
3258///   - arg3: event (pointer to epoll_event structure)
3259///
3260/// Returns:
3261/// - 0 on success
3262/// - usize::MAX (Linux -1) on error
3263pub fn sys_epoll_ctl(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
3264    let task = match mytask() {
3265        Some(t) => t,
3266        None => return usize::MAX,
3267    };
3268
3269    let _epfd = trapframe.get_arg(0) as i32;
3270    let _op = trapframe.get_arg(1) as i32;
3271    let _fd = trapframe.get_arg(2) as i32;
3272    let _event_ptr = trapframe.get_arg(3);
3273
3274    // Increment PC to avoid infinite loop
3275    trapframe.increment_pc_next(task);
3276
3277    // Stub implementation: just return success
3278    // In a real implementation, we would:
3279    // 1. Validate the epoll fd
3280    // 2. Parse the operation (EPOLL_CTL_ADD/MOD/DEL)
3281    // 3. Manage the interest list
3282    // 4. Set up event monitoring
3283    0 // Success
3284}
3285
3286/// Linux epoll_wait implementation (stub)
3287///
3288/// Waits for events on an epoll file descriptor. This is a stub implementation
3289/// that immediately returns 0 (no events ready) to prevent blocking.
3290///
3291/// Arguments:
3292/// - abi: LinuxRiscv64Abi context
3293/// - trapframe: Trapframe containing syscall arguments
3294///   - arg0: epfd (epoll file descriptor)
3295///   - arg1: events (pointer to epoll_event array)
3296///   - arg2: maxevents (maximum number of events)
3297///   - arg3: timeout (timeout in milliseconds)
3298///
3299/// Returns:
3300/// - number of ready events
3301/// - usize::MAX (Linux -1) on error
3302pub fn sys_epoll_wait(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
3303    let task = match mytask() {
3304        Some(t) => t,
3305        None => return usize::MAX,
3306    };
3307
3308    let _epfd = trapframe.get_arg(0) as i32;
3309    let _events_ptr = trapframe.get_arg(1);
3310    let _maxevents = trapframe.get_arg(2) as i32;
3311    let _timeout = trapframe.get_arg(3) as i32;
3312
3313    // Increment PC to avoid infinite loop
3314    trapframe.increment_pc_next(task);
3315
3316    // Stub implementation: return 0 (no events ready)
3317    // In a real implementation, we would:
3318    // 1. Validate the epoll fd
3319    // 2. Check for ready events
3320    // 3. Block if no events and timeout > 0
3321    // 4. Fill the events array with ready events
3322    0 // No events ready
3323}
3324
3325/// Linux epoll_pwait implementation (stub)
3326///
3327/// Like epoll_wait but with signal mask. This is a stub implementation
3328/// that immediately returns 0 (no events ready).
3329///
3330/// Arguments:
3331/// - abi: LinuxRiscv64Abi context
3332/// - trapframe: Trapframe containing syscall arguments
3333///   - arg0: epfd (epoll file descriptor)  
3334///   - arg1: events (pointer to epoll_event array)
3335///   - arg2: maxevents (maximum number of events)
3336///   - arg3: timeout (timeout in milliseconds)
3337///   - arg4: sigmask (signal mask)
3338///
3339/// Returns:
3340/// - number of ready events
3341/// - usize::MAX (Linux -1) on error
3342pub fn sys_epoll_pwait(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
3343    let task = match mytask() {
3344        Some(t) => t,
3345        None => return usize::MAX,
3346    };
3347
3348    let _epfd = trapframe.get_arg(0) as i32;
3349    let _events_ptr = trapframe.get_arg(1);
3350    let _maxevents = trapframe.get_arg(2) as i32;
3351    let _timeout = trapframe.get_arg(3) as i32;
3352    let _sigmask_ptr = trapframe.get_arg(4);
3353
3354    // Increment PC to avoid infinite loop
3355    trapframe.increment_pc_next(task);
3356
3357    // Stub implementation: return 0 (no events ready)
3358    0 // No events ready
3359}
3360
3361/// Minimal Linux pselect6 implementation (stub)
3362///
3363/// Temporary reset: always returns immediately with 0 (no fds ready) and
3364/// does not block. This avoids complex readiness/timeout semantics until
3365/// the final design is in place.
3366///
3367/// Arguments (RISC-V register usage):
3368///   arg0: nfds (number of file descriptors to check)
3369///   arg1: readfds pointer (fd_set*)
3370///   arg2: writefds pointer (fd_set*)
3371///   arg3: exceptfds pointer (fd_set*)
3372///   arg4: timeout pointer (timespec*) or NULL
3373///   arg5: sigmask pointer (ignored)
3374///
3375/// Returns: number of ready descriptors, or -1 (usize::MAX) on error.
3376pub fn sys_pselect6(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
3377    use crate::object::capability::selectable::{ReadyInterest, ReadySet};
3378    use crate::timer::ns_to_ticks;
3379
3380    let task = match mytask() {
3381        Some(t) => t,
3382        None => return usize::MAX,
3383    };
3384
3385    let nfds = trapframe.get_arg(0) as usize;
3386    let readfds_ptr = trapframe.get_arg(1);
3387    let writefds_ptr = trapframe.get_arg(2);
3388    let exceptfds_ptr = trapframe.get_arg(3);
3389    let timeout_ptr = trapframe.get_arg(4);
3390    let _sigmask_ptr = trapframe.get_arg(5);
3391
3392    // Only support up to 64 fds in this minimal implementation
3393    let max_fds = core::cmp::min(nfds, 64);
3394
3395    // Translate fd_set user pointers (treat as u64 bitmask)
3396    let mut in_read: u64 = 0;
3397    let mut in_write: u64 = 0;
3398    let mut in_except: u64 = 0;
3399    if readfds_ptr != 0 {
3400        let kptr = task.vm_manager.translate_vaddr(readfds_ptr).unwrap() as *const u64;
3401        unsafe { in_read = core::ptr::read_unaligned(kptr) };
3402    }
3403    if writefds_ptr != 0 {
3404        let kptr = task.vm_manager.translate_vaddr(writefds_ptr).unwrap() as *const u64;
3405        unsafe { in_write = core::ptr::read_unaligned(kptr) };
3406    }
3407    if exceptfds_ptr != 0 {
3408        let kptr = task.vm_manager.translate_vaddr(exceptfds_ptr).unwrap() as *const u64;
3409        unsafe { in_except = core::ptr::read_unaligned(kptr) };
3410    }
3411
3412    // Parse timeout (timespec)
3413    #[repr(C)]
3414    struct LinuxTimespec {
3415        tv_sec: i64,
3416        tv_nsec: i64,
3417    }
3418    let mut timeout_ticks: Option<u64> = None;
3419    if timeout_ptr != 0 {
3420        let kptr = task.vm_manager.translate_vaddr(timeout_ptr).unwrap() as *const LinuxTimespec;
3421        let ts = unsafe { core::ptr::read_unaligned(kptr) };
3422        // Zero timeout behaves as poll
3423        if ts.tv_sec == 0 && ts.tv_nsec == 0 {
3424            timeout_ticks = Some(0);
3425        } else {
3426            let ns = (ts.tv_sec as i128) * 1_000_000_000i128 + (ts.tv_nsec as i128);
3427            let ns_u = if ns <= 0 { 0 } else { (ns as u128) as u64 };
3428            timeout_ticks = Some(ns_to_ticks(ns_u));
3429        }
3430    }
3431
3432    // First pass: compute immediate readiness; default-ready for non-selectables
3433    let mut out_read: u64 = 0;
3434    let mut out_write: u64 = 0;
3435    let mut out_except: u64 = 0;
3436    let mut any_ready = false;
3437    let mut first_selectable_fd: Option<usize> = None;
3438
3439    for fd in 0..max_fds {
3440        let bit = 1u64 << fd;
3441        let want_read = (in_read & bit) != 0;
3442        let want_write = (in_write & bit) != 0;
3443        let want_except = (in_except & bit) != 0;
3444        if !(want_read || want_write || want_except) {
3445            continue;
3446        }
3447
3448        // Resolve handle → KernelObject
3449        let Some(handle) = abi.get_handle(fd) else {
3450            continue;
3451        };
3452        let Some(kobj) = task.handle_table.get(handle) else {
3453            continue;
3454        };
3455
3456        // Use generic Selectable if available; otherwise default policy
3457        if let Some(sel) = kobj.as_selectable() {
3458            // Remember first selectable for potential blocking
3459            if first_selectable_fd.is_none() {
3460                first_selectable_fd = Some(fd);
3461            }
3462
3463            let interest = ReadyInterest {
3464                read: want_read,
3465                write: want_write,
3466                except: want_except,
3467            };
3468            let rs: ReadySet = sel.current_ready(interest);
3469            if rs.read {
3470                out_read |= bit;
3471                any_ready = true;
3472            }
3473            if rs.write {
3474                out_write |= bit;
3475                any_ready = true;
3476            }
3477            if rs.except {
3478                out_except |= bit; /* any_ready unchanged */
3479            }
3480        } else {
3481            // Default: treat as immediately ready (non-selectable path)
3482            if want_read {
3483                out_read |= bit;
3484                any_ready = true;
3485            }
3486            if want_write {
3487                out_write |= bit;
3488                any_ready = true;
3489            }
3490            // except always false in this minimal implementation
3491        }
3492    }
3493
3494    // If nothing is ready and a non-zero timeout is provided, attempt to block
3495    if !any_ready {
3496        let zero_poll = matches!(timeout_ticks, Some(t) if t == 0);
3497        if !zero_poll {
3498            // Best-effort: wait on the first selectable fd's primary interest
3499            if let Some(fd_wait) = first_selectable_fd {
3500                let bit = 1u64 << fd_wait;
3501                let want_read = (in_read & bit) != 0;
3502                let want_write = (in_write & bit) != 0;
3503                let want_except = (in_except & bit) != 0;
3504                if let Some(handlew) = abi.get_handle(fd_wait) {
3505                    if let Some(kobjw) = task.handle_table.get(handlew) {
3506                        if let Some(sel) = kobjw.as_selectable() {
3507                            let _ = sel.wait_until_ready(
3508                                ReadyInterest {
3509                                    read: want_read,
3510                                    write: want_write,
3511                                    except: want_except,
3512                                },
3513                                trapframe,
3514                                timeout_ticks,
3515                            );
3516                            // After wake or timeout, recompute readiness for all fds properly
3517                            out_read = 0;
3518                            out_write = 0;
3519                            out_except = 0;
3520                            for fd2 in 0..max_fds {
3521                                let bit2 = 1u64 << fd2;
3522                                let want_r = (in_read & bit2) != 0;
3523                                let want_w = (in_write & bit2) != 0;
3524                                let want_x = (in_except & bit2) != 0;
3525                                if !(want_r || want_w || want_x) {
3526                                    continue;
3527                                }
3528                                if let Some(handle2) = abi.get_handle(fd2) {
3529                                    if let Some(kobj2) = task.handle_table.get(handle2) {
3530                                        if let Some(sel2) = kobj2.as_selectable() {
3531                                            let rs2: ReadySet = sel2.current_ready(ReadyInterest {
3532                                                read: want_r,
3533                                                write: want_w,
3534                                                except: want_x,
3535                                            });
3536                                            if rs2.read {
3537                                                out_read |= bit2;
3538                                            }
3539                                            if rs2.write {
3540                                                out_write |= bit2;
3541                                            }
3542                                            if rs2.except {
3543                                                out_except |= bit2;
3544                                            }
3545                                        } else {
3546                                            if want_r {
3547                                                out_read |= bit2;
3548                                            }
3549                                            if want_w {
3550                                                out_write |= bit2;
3551                                            }
3552                                        }
3553                                    }
3554                                }
3555                            }
3556                        }
3557                    }
3558                }
3559            }
3560        }
3561    }
3562
3563    // Write back fd_sets with results
3564    if readfds_ptr != 0 {
3565        let kptr = task.vm_manager.translate_vaddr(readfds_ptr).unwrap() as *mut u64;
3566        unsafe { core::ptr::write_unaligned(kptr, out_read) };
3567    }
3568    if writefds_ptr != 0 {
3569        let kptr = task.vm_manager.translate_vaddr(writefds_ptr).unwrap() as *mut u64;
3570        unsafe { core::ptr::write_unaligned(kptr, out_write) };
3571    }
3572    if exceptfds_ptr != 0 {
3573        let kptr = task.vm_manager.translate_vaddr(exceptfds_ptr).unwrap() as *mut u64;
3574        unsafe { core::ptr::write_unaligned(kptr, out_except) };
3575    }
3576
3577    // Return count of ready fds
3578    let ready_count = out_read.count_ones() as usize
3579        + out_write.count_ones() as usize
3580        + out_except.count_ones() as usize;
3581
3582    trapframe.increment_pc_next(task);
3583    ready_count
3584}
3585
3586/// Minimal Linux ppoll implementation
3587///
3588/// Arguments (RISC-V):
3589///   arg0: fds_ptr (struct pollfd*)
3590///   arg1: nfds (usize)
3591///   arg2: timeout_ptr (timespec*) or NULL
3592///   arg3: sigmask (ignored)
3593///   arg4: sigsetsize (ignored)
3594///
3595/// Returns: number of fds with non-zero revents, or -1 (usize::MAX) on error.
3596pub fn sys_ppoll(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
3597    use crate::object::capability::selectable::{ReadyInterest, ReadySet};
3598    use crate::timer::ns_to_ticks;
3599
3600    let task = match mytask() {
3601        Some(t) => t,
3602        None => return usize::MAX,
3603    };
3604
3605    #[repr(C)]
3606    struct PollFd {
3607        fd: i32,
3608        events: i16,
3609        revents: i16,
3610    }
3611
3612    const POLLIN: i16 = 0x0001;
3613    const POLLPRI: i16 = 0x0002;
3614    const POLLOUT: i16 = 0x0004;
3615    const POLLERR: i16 = 0x0008;
3616    const POLLHUP: i16 = 0x0010;
3617    const POLLNVAL: i16 = 0x0020;
3618
3619    let fds_ptr = trapframe.get_arg(0);
3620    let nfds = trapframe.get_arg(1) as usize;
3621    let timeout_ptr = trapframe.get_arg(2);
3622    let _sigmask = trapframe.get_arg(3);
3623    let _sigsetsize = trapframe.get_arg(4);
3624
3625    trapframe.increment_pc_next(task);
3626
3627    if fds_ptr == 0 {
3628        return usize::MAX;
3629    }
3630    let kptr = match task.vm_manager.translate_vaddr(fds_ptr) {
3631        Some(p) => p as *mut PollFd,
3632        None => return usize::MAX,
3633    };
3634    if kptr.is_null() {
3635        return usize::MAX;
3636    }
3637    let fds: &mut [PollFd] = unsafe { core::slice::from_raw_parts_mut(kptr, nfds) };
3638
3639    #[repr(C)]
3640    struct LinuxTimespec {
3641        tv_sec: i64,
3642        tv_nsec: i64,
3643    }
3644    let mut timeout_ticks: Option<u64> = None;
3645    if timeout_ptr != 0 {
3646        let tsp = match task.vm_manager.translate_vaddr(timeout_ptr) {
3647            Some(p) => p as *const LinuxTimespec,
3648            None => return usize::MAX,
3649        };
3650        let ts = unsafe { core::ptr::read_unaligned(tsp) };
3651        if ts.tv_sec == 0 && ts.tv_nsec == 0 {
3652            timeout_ticks = Some(0);
3653        } else {
3654            let ns = (ts.tv_sec as i128) * 1_000_000_000i128 + (ts.tv_nsec as i128);
3655            let ns_u = if ns <= 0 { 0 } else { (ns as u128) as u64 };
3656            timeout_ticks = Some(ns_to_ticks(ns_u));
3657            // crate::println!(
3658            //     "[sys_ppoll] timeout ts={}s {}ns -> ns={} ticks={:?}",
3659            //     ts.tv_sec,
3660            //     ts.tv_nsec,
3661            //     ns_u,
3662            //     timeout_ticks
3663            // );
3664        }
3665    }
3666
3667    struct EvalResult {
3668        ready: bool,
3669        selectable: bool,
3670    }
3671
3672    fn eval_pfd(
3673        pfd: &mut PollFd,
3674        abi_ref: &LinuxRiscv64Abi,
3675        task_ref: &crate::task::Task,
3676    ) -> EvalResult {
3677        pfd.revents = 0;
3678        if pfd.fd < 0 {
3679            pfd.revents |= POLLNVAL;
3680            return EvalResult {
3681                ready: true,
3682                selectable: false,
3683            };
3684        }
3685        let fd_usize = pfd.fd as usize;
3686        let Some(handle) = abi_ref.get_handle(fd_usize) else {
3687            pfd.revents |= POLLNVAL;
3688            return EvalResult {
3689                ready: true,
3690                selectable: false,
3691            };
3692        };
3693        let Some(kobj) = task_ref.handle_table.get(handle) else {
3694            pfd.revents |= POLLNVAL;
3695            return EvalResult {
3696                ready: true,
3697                selectable: false,
3698            };
3699        };
3700
3701        let want_read = (pfd.events & POLLIN) != 0;
3702        let want_write = (pfd.events & POLLOUT) != 0;
3703        let want_except = (pfd.events & POLLPRI) != 0;
3704
3705        let mut selectable = false;
3706
3707        if let Some(sel) = kobj.as_selectable() {
3708            selectable = true;
3709            let rs: ReadySet = sel.current_ready(ReadyInterest {
3710                read: want_read,
3711                write: want_write,
3712                except: want_except,
3713            });
3714            if rs.read && want_read {
3715                pfd.revents |= POLLIN;
3716            }
3717            if rs.write && want_write {
3718                pfd.revents |= POLLOUT;
3719            }
3720            if rs.except && want_except {
3721                pfd.revents |= POLLPRI;
3722            }
3723        } else {
3724            if want_read {
3725                pfd.revents |= POLLIN;
3726            }
3727            if want_write {
3728                pfd.revents |= POLLOUT;
3729            }
3730        }
3731
3732        if let Some(pipe) = kobj.as_pipe() {
3733            if pipe.is_readable() && !pipe.has_writers() {
3734                pfd.revents |= POLLHUP;
3735                if want_read && (pfd.revents & POLLIN) == 0 {
3736                    pfd.revents |= POLLIN;
3737                }
3738            }
3739            if pipe.is_writable() && !pipe.has_readers() {
3740                pfd.revents |= POLLERR | POLLHUP;
3741            }
3742        }
3743
3744        EvalResult {
3745            ready: pfd.revents != 0,
3746            selectable,
3747        }
3748    }
3749
3750    let mut any_ready = false;
3751    let mut first_selectable_index: Option<usize> = None;
3752    let mut selectable_count = 0usize;
3753    let mut ready_count = 0usize;
3754    {
3755        let abi_ref = &*abi;
3756        let task_ref: &crate::task::Task = &*task;
3757        for (idx, pfd) in fds.iter_mut().enumerate() {
3758            let eval = eval_pfd(pfd, abi_ref, task_ref);
3759            if eval.ready {
3760                any_ready = true;
3761                ready_count += 1;
3762            }
3763            if eval.selectable {
3764                selectable_count += 1;
3765            }
3766            if first_selectable_index.is_none() && eval.selectable {
3767                first_selectable_index = Some(idx);
3768            }
3769
3770            let mut kind = "unknown";
3771            let mut socket_state: Option<u32> = None;
3772            let fd_usize = pfd.fd as usize;
3773            if let Some(handle) = abi_ref.get_handle(fd_usize) {
3774                if let Some(kobj) = task_ref.handle_table.get(handle) {
3775                    match kobj {
3776                        crate::object::KernelObject::File(_) => {
3777                            kind = "file";
3778                        }
3779                        crate::object::KernelObject::Pipe(_) => {
3780                            kind = "pipe";
3781                        }
3782                        crate::object::KernelObject::Counter(_) => {
3783                            kind = "counter";
3784                        }
3785                        crate::object::KernelObject::EventChannel(_) => {
3786                            kind = "event_channel";
3787                        }
3788                        crate::object::KernelObject::EventSubscription(_) => {
3789                            kind = "event_sub";
3790                        }
3791                        #[cfg(feature = "network")]
3792                        crate::object::KernelObject::Socket(socket) => {
3793                            kind = "socket";
3794                            socket_state = Some(socket.state() as u32);
3795                        }
3796                        crate::object::KernelObject::SharedMemory(_) => {
3797                            kind = "shmem";
3798                        }
3799                    }
3800                }
3801            }
3802
3803            // crate::println!(
3804            //     "[sys_ppoll] fd={} events=0x{:x} revents=0x{:x} selectable={} kind={} socket_state={:?}",
3805            //     pfd.fd,
3806            //     pfd.events as u16,
3807            //     pfd.revents as u16,
3808            //     eval.selectable,
3809            //     kind,
3810            //     socket_state
3811            // );
3812        }
3813    }
3814
3815    {
3816        let zero_poll = matches!(timeout_ticks, Some(t) if t == 0);
3817        // crate::println!(
3818        //     "[sys_ppoll] task={} nfds={} selectable={} ready={} any_ready={} zero_poll={} timeout_ticks={:?} first_selectable={:?}",
3819        //     task.name,
3820        //     nfds,
3821        //     selectable_count,
3822        //     ready_count,
3823        //     any_ready,
3824        //     zero_poll,
3825        //     timeout_ticks,
3826        //     first_selectable_index
3827        // );
3828    }
3829
3830    if !any_ready {
3831        let zero_poll = matches!(timeout_ticks, Some(t) if t == 0);
3832        if !zero_poll {
3833            if selectable_count > 1 {
3834                use crate::timer::get_tick;
3835
3836                let deadline = timeout_ticks.map(|ticks| get_tick().saturating_add(ticks));
3837                loop {
3838                    if let Some(deadline) = deadline {
3839                        let now = get_tick();
3840                        if now >= deadline {
3841                            break;
3842                        }
3843                        let remaining = deadline.saturating_sub(now);
3844                        if remaining == 0 {
3845                            break;
3846                        }
3847                    }
3848
3849                    task.sleep(trapframe, 1);
3850
3851                    any_ready = false;
3852                    ready_count = 0;
3853                    {
3854                        let abi_ref = &*abi;
3855                        let task_ref: &crate::task::Task = &*task;
3856                        for pfd in fds.iter_mut() {
3857                            let eval = eval_pfd(pfd, abi_ref, task_ref);
3858                            if eval.ready {
3859                                any_ready = true;
3860                                ready_count += 1;
3861                            }
3862                        }
3863                    }
3864
3865                    if any_ready {
3866                        break;
3867                    }
3868                }
3869            } else if let Some(wait_idx) = first_selectable_index {
3870                let pfd = &fds[wait_idx];
3871                if pfd.fd >= 0 {
3872                    let fd_usize = pfd.fd as usize;
3873                    let abi_ref = &*abi;
3874                    let task_ref: &crate::task::Task = &*task;
3875                    if let Some(handle) = abi_ref.get_handle(fd_usize) {
3876                        if let Some(kobj) = task_ref.handle_table.get(handle) {
3877                            if let Some(sel) = kobj.as_selectable() {
3878                                let want_read = (pfd.events & POLLIN) != 0;
3879                                let want_write = (pfd.events & POLLOUT) != 0;
3880                                let want_except = (pfd.events & POLLPRI) != 0;
3881                                let _ = sel.wait_until_ready(
3882                                    ReadyInterest {
3883                                        read: want_read,
3884                                        write: want_write,
3885                                        except: want_except,
3886                                    },
3887                                    trapframe,
3888                                    timeout_ticks,
3889                                );
3890                            }
3891                        }
3892                    }
3893                }
3894
3895                let abi_ref = &*abi;
3896                let task_ref: &crate::task::Task = &*task;
3897                for pfd in fds.iter_mut() {
3898                    let _ = eval_pfd(pfd, abi_ref, task_ref);
3899                }
3900            }
3901        }
3902    }
3903
3904    let mut count = 0usize;
3905    for pfd in fds.iter() {
3906        if pfd.revents != 0 {
3907            count += 1;
3908        }
3909    }
3910    count
3911}
3912
3913/// Linux sys_fchmod implementation (stub)
3914///
3915/// Changes the permissions of a file using its file descriptor.
3916/// This is a stub implementation that simply validates the file descriptor
3917/// and returns success without actually changing permissions.
3918///
3919/// Arguments:
3920/// - abi: LinuxRiscv64Abi context
3921/// - trapframe: Trapframe containing syscall arguments
3922///   - arg0: fd (file descriptor)
3923///   - arg1: mode (new file permissions)
3924///
3925/// Returns:
3926/// - 0 on success (if fd is valid)
3927/// - usize::MAX (Linux -1) on error (if fd is invalid)
3928pub fn sys_fchmod(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
3929    let task = match mytask() {
3930        Some(t) => t,
3931        None => return usize::MAX,
3932    };
3933
3934    let fd = trapframe.get_arg(0) as i32;
3935    let _mode = trapframe.get_arg(1) as u32;
3936
3937    // Increment PC to avoid infinite loop
3938    trapframe.increment_pc_next(task);
3939
3940    // Validate the file descriptor
3941    let handle = match abi.get_handle(fd as usize) {
3942        Some(h) => h,
3943        None => return usize::MAX, // Invalid file descriptor
3944    };
3945
3946    // Check if the handle exists in the handle table
3947    match task.handle_table.get(handle) {
3948        Some(_) => {
3949            // File descriptor is valid, return success
3950            // In a real implementation, we would change the file permissions here
3951            0 // Success
3952        }
3953        None => usize::MAX, // Handle not found
3954    }
3955}
3956
3957/// Linux sys_umask implementation (stub)
3958///
3959/// Sets the file mode creation mask (umask) and returns the previous value.
3960/// This is a stub implementation that simply returns the provided mask
3961/// without actually storing or using it for file creation permissions.
3962///
3963/// Arguments:
3964/// - abi: LinuxRiscv64Abi context
3965/// - trapframe: Trapframe containing syscall arguments
3966///   - arg0: mask (new file creation mask)
3967///
3968/// Returns:
3969/// - The provided mask value (simulating the previous umask)
3970pub fn sys_umask(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
3971    let task = match mytask() {
3972        Some(t) => t,
3973        None => return usize::MAX,
3974    };
3975
3976    let mask = trapframe.get_arg(0) as u32;
3977
3978    // Increment PC to avoid infinite loop
3979    trapframe.increment_pc_next(task);
3980
3981    // In a real implementation, we would:
3982    // 1. Store the current umask value to return it
3983    // 2. Set the new umask value for future file creation operations
3984    // 3. Return the previous umask value
3985    //
3986    // For this stub implementation, we simply return the provided mask
3987    // This satisfies most applications that just want to set a umask
3988    mask as usize // Return the provided mask as if it was the previous value
3989}
3990
3991/// Linux sys_readlinkat implementation
3992///
3993/// Reads the target of a symbolic link relative to a directory file descriptor.
3994/// Properly queries the VFS and does not append a null terminator.
3995///
3996/// Arguments:
3997/// - abi: LinuxRiscv64Abi context  
3998/// - trapframe: Trapframe containing syscall arguments
3999///   - arg0: dirfd (directory file descriptor or AT_FDCWD)
4000///   - arg1: pathname (pointer to path string)
4001///   - arg2: buf (buffer to store link contents)
4002///   - arg3: bufsiz (size of buffer)
4003///
4004/// Returns:
4005/// - Number of bytes placed in buf on success
4006/// - usize::MAX (Linux -1) on error
4007pub fn sys_readlinkat(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
4008    let task = match mytask() {
4009        Some(t) => t,
4010        None => return errno::to_result(errno::EIO),
4011    };
4012
4013    let dirfd = trapframe.get_arg(0) as i32;
4014    let pathname_ptr = trapframe.get_arg(1);
4015    let buf_ptr = trapframe.get_arg(2);
4016    let bufsiz = trapframe.get_arg(3) as usize;
4017
4018    // Increment PC to avoid infinite loop
4019    trapframe.increment_pc_next(task);
4020
4021    // Fast path: zero-sized buffer
4022    if bufsiz == 0 {
4023        return 0;
4024    }
4025
4026    // Parse the pathname
4027    let path_str = match parse_c_string_from_userspace(task, pathname_ptr, MAX_PATH_LENGTH) {
4028        Ok(s) => s,
4029        Err(_) => return errno::to_result(errno::EFAULT),
4030    };
4031
4032    // Acquire VFS
4033    let vfs = match task.vfs.read().clone() {
4034        Some(v) => v,
4035        None => return errno::to_result(errno::EIO),
4036    };
4037
4038    // Determine base directory (entry and mount) for path resolution
4039    use crate::fs::vfs_v2::core::VfsFileObject;
4040    const AT_FDCWD: i32 = -100;
4041
4042    let (base_entry, base_mount) = if dirfd == AT_FDCWD {
4043        vfs.get_cwd().unwrap_or_else(|| {
4044            let root_mount = vfs.mount_tree.root_mount.read().clone();
4045            (root_mount.root.clone(), root_mount)
4046        })
4047    } else {
4048        // Resolve base from dirfd
4049        let handle = match abi.get_handle(dirfd as usize) {
4050            Some(h) => h,
4051            None => return errno::to_result(errno::EBADF),
4052        };
4053        let kernel_obj = match task.handle_table.get(handle) {
4054            Some(obj) => obj,
4055            None => return errno::to_result(errno::EBADF),
4056        };
4057        let file_obj = match kernel_obj.as_file() {
4058            Some(f) => f,
4059            None => return errno::to_result(errno::ENOTDIR),
4060        };
4061        let vfs_file_obj = match file_obj.as_any().downcast_ref::<VfsFileObject>() {
4062            Some(vfs_obj) => vfs_obj,
4063            None => return errno::to_result(errno::ENOTDIR),
4064        };
4065        (
4066            vfs_file_obj.get_vfs_entry().clone(),
4067            vfs_file_obj.get_mount_point().clone(),
4068        )
4069    };
4070
4071    // Resolve the path from the base (do not follow the final link)
4072    let (entry, _mp) = match vfs.resolve_path_from(&base_entry, &base_mount, &path_str) {
4073        Ok(v) => v,
4074        Err(e) => return errno::to_result(errno::from_fs_error(&e)),
4075    };
4076
4077    // Ensure the target is a symlink and obtain its target
4078    let node = entry.node();
4079    let metadata = match node.metadata() {
4080        Ok(m) => m,
4081        Err(e) => return errno::to_result(errno::from_fs_error(&e)),
4082    };
4083
4084    let target = match metadata.file_type {
4085        FileType::SymbolicLink(ref t) => t.as_str(),
4086        _ => return errno::to_result(errno::EINVAL), // Not a symlink
4087    };
4088
4089    // Copy to user buffer (no null terminator), truncated if needed
4090    let target_bytes = target.as_bytes();
4091    let copy_len = core::cmp::min(target_bytes.len(), bufsiz);
4092
4093    let user_buf = match task.vm_manager.translate_vaddr(buf_ptr) {
4094        Some(addr) => addr as *mut u8,
4095        None => return errno::to_result(errno::EFAULT),
4096    };
4097
4098    if copy_len > 0 {
4099        unsafe {
4100            core::ptr::copy_nonoverlapping(target_bytes.as_ptr(), user_buf, copy_len);
4101        }
4102    }
4103
4104    copy_len
4105}
4106
4107/// Linux sys_getrandom implementation
4108///
4109/// Fills the user buffer with random bytes from the kernel pool.
4110pub fn sys_getrandom(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
4111    let task = match mytask() {
4112        Some(t) => t,
4113        None => return errno::to_result(errno::EIO),
4114    };
4115
4116    let buf_ptr = trapframe.get_arg(0);
4117    let buflen = trapframe.get_arg(1) as usize;
4118    let flags = trapframe.get_arg(2) as u32;
4119
4120    trapframe.increment_pc_next(task);
4121
4122    if buflen == 0 {
4123        return 0;
4124    }
4125
4126    let user_buf = match task.vm_manager.translate_vaddr(buf_ptr) {
4127        Some(addr) => addr as *mut u8,
4128        None => return errno::to_result(errno::EFAULT),
4129    };
4130
4131    if user_buf.is_null() {
4132        return errno::to_result(errno::EFAULT);
4133    }
4134
4135    let buffer = unsafe { core::slice::from_raw_parts_mut(user_buf, buflen) };
4136    let bytes_read = crate::random::RandomManager::get_random_bytes(buffer);
4137
4138    if bytes_read == 0 {
4139        if (flags & GRND_NONBLOCK) != 0 {
4140            return errno::to_result(errno::EAGAIN);
4141        }
4142        fill_pseudo_random(buffer);
4143        return buflen;
4144    }
4145
4146    if bytes_read < buflen && (flags & GRND_NONBLOCK) == 0 {
4147        fill_pseudo_random(&mut buffer[bytes_read..]);
4148        return buflen;
4149    }
4150
4151    bytes_read
4152}
4153
4154/// Linux sys_getcwd system call implementation
4155/// Get current working directory
4156///
4157/// Arguments:
4158/// - buf: Buffer to store the current working directory path
4159/// - size: Size of the buffer
4160///
4161/// Returns:
4162/// - Number of bytes written to buffer on success
4163/// - usize::MAX on error
4164pub fn sys_getcwd(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
4165    let task = mytask().unwrap();
4166    let buf_ptr = trapframe.get_arg(0);
4167    let size = trapframe.get_arg(1);
4168    trapframe.increment_pc_next(task);
4169
4170    // Check for invalid arguments
4171    if buf_ptr == 0 || size == 0 {
4172        return usize::MAX; // EFAULT or EINVAL
4173    }
4174
4175    // Get current working directory from task context
4176    let cwd = if let Some(vfs) = task.vfs.read().clone() {
4177        vfs.get_cwd_path()
4178    } else {
4179        "/".to_string() // Default to root if no VFS manager
4180    };
4181    let cwd_bytes = cwd.as_bytes();
4182
4183    // Check if buffer is large enough (including null terminator)
4184    if cwd_bytes.len() + 1 > size {
4185        return usize::MAX; // ERANGE - buffer too small
4186    }
4187
4188    // Translate user buffer address
4189    let user_buf = match task.vm_manager.translate_vaddr(buf_ptr) {
4190        Some(addr) => addr as *mut u8,
4191        None => return usize::MAX, // EFAULT - invalid buffer address
4192    };
4193
4194    // Copy current working directory to user buffer
4195    unsafe {
4196        core::ptr::copy_nonoverlapping(cwd_bytes.as_ptr(), user_buf, cwd_bytes.len());
4197        // Add null terminator
4198        *user_buf.add(cwd_bytes.len()) = 0;
4199    }
4200
4201    // Return the number of bytes written (including null terminator)
4202    cwd_bytes.len() + 1
4203}
4204
4205/// Linux sys_chdir system call implementation (syscall 49)
4206/// Change current working directory
4207///
4208/// Arguments:
4209/// - path: Path to the new working directory
4210///
4211/// Returns:
4212/// - 0 on success
4213/// - usize::MAX on error (path not found, not a directory, permission denied, etc.)
4214pub fn sys_chdir(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
4215    let task = match mytask() {
4216        Some(t) => t,
4217        None => return usize::MAX,
4218    };
4219
4220    let path_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(0)) {
4221        Some(ptr) => ptr as *const u8,
4222        None => return usize::MAX,
4223    };
4224
4225    // Increment PC to avoid infinite loop
4226    trapframe.increment_pc_next(task);
4227
4228    // Parse path from user space
4229    let path_str = match cstring_to_string(path_ptr, MAX_PATH_LENGTH) {
4230        Ok((path, _)) => path,
4231        Err(_) => return usize::MAX, // Invalid UTF-8 or path too long
4232    };
4233
4234    crate::println!("sys_chdir: Changing directory to '{}'", path_str);
4235
4236    // Convert to absolute path
4237    let absolute_path = if path_str.starts_with('/') {
4238        path_str
4239    } else {
4240        match to_absolute_path_v2(&task, &path_str) {
4241            Ok(p) => p,
4242            Err(_) => return usize::MAX,
4243        }
4244    };
4245
4246    let vfs = match task.vfs.read().clone() {
4247        Some(v) => v,
4248        None => return usize::MAX,
4249    };
4250
4251    // Check if the path exists and is a directory
4252    match vfs.resolve_path(&absolute_path) {
4253        Ok((entry, _mount_point)) => {
4254            match entry.node().file_type() {
4255                Ok(file_type) => {
4256                    if file_type == FileType::Directory {
4257                        // Update the current working directory via VfsManager
4258                        match vfs.set_cwd_by_path(&absolute_path) {
4259                            Ok(()) => {
4260                                crate::println!(
4261                                    "sys_chdir: Successfully changed directory to '{}'",
4262                                    absolute_path
4263                                );
4264                                0 // Success
4265                            }
4266                            Err(_) => {
4267                                crate::println!(
4268                                    "sys_chdir: Failed to set working directory to '{}'",
4269                                    absolute_path
4270                                );
4271                                usize::MAX // Failed to set cwd
4272                            }
4273                        }
4274                    } else {
4275                        crate::println!("sys_chdir: '{}' is not a directory", absolute_path);
4276                        usize::MAX // Not a directory (ENOTDIR)
4277                    }
4278                }
4279                Err(_) => {
4280                    crate::println!("sys_chdir: Failed to get file type for '{}'", absolute_path);
4281                    usize::MAX // Failed to get file type
4282                }
4283            }
4284        }
4285        Err(_) => {
4286            crate::println!("sys_chdir: Path '{}' not found", absolute_path);
4287            usize::MAX // Path not found (ENOENT)
4288        }
4289    }
4290}
4291
4292// renameat2 flags
4293const RENAME_NOREPLACE: u32 = 1 << 0; // Don't overwrite target
4294const RENAME_EXCHANGE: u32 = 1 << 1; // Exchange source and target
4295#[allow(dead_code)]
4296const RENAME_WHITEOUT: u32 = 1 << 2; // Create whiteout object
4297
4298/// Linux sys_renameat2 system call implementation (syscall 276)
4299/// Rename/move a file or directory with additional flags
4300///
4301/// Arguments:
4302/// - olddirfd: Old directory file descriptor (or AT_FDCWD)
4303/// - oldpath: Pointer to old path string
4304/// - newdirfd: New directory file descriptor (or AT_FDCWD)  
4305/// - newpath: Pointer to new path string
4306/// - flags: Rename operation flags
4307///
4308/// Returns:
4309/// - 0 on success
4310/// - usize::MAX on error
4311pub fn sys_renameat2(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
4312    let task = match mytask() {
4313        Some(t) => t,
4314        None => return usize::MAX,
4315    };
4316
4317    let olddirfd = trapframe.get_arg(0) as i32;
4318    let oldpath_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(1)) {
4319        Some(ptr) => ptr as *const u8,
4320        None => return usize::MAX,
4321    };
4322    let newdirfd = trapframe.get_arg(2) as i32;
4323    let newpath_ptr = match task.vm_manager.translate_vaddr(trapframe.get_arg(3)) {
4324        Some(ptr) => ptr as *const u8,
4325        None => return usize::MAX,
4326    };
4327    let flags = trapframe.get_arg(4) as u32;
4328
4329    // Increment PC to avoid infinite loop
4330    trapframe.increment_pc_next(task);
4331
4332    // Parse old path from user space
4333    let oldpath_str = match cstring_to_string(oldpath_ptr, MAX_PATH_LENGTH) {
4334        Ok((path, _)) => path,
4335        Err(_) => return usize::MAX, // Invalid UTF-8 or path too long
4336    };
4337
4338    // Parse new path from user space
4339    let newpath_str = match cstring_to_string(newpath_ptr, MAX_PATH_LENGTH) {
4340        Ok((path, _)) => path,
4341        Err(_) => return usize::MAX, // Invalid UTF-8 or path too long
4342    };
4343
4344    crate::println!(
4345        "sys_renameat2: olddirfd={}, oldpath='{}', newdirfd={}, newpath='{}', flags={:#x}",
4346        olddirfd,
4347        oldpath_str,
4348        newdirfd,
4349        newpath_str,
4350        flags
4351    );
4352
4353    // Check for unsupported flags
4354    const SUPPORTED_FLAGS: u32 = RENAME_NOREPLACE | RENAME_EXCHANGE;
4355    if (flags & !SUPPORTED_FLAGS) != 0 {
4356        crate::println!(
4357            "sys_renameat2: Unsupported flags: {:#x}",
4358            flags & !SUPPORTED_FLAGS
4359        );
4360        return usize::MAX; // EINVAL - unsupported flags
4361    }
4362
4363    // RENAME_EXCHANGE and RENAME_NOREPLACE are mutually exclusive
4364    if (flags & RENAME_EXCHANGE) != 0 && (flags & RENAME_NOREPLACE) != 0 {
4365        crate::println!(
4366            "sys_renameat2: RENAME_EXCHANGE and RENAME_NOREPLACE are mutually exclusive"
4367        );
4368        return usize::MAX; // EINVAL
4369    }
4370
4371    let vfs = match task.vfs.read().clone() {
4372        Some(v) => v,
4373        None => return usize::MAX,
4374    };
4375
4376    // Note: Current implementation ignores dirfd and only uses absolute path resolution
4377    // TODO: Implement proper *at support for relative paths from directory file descriptors
4378
4379    // Resolve absolute paths using basic path resolution
4380    let old_absolute_path = if oldpath_str.starts_with('/') {
4381        oldpath_str
4382    } else {
4383        match to_absolute_path_v2(&task, &oldpath_str) {
4384            Ok(p) => p,
4385            Err(_) => return usize::MAX,
4386        }
4387    };
4388
4389    let new_absolute_path = if newpath_str.starts_with('/') {
4390        newpath_str
4391    } else {
4392        match to_absolute_path_v2(&task, &newpath_str) {
4393            Ok(p) => p,
4394            Err(_) => return usize::MAX,
4395        }
4396    };
4397
4398    crate::println!(
4399        "sys_renameat2: Resolved paths: '{}' -> '{}'",
4400        old_absolute_path,
4401        new_absolute_path
4402    );
4403
4404    // Handle different rename operations based on flags
4405    if (flags & RENAME_EXCHANGE) != 0 {
4406        // Exchange operation: swap the two files/directories
4407        crate::println!("sys_renameat2: Exchange operation not yet implemented");
4408        return usize::MAX; // ENOSYS - not implemented
4409    } else {
4410        // Standard rename/move operation
4411        let no_replace = (flags & RENAME_NOREPLACE) != 0;
4412
4413        // Check if target exists when RENAME_NOREPLACE is set
4414        if no_replace {
4415            match vfs.resolve_path(&new_absolute_path) {
4416                Ok(_) => {
4417                    crate::println!(
4418                        "sys_renameat2: Target exists and RENAME_NOREPLACE flag is set"
4419                    );
4420                    return usize::MAX; // EEXIST - target exists
4421                }
4422                Err(_) => {
4423                    // Target doesn't exist, which is what we want for RENAME_NOREPLACE
4424                }
4425            }
4426        }
4427
4428        // Implement rename as a combination of copy and remove (simplified approach)
4429        // This is not ideal for atomic operations but works with current VFS API
4430        // TODO: Implement proper atomic rename operation in VfsManager
4431
4432        // For now, return not implemented for most cases
4433        // In practice, this would need proper filesystem-level rename support
4434        crate::println!("sys_renameat2: Full rename operation not yet implemented");
4435        0 // Return success for basic compatibility (temporary)
4436    }
4437}
4438
4439/// eventfd2 - create file descriptor for event notification
4440///
4441/// # Arguments
4442/// * `initval` - Initial value of the event counter (unsigned int)
4443/// * `flags` - Flags (bitwise OR of EFD_CLOEXEC, EFD_NONBLOCK, EFD_SEMAPHORE)
4444///
4445/// # Returns
4446/// * New file descriptor on success
4447/// * Error code on failure
4448pub fn sys_eventfd2(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
4449    let task = match mytask() {
4450        Some(t) => t,
4451        None => return errno::to_result(errno::EIO),
4452    };
4453
4454    let initval = trapframe.get_arg(0) as u32;
4455    let flags = trapframe.get_arg(1) as u32;
4456
4457    trapframe.increment_pc_next(task);
4458
4459    // eventfd2 flags
4460    const EFD_CLOEXEC: u32 = 0o02000000;
4461    const EFD_NONBLOCK: u32 = 0o00004000;
4462    const EFD_SEMAPHORE: u32 = 0x00000001;
4463
4464    // Validate flags (only allow EFD_CLOEXEC, EFD_NONBLOCK, EFD_SEMAPHORE)
4465    let valid_flags = EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE;
4466    if flags & !valid_flags != 0 {
4467        crate::println!("[sys_eventfd2] Invalid flags: 0x{:x}", flags);
4468        return errno::to_result(errno::EINVAL);
4469    }
4470
4471    // Create Counter object
4472    let counter_obj = crate::ipc::counter::Counter::create_kernel_object(initval, flags);
4473
4474    // Insert into handle table
4475    let handle = match task.handle_table.insert(counter_obj) {
4476        Ok(h) => h,
4477        Err(_) => return errno::to_result(errno::EMFILE),
4478    };
4479
4480    // Allocate file descriptor for the handle
4481    let fd = match abi.allocate_fd(handle as u32) {
4482        Ok(fd) => fd,
4483        Err(_) => {
4484            let _ = task.handle_table.remove(handle);
4485            return errno::to_result(errno::EMFILE);
4486        }
4487    };
4488
4489    // Set FD_CLOEXEC if requested
4490    if (flags & EFD_CLOEXEC) != 0 {
4491        let _ = abi.set_fd_flags(fd, FD_CLOEXEC);
4492    }
4493
4494    // Set O_NONBLOCK if requested (already set in Counter, but also in ABI for consistency)
4495    if (flags & EFD_NONBLOCK) != 0 {
4496        let _ = abi.set_file_status_flags(fd, O_NONBLOCK as u32);
4497    }
4498
4499    fd
4500}