1 use core::ffi::{c_char, c_void}; 2 use libc::{ 3 chown, fchown, fchownat, getgrnam, getpwnam, gid_t, lchown, mount, uid_t, umount, AT_FDCWD, 4 AT_SYMLINK_NOFOLLOW, 5 }; 6 use nix::errno::Errno; 7 use std::{ 8 ffi::CString, 9 fs::{self, metadata, File}, 10 io::{self, Error, Write}, 11 os::unix::{ 12 fs::{MetadataExt, PermissionsExt}, 13 io::AsRawFd, 14 }, 15 path::Path, 16 }; 17 18 fn print_file_owner_group(filename: &str) -> Result<(), Error> { 19 let metadata = std::fs::metadata(filename)?; 20 let uid = metadata.uid(); 21 let gid = metadata.gid(); 22 23 // 确保 UID 和 GID 打印正确 24 assert!(uid > 0, "UID should be greater than 0"); 25 assert!(gid > 0, "GID should be greater than 0"); 26 27 Ok(()) 28 } 29 30 fn test_fchownat(filename: &str, new_uid: uid_t, new_gid: gid_t, flags: i32) -> Result<(), Error> { 31 let c_filename = CString::new(filename)?; 32 let result = unsafe { fchownat(AT_FDCWD, c_filename.as_ptr(), new_uid, new_gid, flags) }; 33 34 // 确保 fchownat 成功 35 assert!(result != -1, "fchownat failed"); 36 37 print_file_owner_group(filename)?; 38 Ok(()) 39 } 40 41 fn test_chown(filename: &str, new_uid: uid_t, new_gid: gid_t) -> Result<(), Error> { 42 let c_filename = CString::new(filename)?; 43 let result = unsafe { chown(c_filename.as_ptr(), new_uid, new_gid) }; 44 45 // 确保 chown 成功 46 assert!(result != -1, "chown failed"); 47 48 print_file_owner_group(filename)?; 49 Ok(()) 50 } 51 52 fn test_fchown(fd: i32, new_uid: uid_t, new_gid: gid_t) -> Result<(), Error> { 53 let result = unsafe { fchown(fd, new_uid, new_gid) }; 54 55 // 确保 fchown 成功 56 assert!(result != -1, "fchown failed"); 57 58 Ok(()) 59 } 60 61 fn test_lchown(symlink_name: &str, new_uid: uid_t, new_gid: gid_t) -> Result<(), Error> { 62 let c_symlink = CString::new(symlink_name)?; 63 let result = unsafe { lchown(c_symlink.as_ptr(), new_uid, new_gid) }; 64 65 // 确保 lchown 成功 66 assert!(result != -1, "lchown failed"); 67 68 print_file_owner_group(symlink_name)?; 69 Ok(()) 70 } 71 72 fn main() -> Result<(), Error> { 73 mount_test_ramfs(); 74 75 let filename = "/mnt/myramfs/testfile.txt"; 76 let symlink_name = "/mnt/myramfs/testsymlink"; 77 let new_owner = "nobody"; // 替换为你测试系统中的有效用户名 78 let new_group = "nogroup"; // 替换为你测试系统中的有效组名 79 80 // 获取新的 UID 和 GID 81 let pw = unsafe { getpwnam(CString::new(new_owner)?.as_ptr()) }; 82 let gr = unsafe { getgrnam(CString::new(new_group)?.as_ptr()) }; 83 84 assert!(!pw.is_null(), "Invalid user name"); 85 assert!(!gr.is_null(), "Invalid group name"); 86 87 let new_uid = unsafe { (*pw).pw_uid }; 88 let new_gid = unsafe { (*gr).gr_gid }; 89 90 // 创建测试文件 91 let mut file = File::create(filename)?; 92 println!("Created test file: {}", filename); 93 writeln!(file, "This is a test file for chown system call")?; 94 95 // 创建符号链接 96 std::os::unix::fs::symlink(filename, symlink_name)?; 97 println!("Created symlink: {}", symlink_name); 98 99 // 打开文件以测试 fchown 100 let fd = file.as_raw_fd(); 101 102 // 测试 chown 103 test_chown(filename, new_uid, new_gid)?; 104 105 // 测试 fchown 106 test_fchown(fd, new_uid, new_gid)?; 107 108 // 测试 lchown 109 test_lchown(symlink_name, new_uid, new_gid)?; 110 111 // 测试 fchownat,带 AT_SYMLINK_NOFOLLOW 标志(不会跟随符号链接) 112 test_fchownat(symlink_name, new_uid, new_gid, AT_SYMLINK_NOFOLLOW)?; 113 114 // 清理测试文件 115 std::fs::remove_file(filename)?; 116 117 umount_test_ramfs(); 118 119 println!("All tests passed!"); 120 121 Ok(()) 122 } 123 124 fn mount_test_ramfs() { 125 let path = Path::new("mnt/myramfs"); 126 let dir = fs::create_dir_all(path); 127 assert!(dir.is_ok(), "mkdir /mnt/myramfs failed"); 128 129 let source = b"\0".as_ptr() as *const c_char; 130 let target = b"/mnt/myramfs\0".as_ptr() as *const c_char; 131 let fstype = b"ramfs\0".as_ptr() as *const c_char; 132 // let flags = MS_BIND; 133 let flags = 0; 134 let data = std::ptr::null() as *const c_void; 135 let result = unsafe { mount(source, target, fstype, flags, data) }; 136 137 assert_eq!( 138 result, 139 0, 140 "Mount myramfs failed, errno: {}", 141 Errno::last().desc() 142 ); 143 println!("Mount myramfs for test success!"); 144 } 145 146 fn umount_test_ramfs() { 147 let path = b"/mnt/myramfs\0".as_ptr() as *const c_char; 148 let result = unsafe { umount(path) }; 149 if result != 0 { 150 let err = Errno::last(); 151 println!("Errno: {}", err); 152 println!("Infomation: {}", err.desc()); 153 } else { 154 // 删除mnt/myramfs 155 let path = Path::new("mnt/myramfs"); 156 let _ = fs::remove_dir(path); 157 } 158 assert_eq!(result, 0, "Umount myramfs failed"); 159 println!("Umount myramfs for test success!"); 160 } 161