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
print_file_owner_group(filename: &str) -> Result<(), Error>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
test_fchownat(filename: &str, new_uid: uid_t, new_gid: gid_t, flags: i32) -> Result<(), Error>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
test_chown(filename: &str, new_uid: uid_t, new_gid: gid_t) -> Result<(), Error>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
test_fchown(fd: i32, new_uid: uid_t, new_gid: gid_t) -> Result<(), Error>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
test_lchown(symlink_name: &str, new_uid: uid_t, new_gid: gid_t) -> Result<(), Error>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
main() -> Result<(), Error>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
mount_test_ramfs()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
umount_test_ramfs()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