kernel/abi/xv6/riscv64/
file.rs

1use crate::{
2    abi::xv6::riscv64::fs::xv6fs::{Dirent, Stat},
3    arch::Trapframe,
4    device::manager::DeviceManager,
5    executor::TransparentExecutor,
6    fs::{
7        DeviceFileInfo,
8        DirectoryEntry, // Legacy support for conversion
9        FileType,
10        SeekFrom,
11    },
12    library::std::string::{
13        cstring_to_string, parse_c_string_from_userspace, parse_string_array_from_userspace,
14    },
15    task::mytask,
16};
17use alloc::{
18    string::{String, ToString},
19    sync::Arc,
20    vec,
21    vec::Vec,
22};
23
24/// Convert Scarlet DirectoryEntry to xv6 Dirent and write to buffer
25fn read_directory_as_xv6_dirent(buf_ptr: *mut u8, count: usize, buffer_data: &[u8]) -> usize {
26    if count < Dirent::DIRENT_SIZE {
27        return 0; // Buffer too small for even one entry
28    }
29
30    // Parse DirectoryEntry from buffer data
31    if let Some(dir_entry) = DirectoryEntry::parse(buffer_data) {
32        // Convert Scarlet DirectoryEntry to xv6 Dirent
33        let inum = (dir_entry.file_id & 0xFFFF) as u16; // Use lower 16 bits as inode number
34        let name = dir_entry.name_str().unwrap_or("");
35
36        let xv6_dirent = Dirent::new(inum, name);
37
38        // Check if we have enough space
39        if count >= Dirent::DIRENT_SIZE {
40            // Copy the dirent to the buffer
41            let dirent_bytes = xv6_dirent.as_bytes();
42            unsafe {
43                core::ptr::copy_nonoverlapping(dirent_bytes.as_ptr(), buf_ptr, Dirent::DIRENT_SIZE);
44            }
45            return Dirent::DIRENT_SIZE;
46        }
47    }
48
49    0 // No data or error
50}
51
52const MAX_PATH_LENGTH: usize = 128;
53const MAX_ARG_COUNT: usize = 64;
54
55pub fn sys_exec(
56    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
57    trapframe: &mut Trapframe,
58) -> usize {
59    let task = mytask().unwrap();
60
61    // Increment PC to avoid infinite loop if execve fails
62    trapframe.increment_pc_next(task);
63
64    // Get arguments from trapframe
65    let path_ptr = trapframe.get_arg(0);
66    let argv_ptr = trapframe.get_arg(1);
67
68    // Parse path
69    let path_str = match parse_c_string_from_userspace(task, path_ptr, MAX_PATH_LENGTH) {
70        Ok(path) => match to_absolute_path_v2(&task, &path) {
71            Ok(abs_path) => abs_path,
72            Err(_) => return usize::MAX, // Path error
73        },
74        Err(_) => return usize::MAX, // Path parsing error
75    };
76
77    // Parse argv and envp
78    let argv_strings =
79        match parse_string_array_from_userspace(task, argv_ptr, MAX_ARG_COUNT, MAX_PATH_LENGTH) {
80            Ok(args) => args,
81            Err(_) => return usize::MAX, // argv parsing error
82        };
83
84    // Convert Vec<String> to Vec<&str> for TransparentExecutor
85    let argv_refs: Vec<&str> = argv_strings.iter().map(|s| s.as_str()).collect();
86
87    // Use TransparentExecutor for cross-ABI execution
88    match TransparentExecutor::execute_binary(&path_str, &argv_refs, &[], task, trapframe, false) {
89        Ok(_) => {
90            // execve normally should not return on success - the process is replaced
91            // However, if ABI module sets trapframe return value and returns here,
92            // we should respect that value instead of hardcoding 0
93            trapframe.get_return_value()
94        }
95        Err(_) => {
96            // Execution failed - return error code
97            // The trap handler will automatically set trapframe return value from our return
98            usize::MAX // Error return value
99        }
100    }
101}
102
103#[repr(i32)]
104enum OpenMode {
105    ReadOnly = 0x000,
106    WriteOnly = 0x001,
107    ReadWrite = 0x002,
108    Create = 0x200,
109    Truncate = 0x400,
110}
111
112pub fn sys_open(
113    abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
114    trapframe: &mut Trapframe,
115) -> usize {
116    let task = mytask().unwrap();
117    let path_ptr = task
118        .vm_manager
119        .translate_vaddr(trapframe.get_arg(0))
120        .unwrap() as *const u8;
121    let mode = trapframe.get_arg(1) as i32;
122
123    // Increment PC to avoid infinite loop if open fails
124    trapframe.increment_pc_next(task);
125
126    // Convert path bytes to string
127    let path_str = match cstring_to_string(path_ptr, MAX_PATH_LENGTH) {
128        Ok((path, _)) => match to_absolute_path_v2(&task, &path) {
129            Ok(abs_path) => abs_path,
130            Err(_) => return usize::MAX,
131        },
132        Err(_) => return usize::MAX, // Invalid UTF-8
133    };
134
135    // Use task's VFS manager
136    let vfs = task.vfs.read().clone().unwrap();
137
138    // Try to open the file
139    let file = vfs.open(&path_str, 0);
140
141    match file {
142        Ok(kernel_obj) => {
143            // Register the file with the task using HandleTable
144            let handle = task.handle_table.insert(kernel_obj);
145            match handle {
146                Ok(handle) => {
147                    match abi.allocate_fd(handle as u32) {
148                        Ok(fd) => fd,
149                        Err(_) => usize::MAX, // Too many open files
150                    }
151                }
152                Err(_) => usize::MAX, // Handle table full
153            }
154        }
155        Err(_) => {
156            // If the file does not exist and we are trying to create it
157            if mode & OpenMode::Create as i32 != 0 {
158                let res = vfs.create_file(&path_str, FileType::RegularFile);
159                if res.is_err() {
160                    return usize::MAX; // File creation error
161                }
162                match vfs.open(&path_str, 0) {
163                    Ok(kernel_obj) => {
164                        // Register the file with the task using HandleTable
165                        let handle = task.handle_table.insert(kernel_obj);
166                        match handle {
167                            Ok(handle) => {
168                                match abi.allocate_fd(handle as u32) {
169                                    Ok(fd) => fd,
170                                    Err(_) => usize::MAX, // Too many open files
171                                }
172                            }
173                            Err(_) => usize::MAX, // Handle table full
174                        }
175                    }
176                    Err(_) => usize::MAX, // File open error
177                }
178            } else {
179                return usize::MAX; // VFS not initialized
180            }
181        }
182    }
183}
184
185pub fn sys_dup(
186    abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
187    trapframe: &mut Trapframe,
188) -> usize {
189    let task = mytask().unwrap();
190    let fd = trapframe.get_arg(0) as usize;
191    trapframe.increment_pc_next(task);
192
193    // Get handle from XV6 fd
194    if let Some(old_handle) = abi.get_handle(fd) {
195        // Use clone_for_dup to get proper dup() semantics (increments Pipe reader/writer counts etc.)
196        if let Some(kernel_obj) = task.handle_table.clone_for_dup(old_handle) {
197            let handle = task.handle_table.insert(kernel_obj);
198            match handle {
199                Ok(new_handle) => {
200                    match abi.allocate_fd(new_handle as u32) {
201                        Ok(fd) => fd,
202                        Err(_) => usize::MAX, // Too many open files
203                    }
204                }
205                Err(_) => usize::MAX, // Handle table full
206            }
207        } else {
208            usize::MAX // Handle not found in handle table
209        }
210    } else {
211        usize::MAX // Invalid file descriptor
212    }
213}
214
215pub fn sys_close(
216    abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
217    trapframe: &mut Trapframe,
218) -> usize {
219    let task = mytask().unwrap();
220    let fd = trapframe.get_arg(0) as usize;
221    trapframe.increment_pc_next(task);
222
223    // Get handle from XV6 fd and remove mapping
224    if let Some(handle) = abi.remove_fd(fd) {
225        if task.handle_table.remove(handle).is_some() {
226            0 // Success
227        } else {
228            usize::MAX // Handle not found in handle table
229        }
230    } else {
231        usize::MAX // Invalid file descriptor
232    }
233}
234
235pub fn sys_read(
236    abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
237    trapframe: &mut Trapframe,
238) -> usize {
239    let task = mytask().unwrap();
240    let fd = trapframe.get_arg(0) as usize;
241    let buf_ptr = task
242        .vm_manager
243        .translate_vaddr(trapframe.get_arg(1))
244        .unwrap() as *mut u8;
245    let count = trapframe.get_arg(2) as usize;
246
247    // Get handle from XV6 fd
248    let handle = match abi.get_handle(fd) {
249        Some(h) => h,
250        None => {
251            trapframe.increment_pc_next(task);
252            return usize::MAX; // Invalid file descriptor
253        }
254    };
255
256    let kernel_obj = match task.handle_table.get(handle) {
257        Some(obj) => obj,
258        None => {
259            trapframe.increment_pc_next(task);
260            return usize::MAX; // Invalid file descriptor
261        }
262    };
263
264    // Check if this is a directory by getting file metadata
265    let is_directory = if let Some(file_obj) = kernel_obj.as_file() {
266        if let Ok(metadata) = file_obj.metadata() {
267            matches!(metadata.file_type, FileType::Directory)
268        } else {
269            false
270        }
271    } else {
272        false
273    };
274
275    let stream = match kernel_obj.as_stream() {
276        Some(stream) => stream,
277        None => {
278            trapframe.increment_pc_next(task);
279            return usize::MAX; // Not a stream object
280        }
281    };
282
283    if is_directory {
284        // For directories, we need a larger buffer to read DirectoryEntry, then convert to Dirent
285        let directory_entry_size = core::mem::size_of::<DirectoryEntry>();
286        let mut temp_buffer = vec![0u8; directory_entry_size];
287
288        match stream.read(&mut temp_buffer) {
289            Ok(n) => {
290                trapframe.increment_pc_next(task); // Increment PC to avoid infinite loop
291                if n > 0 && n >= directory_entry_size {
292                    // Convert DirectoryEntry to xv6 Dirent
293                    let converted_bytes =
294                        read_directory_as_xv6_dirent(buf_ptr, count, &temp_buffer[..n]);
295                    if converted_bytes > 0 {
296                        return converted_bytes; // Return converted xv6 dirent size
297                    }
298                }
299                0 // EOF or no valid directory entry
300            }
301            Err(_) => usize::MAX, // Read error
302        }
303    } else {
304        // For regular files, use the user-provided buffer directly
305        let mut buffer = unsafe { core::slice::from_raw_parts_mut(buf_ptr, count) };
306
307        match stream.read(&mut buffer) {
308            Ok(n) => {
309                trapframe.increment_pc_next(task); // Increment PC to avoid infinite loop
310                n
311            } // Return original read size for regular files
312            Err(_) => usize::MAX, // Read error
313        }
314    }
315}
316
317pub fn sys_write(
318    abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
319    trapframe: &mut Trapframe,
320) -> usize {
321    let task = mytask().unwrap();
322    let fd = trapframe.get_arg(0) as usize;
323    let buf_ptr = task
324        .vm_manager
325        .translate_vaddr(trapframe.get_arg(1))
326        .unwrap() as *const u8;
327    let count = trapframe.get_arg(2) as usize;
328
329    // Increment PC to avoid infinite loop if write fails
330    trapframe.increment_pc_next(task);
331
332    // Get handle from XV6 fd
333    let handle = match abi.get_handle(fd) {
334        Some(h) => h,
335        None => return usize::MAX, // Invalid file descriptor
336    };
337
338    let kernel_obj = match task.handle_table.get(handle) {
339        Some(obj) => obj,
340        None => return usize::MAX, // Invalid file descriptor
341    };
342
343    let stream = match kernel_obj.as_stream() {
344        Some(stream) => stream,
345        None => return usize::MAX, // Not a stream object
346    };
347
348    let buffer = unsafe { core::slice::from_raw_parts(buf_ptr, count) };
349
350    match stream.write(buffer) {
351        Ok(n) => n,
352        Err(_) => usize::MAX, // Write error
353    }
354}
355
356pub fn sys_lseek(
357    abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
358    trapframe: &mut Trapframe,
359) -> usize {
360    let task = mytask().unwrap();
361    let fd = trapframe.get_arg(0) as usize;
362    let offset = trapframe.get_arg(1) as i64;
363    let whence = trapframe.get_arg(2) as i32;
364
365    // Increment PC to avoid infinite loop if lseek fails
366    trapframe.increment_pc_next(task);
367
368    // Get handle from XV6 fd
369    let handle = match abi.get_handle(fd) {
370        Some(h) => h,
371        None => return usize::MAX, // Invalid file descriptor
372    };
373
374    let kernel_obj = match task.handle_table.get(handle) {
375        Some(obj) => obj,
376        None => return usize::MAX, // Invalid file descriptor
377    };
378
379    let file = match kernel_obj.as_file() {
380        Some(file) => file,
381        None => return usize::MAX, // Not a file object
382    };
383
384    let whence = match whence {
385        0 => SeekFrom::Start(offset as u64),
386        1 => SeekFrom::Current(offset),
387        2 => SeekFrom::End(offset),
388        _ => return usize::MAX, // Invalid whence
389    };
390
391    match file.seek(whence) {
392        Ok(pos) => pos as usize,
393        Err(_) => usize::MAX, // Lseek error
394    }
395}
396
397// Create device file
398pub fn sys_mknod(
399    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
400    trapframe: &mut Trapframe,
401) -> usize {
402    let task = mytask().unwrap();
403    trapframe.increment_pc_next(task);
404    let name_ptr = task
405        .vm_manager
406        .translate_vaddr(trapframe.get_arg(0))
407        .unwrap() as *const u8;
408    let name = get_path_str_v2(name_ptr).unwrap();
409    let path = to_absolute_path_v2(&task, &name).unwrap();
410
411    let major = trapframe.get_arg(1) as u32;
412    let minor = trapframe.get_arg(2) as u32;
413
414    match (major, minor) {
415        (1, 0) => {
416            // Create a console device
417            let console_dev = Some(DeviceManager::get_manager().register_device(Arc::new(
418                crate::abi::xv6::drivers::console::ConsoleDevice::new(0, "console"),
419            )));
420
421            let vfs = task.vfs.read().clone().unwrap();
422            let _res = vfs.create_file(
423                &path,
424                FileType::CharDevice(DeviceFileInfo {
425                    device_id: console_dev.unwrap(),
426                    device_type: crate::device::DeviceType::Char,
427                }),
428            );
429            // crate::println!("Created console device at {}", path);
430        }
431        _ => {}
432    }
433    0
434}
435
436pub fn sys_fstat(
437    abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
438    trapframe: &mut crate::arch::Trapframe,
439) -> usize {
440    let fd = trapframe.get_arg(0) as usize;
441
442    let task = mytask().expect("sys_fstat: No current task found");
443    trapframe.increment_pc_next(task); // Increment the program counter
444
445    let stat_ptr = task
446        .vm_manager
447        .translate_vaddr(trapframe.get_arg(1) as usize)
448        .expect("sys_fstat: Failed to translate stat pointer") as *mut Stat;
449
450    // Get handle from XV6 fd
451    let handle = match abi.get_handle(fd) {
452        Some(h) => h,
453        None => return usize::MAX, // Invalid file descriptor
454    };
455
456    let kernel_obj = match task.handle_table.get(handle) {
457        Some(obj) => obj,
458        None => return usize::MAX, // Return -1 on error
459    };
460
461    let file = match kernel_obj.as_file() {
462        Some(file) => file,
463        None => return usize::MAX, // Not a file object
464    };
465
466    let metadata = file
467        .metadata()
468        .expect("sys_fstat: Failed to get file metadata");
469
470    if stat_ptr.is_null() {
471        return usize::MAX; // Return -1 if stat pointer is null
472    }
473
474    let stat = unsafe { &mut *stat_ptr };
475
476    *stat = Stat {
477        dev: 0,
478        ino: metadata.file_id as u32,
479        file_type: match metadata.file_type {
480            FileType::Directory => 1,      // T_DIR
481            FileType::RegularFile => 2,    // T_FILE
482            FileType::CharDevice(_) => 3,  // T_DEVICE
483            FileType::BlockDevice(_) => 3, // T_DEVICE
484            _ => 0,                        // Unknown type
485        },
486        nlink: 1,
487        size: metadata.size as u64,
488    };
489
490    0
491}
492
493pub fn sys_mkdir(
494    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
495    trapframe: &mut Trapframe,
496) -> usize {
497    let task = mytask().unwrap();
498    trapframe.increment_pc_next(task);
499
500    let path_ptr = task
501        .vm_manager
502        .translate_vaddr(trapframe.get_arg(0))
503        .unwrap() as *const u8;
504    let path = match get_path_str_v2(path_ptr) {
505        Ok(p) => to_absolute_path_v2(&task, &p).unwrap(),
506        Err(_) => return usize::MAX, // Invalid path
507    };
508
509    // Try to create the directory
510    let vfs = task.vfs.read().clone().unwrap();
511    match vfs.create_dir(&path) {
512        Ok(_) => 0,           // Success
513        Err(_) => usize::MAX, // Error
514    }
515}
516
517pub fn sys_unlink(
518    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
519    trapframe: &mut Trapframe,
520) -> usize {
521    let task = mytask().unwrap();
522    trapframe.increment_pc_next(task);
523
524    let path_ptr = task
525        .vm_manager
526        .translate_vaddr(trapframe.get_arg(0))
527        .unwrap() as *const u8;
528    let path = match cstring_to_string(path_ptr, MAX_PATH_LENGTH) {
529        Ok((p, _)) => to_absolute_path_v2(&task, &p).unwrap(),
530        Err(_) => return usize::MAX, // Invalid path
531    };
532
533    // Try to remove the file or directory
534    let vfs = task.vfs.read().clone().unwrap();
535    match vfs.remove(&path) {
536        Ok(_) => 0,           // Success
537        Err(_) => usize::MAX, // Error
538    }
539}
540
541pub fn sys_link(
542    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
543    trapframe: &mut Trapframe,
544) -> usize {
545    let task = mytask().unwrap();
546    trapframe.increment_pc_next(task);
547
548    let src_path_ptr = task
549        .vm_manager
550        .translate_vaddr(trapframe.get_arg(0))
551        .unwrap() as *const u8;
552    let dst_path_ptr = task
553        .vm_manager
554        .translate_vaddr(trapframe.get_arg(1))
555        .unwrap() as *const u8;
556
557    let src_path = match cstring_to_string(src_path_ptr, MAX_PATH_LENGTH) {
558        Ok((p, _)) => to_absolute_path_v2(&task, &p).unwrap(),
559        Err(_) => return usize::MAX, // Invalid path
560    };
561
562    let dst_path = match cstring_to_string(dst_path_ptr, MAX_PATH_LENGTH) {
563        Ok((p, _)) => to_absolute_path_v2(&task, &p).unwrap(),
564        Err(_) => return usize::MAX, // Invalid path
565    };
566
567    let vfs = task.vfs.read().clone().unwrap();
568    match vfs.create_hardlink(&src_path, &dst_path) {
569        Ok(_) => 0, // Success
570        Err(err) => {
571            use crate::fs::FileSystemErrorKind;
572
573            // Map VFS errors to appropriate errno values for xv6
574            match err.kind {
575                FileSystemErrorKind::NotFound => {
576                    // Source file doesn't exist
577                    2 // ENOENT
578                }
579                FileSystemErrorKind::FileExists => {
580                    // Destination already exists
581                    17 // EEXIST
582                }
583                FileSystemErrorKind::CrossDevice => {
584                    // Hard links across devices not supported
585                    18 // EXDEV
586                }
587                FileSystemErrorKind::InvalidOperation => {
588                    // Operation not supported (e.g., directory hardlink)
589                    1 // EPERM
590                }
591                FileSystemErrorKind::PermissionDenied => {
592                    13 // EACCES
593                }
594                _ => {
595                    // Other errors
596                    5 // EIO
597                }
598            }
599        }
600    }
601}
602
603/// VFS v2 helper function for path absolutization
604/// TODO: Move this to a shared helper module when VFS v2 provides public API
605fn to_absolute_path_v2(task: &crate::task::Task, path: &str) -> Result<String, ()> {
606    if path.starts_with('/') {
607        Ok(path.to_string())
608    } else {
609        let vfs = task.vfs.read().clone().ok_or(())?;
610        Ok(vfs.resolve_path_to_absolute(path))
611    }
612}
613
614/// Helper function to replace the missing get_path_str function
615/// TODO: This should be moved to a shared helper when VFS v2 provides public API
616fn get_path_str_v2(ptr: *const u8) -> Result<String, ()> {
617    const MAX_PATH_LENGTH: usize = 128;
618    cstring_to_string(ptr, MAX_PATH_LENGTH)
619        .map(|(s, _)| s)
620        .map_err(|_| ())
621}