xref: /DragonOS/user/apps/test-chown/src/main.rs (revision 7b0ef10895108a0de5ff5ef3d2f93f40cf2e33a5)
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