feat:添加chown系统调用 (#962)

* 添加chown系统调用

---------

Co-authored-by: sparkzky <sparkhhhhhhhhh@outlook.com>
Co-authored-by: longjin <longjin@DragonOS.org>
This commit is contained in:
火花
2024-10-26 18:13:25 +08:00
committed by GitHub
parent 7f46e98e36
commit 55e6f0b65f
11 changed files with 482 additions and 21 deletions

4
user/apps/test-chown/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/target
Cargo.lock
testfile.txt
/install/

View File

@ -0,0 +1,13 @@
[package]
name = "test-chown"
version = "0.1.0"
edition = "2021"
description = "测试chown系列系统调用"
authors = [ "sparkzky <sparkhhhhhhhhhh@outlook.com>" ]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2"
errno = "0.3.9"
nix = "0.23"

View File

@ -0,0 +1,56 @@
TOOLCHAIN=
RUSTFLAGS=
ifdef DADK_CURRENT_BUILD_DIR
# 如果是在dadk中编译那么安装到dadk的安装目录中
INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
else
# 如果是在本地编译那么安装到当前目录下的install目录中
INSTALL_DIR = ./install
endif
ifeq ($(ARCH), x86_64)
export RUST_TARGET=x86_64-unknown-linux-musl
else ifeq ($(ARCH), riscv64)
export RUST_TARGET=riscv64gc-unknown-linux-gnu
else
# 默认为x86_86用于本地编译
export RUST_TARGET=x86_64-unknown-linux-musl
endif
run:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET)
build:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET)
clean:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET)
test:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET)
doc:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET)
fmt:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt
fmt-check:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check
run-release:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release
build-release:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release
clean-release:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release
test-release:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release
.PHONY: install
install:
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force

View File

@ -0,0 +1,8 @@
# 一个简单的用于测试chown系列系统调用的程序
### 由于symlink系统调用还未实现目前只测试chown和fchown
### 测试前需要手动添加nogroup用户组和nobody用户程序里加不了
```groupadd -g 65534 nogroup
useradd -d /nonexistent -g 65534 -u 65534 -s /usr/local/bin/false nobody
```

View File

@ -0,0 +1,160 @@
use core::ffi::{c_char, c_void};
use libc::{
chown, fchown, fchownat, getgrnam, getpwnam, gid_t, lchown, mount, uid_t, umount, AT_FDCWD,
AT_SYMLINK_NOFOLLOW,
};
use nix::errno::Errno;
use std::{
ffi::CString,
fs::{self, metadata, File},
io::{self, Error, Write},
os::unix::{
fs::{MetadataExt, PermissionsExt},
io::AsRawFd,
},
path::Path,
};
fn print_file_owner_group(filename: &str) -> Result<(), Error> {
let metadata = std::fs::metadata(filename)?;
let uid = metadata.uid();
let gid = metadata.gid();
// 确保 UID 和 GID 打印正确
assert!(uid > 0, "UID should be greater than 0");
assert!(gid > 0, "GID should be greater than 0");
Ok(())
}
fn test_fchownat(filename: &str, new_uid: uid_t, new_gid: gid_t, flags: i32) -> Result<(), Error> {
let c_filename = CString::new(filename)?;
let result = unsafe { fchownat(AT_FDCWD, c_filename.as_ptr(), new_uid, new_gid, flags) };
// 确保 fchownat 成功
assert!(result != -1, "fchownat failed");
print_file_owner_group(filename)?;
Ok(())
}
fn test_chown(filename: &str, new_uid: uid_t, new_gid: gid_t) -> Result<(), Error> {
let c_filename = CString::new(filename)?;
let result = unsafe { chown(c_filename.as_ptr(), new_uid, new_gid) };
// 确保 chown 成功
assert!(result != -1, "chown failed");
print_file_owner_group(filename)?;
Ok(())
}
fn test_fchown(fd: i32, new_uid: uid_t, new_gid: gid_t) -> Result<(), Error> {
let result = unsafe { fchown(fd, new_uid, new_gid) };
// 确保 fchown 成功
assert!(result != -1, "fchown failed");
Ok(())
}
fn test_lchown(symlink_name: &str, new_uid: uid_t, new_gid: gid_t) -> Result<(), Error> {
let c_symlink = CString::new(symlink_name)?;
let result = unsafe { lchown(c_symlink.as_ptr(), new_uid, new_gid) };
// 确保 lchown 成功
assert!(result != -1, "lchown failed");
print_file_owner_group(symlink_name)?;
Ok(())
}
fn main() -> Result<(), Error> {
mount_test_ramfs();
let filename = "/mnt/myramfs/testfile.txt";
let symlink_name = "/mnt/myramfs/testsymlink";
let new_owner = "nobody"; // 替换为你测试系统中的有效用户名
let new_group = "nogroup"; // 替换为你测试系统中的有效组名
// 获取新的 UID 和 GID
let pw = unsafe { getpwnam(CString::new(new_owner)?.as_ptr()) };
let gr = unsafe { getgrnam(CString::new(new_group)?.as_ptr()) };
assert!(!pw.is_null(), "Invalid user name");
assert!(!gr.is_null(), "Invalid group name");
let new_uid = unsafe { (*pw).pw_uid };
let new_gid = unsafe { (*gr).gr_gid };
// 创建测试文件
let mut file = File::create(filename)?;
println!("Created test file: {}", filename);
writeln!(file, "This is a test file for chown system call")?;
// 创建符号链接
std::os::unix::fs::symlink(filename, symlink_name)?;
println!("Created symlink: {}", symlink_name);
// 打开文件以测试 fchown
let fd = file.as_raw_fd();
// 测试 chown
test_chown(filename, new_uid, new_gid)?;
// 测试 fchown
test_fchown(fd, new_uid, new_gid)?;
// 测试 lchown
test_lchown(symlink_name, new_uid, new_gid)?;
// 测试 fchownat带 AT_SYMLINK_NOFOLLOW 标志(不会跟随符号链接)
test_fchownat(symlink_name, new_uid, new_gid, AT_SYMLINK_NOFOLLOW)?;
// 清理测试文件
std::fs::remove_file(filename)?;
umount_test_ramfs();
println!("All tests passed!");
Ok(())
}
fn mount_test_ramfs() {
let path = Path::new("mnt/myramfs");
let dir = fs::create_dir_all(path);
assert!(dir.is_ok(), "mkdir /mnt/myramfs failed");
let source = b"\0".as_ptr() as *const c_char;
let target = b"/mnt/myramfs\0".as_ptr() as *const c_char;
let fstype = b"ramfs\0".as_ptr() as *const c_char;
// let flags = MS_BIND;
let flags = 0;
let data = std::ptr::null() as *const c_void;
let result = unsafe { mount(source, target, fstype, flags, data) };
assert_eq!(
result,
0,
"Mount myramfs failed, errno: {}",
Errno::last().desc()
);
println!("Mount myramfs for test success!");
}
fn umount_test_ramfs() {
let path = b"/mnt/myramfs\0".as_ptr() as *const c_char;
let result = unsafe { umount(path) };
if result != 0 {
let err = Errno::last();
println!("Errno: {}", err);
println!("Infomation: {}", err.desc());
} else {
// 删除mnt/myramfs
let path = Path::new("mnt/myramfs");
let _ = fs::remove_dir(path);
}
assert_eq!(result, 0, "Umount myramfs failed");
println!("Umount myramfs for test success!");
}