kernel/abi/xv6/riscv64/
file.rs

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