mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-09 07:06:47 +00:00
feat:添加chown系统调用 (#962)
* 添加chown系统调用 --------- Co-authored-by: sparkzky <sparkhhhhhhhhh@outlook.com> Co-authored-by: longjin <longjin@DragonOS.org>
This commit is contained in:
parent
7f46e98e36
commit
55e6f0b65f
@ -598,17 +598,43 @@ impl dyn IndexNode {
|
|||||||
return self.lookup_follow_symlink(path, 0);
|
return self.lookup_follow_symlink(path, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief 查找文件(考虑符号链接)
|
|
||||||
///
|
|
||||||
/// @param path 文件路径
|
|
||||||
/// @param max_follow_times 最大经过的符号链接的大小
|
|
||||||
///
|
|
||||||
/// @return Ok(Arc<dyn IndexNode>) 要寻找的目录项的inode
|
|
||||||
/// @return Err(SystemError) 错误码
|
|
||||||
pub fn lookup_follow_symlink(
|
pub fn lookup_follow_symlink(
|
||||||
&self,
|
&self,
|
||||||
path: &str,
|
path: &str,
|
||||||
max_follow_times: usize,
|
max_follow_times: usize,
|
||||||
|
) -> Result<Arc<dyn IndexNode>, SystemError> {
|
||||||
|
return self.do_lookup_follow_symlink(path, max_follow_times, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_follow_symlink2(
|
||||||
|
&self,
|
||||||
|
path: &str,
|
||||||
|
max_follow_times: usize,
|
||||||
|
follow_final_symlink: bool,
|
||||||
|
) -> Result<Arc<dyn IndexNode>, SystemError> {
|
||||||
|
return self.do_lookup_follow_symlink(path, max_follow_times, follow_final_symlink);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # 查找文件
|
||||||
|
/// 查找指定路径的文件,考虑符号链接的存在,并可选择是否返回最终路径的符号链接文件本身。
|
||||||
|
///
|
||||||
|
/// ## 参数
|
||||||
|
/// - `path`: 文件路径
|
||||||
|
/// - `max_follow_times`: 最大经过的符号链接的数量
|
||||||
|
/// - `follow_final_symlink`: 是否跟随最后的符号链接
|
||||||
|
///
|
||||||
|
/// ## 返回值
|
||||||
|
/// - `Ok(Arc<dyn IndexNode>)`: 要寻找的目录项的inode
|
||||||
|
/// - `Err(SystemError)`: 错误码,表示查找过程中遇到的错误
|
||||||
|
///
|
||||||
|
/// ## Safety
|
||||||
|
/// 此函数在处理符号链接时可能会遇到循环引用的情况,`max_follow_times` 参数用于限制符号链接的跟随次数以避免无限循环。
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn do_lookup_follow_symlink(
|
||||||
|
&self,
|
||||||
|
path: &str,
|
||||||
|
max_follow_times: usize,
|
||||||
|
follow_final_symlink: bool,
|
||||||
) -> Result<Arc<dyn IndexNode>, SystemError> {
|
) -> Result<Arc<dyn IndexNode>, SystemError> {
|
||||||
if self.metadata()?.file_type != FileType::Dir {
|
if self.metadata()?.file_type != FileType::Dir {
|
||||||
return Err(SystemError::ENOTDIR);
|
return Err(SystemError::ENOTDIR);
|
||||||
@ -632,13 +658,10 @@ impl dyn IndexNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let name;
|
let name;
|
||||||
|
|
||||||
// 寻找“/”
|
// 寻找“/”
|
||||||
match rest_path.find('/') {
|
match rest_path.find('/') {
|
||||||
Some(pos) => {
|
Some(pos) => {
|
||||||
// 找到了,设置下一个要查找的名字
|
|
||||||
name = String::from(&rest_path[0..pos]);
|
name = String::from(&rest_path[0..pos]);
|
||||||
// 剩余的路径字符串
|
|
||||||
rest_path = String::from(&rest_path[pos + 1..]);
|
rest_path = String::from(&rest_path[pos + 1..]);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -653,11 +676,18 @@ impl dyn IndexNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let inode = result.find(&name)?;
|
let inode = result.find(&name)?;
|
||||||
|
let file_type = inode.metadata()?.file_type;
|
||||||
|
// 如果已经是路径的最后一个部分,并且不希望跟随最后的符号链接
|
||||||
|
if rest_path.is_empty() && !follow_final_symlink && file_type == FileType::SymLink {
|
||||||
|
// 返回符号链接本身
|
||||||
|
return Ok(inode);
|
||||||
|
}
|
||||||
|
|
||||||
// 处理符号链接的问题
|
// 跟随符号链接跳转
|
||||||
if inode.metadata()?.file_type == FileType::SymLink && max_follow_times > 0 {
|
if file_type == FileType::SymLink && max_follow_times > 0 {
|
||||||
let mut content = [0u8; 256];
|
let mut content = [0u8; 256];
|
||||||
// 读取符号链接
|
// 读取符号链接
|
||||||
|
|
||||||
let len = inode.read_at(
|
let len = inode.read_at(
|
||||||
0,
|
0,
|
||||||
256,
|
256,
|
||||||
@ -667,12 +697,16 @@ impl dyn IndexNode {
|
|||||||
|
|
||||||
// 将读到的数据转换为utf8字符串(先转为str,再转为String)
|
// 将读到的数据转换为utf8字符串(先转为str,再转为String)
|
||||||
let link_path = String::from(
|
let link_path = String::from(
|
||||||
::core::str::from_utf8(&content[..len]).map_err(|_| SystemError::ENOTDIR)?,
|
::core::str::from_utf8(&content[..len]).map_err(|_| SystemError::EINVAL)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let new_path = link_path + "/" + &rest_path;
|
let new_path = link_path + "/" + &rest_path;
|
||||||
|
|
||||||
// 继续查找符号链接
|
// 继续查找符号链接
|
||||||
return result.lookup_follow_symlink(&new_path, max_follow_times - 1);
|
return result.lookup_follow_symlink2(
|
||||||
|
&new_path,
|
||||||
|
max_follow_times - 1,
|
||||||
|
follow_final_symlink,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
result = inode;
|
result = inode;
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,15 @@ use super::{
|
|||||||
utils::{rsplit_path, user_path_at},
|
utils::{rsplit_path, user_path_at},
|
||||||
FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES,
|
FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES,
|
||||||
};
|
};
|
||||||
use crate::filesystem::vfs::syscall::UtimensFlags;
|
|
||||||
use crate::time::{syscall::PosixTimeval, PosixTimeSpec};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
driver::base::block::SeekFrom, process::ProcessManager,
|
driver::base::block::SeekFrom, process::ProcessManager,
|
||||||
syscall::user_access::check_and_clone_cstr,
|
syscall::user_access::check_and_clone_cstr,
|
||||||
};
|
};
|
||||||
|
use crate::{filesystem::vfs::syscall::UtimensFlags, process::cred::Kgid};
|
||||||
|
use crate::{
|
||||||
|
process::cred::GroupInfo,
|
||||||
|
time::{syscall::PosixTimeval, PosixTimeSpec},
|
||||||
|
};
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
|
||||||
pub(super) fn do_faccessat(
|
pub(super) fn do_faccessat(
|
||||||
@ -64,6 +67,88 @@ pub fn do_fchmodat(dirfd: i32, path: *const u8, _mode: ModeType) -> Result<usize
|
|||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_fchownat(
|
||||||
|
dirfd: i32,
|
||||||
|
path: &str,
|
||||||
|
uid: usize,
|
||||||
|
gid: usize,
|
||||||
|
flag: AtFlags,
|
||||||
|
) -> Result<usize, SystemError> {
|
||||||
|
// 检查flag是否合法
|
||||||
|
if flag.contains(!(AtFlags::AT_SYMLINK_NOFOLLOW | AtFlags::AT_EMPTY_PATH)) {
|
||||||
|
return Err(SystemError::EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
let follow_symlink = flag.contains(!AtFlags::AT_SYMLINK_NOFOLLOW);
|
||||||
|
let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?;
|
||||||
|
|
||||||
|
// 如果找不到文件,则返回错误码ENOENT
|
||||||
|
let inode = if follow_symlink {
|
||||||
|
inode.lookup_follow_symlink2(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES, false)
|
||||||
|
} else {
|
||||||
|
inode.lookup(path.as_str())
|
||||||
|
};
|
||||||
|
|
||||||
|
if inode.is_err() {
|
||||||
|
let errno = inode.clone().unwrap_err();
|
||||||
|
// 文件不存在
|
||||||
|
if errno == SystemError::ENOENT {
|
||||||
|
return Err(SystemError::ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let inode = inode.unwrap();
|
||||||
|
|
||||||
|
return chown_common(inode, uid, gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chown_common(inode: Arc<dyn IndexNode>, uid: usize, gid: usize) -> Result<usize, SystemError> {
|
||||||
|
let mut meta = inode.metadata()?;
|
||||||
|
let cred = ProcessManager::current_pcb().cred();
|
||||||
|
let current_uid = cred.uid.data();
|
||||||
|
let current_gid = cred.gid.data();
|
||||||
|
let mut group_info = GroupInfo::default();
|
||||||
|
if let Some(info) = cred.group_info.as_ref() {
|
||||||
|
group_info = info.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查权限
|
||||||
|
match current_uid {
|
||||||
|
0 => {
|
||||||
|
meta.uid = uid;
|
||||||
|
meta.gid = gid;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// 非文件所有者不能更改信息,且不能更改uid
|
||||||
|
if current_uid != meta.uid || uid != meta.uid {
|
||||||
|
return Err(SystemError::EPERM);
|
||||||
|
}
|
||||||
|
if gid != current_gid && !group_info.gids.contains(&Kgid::from(gid)) {
|
||||||
|
return Err(SystemError::EPERM);
|
||||||
|
}
|
||||||
|
meta.gid = gid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meta.mode.remove(ModeType::S_ISUID | ModeType::S_ISGID);
|
||||||
|
inode.set_metadata(&meta)?;
|
||||||
|
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ksys_fchown(fd: i32, uid: usize, gid: usize) -> Result<usize, SystemError> {
|
||||||
|
let fd_table = &ProcessManager::current_pcb().fd_table();
|
||||||
|
let fd_table = fd_table.read();
|
||||||
|
|
||||||
|
let inode = fd_table.get_file_by_fd(fd).unwrap().inode();
|
||||||
|
|
||||||
|
let result = chown_common(inode, uid, gid);
|
||||||
|
|
||||||
|
drop(fd_table);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn do_sys_open(
|
pub(super) fn do_sys_open(
|
||||||
dfd: i32,
|
dfd: i32,
|
||||||
path: &str,
|
path: &str,
|
||||||
|
@ -25,7 +25,9 @@ use super::{
|
|||||||
core::{do_mkdir_at, do_remove_dir, do_unlink_at},
|
core::{do_mkdir_at, do_remove_dir, do_unlink_at},
|
||||||
fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC},
|
fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC},
|
||||||
file::{File, FileMode},
|
file::{File, FileMode},
|
||||||
open::{do_faccessat, do_fchmodat, do_sys_open, do_utimensat, do_utimes},
|
open::{
|
||||||
|
do_faccessat, do_fchmodat, do_fchownat, do_sys_open, do_utimensat, do_utimes, ksys_fchown,
|
||||||
|
},
|
||||||
utils::{rsplit_path, user_path_at},
|
utils::{rsplit_path, user_path_at},
|
||||||
Dirent, FileType, IndexNode, SuperBlock, FSMAKER, MAX_PATHLEN, ROOT_INODE,
|
Dirent, FileType, IndexNode, SuperBlock, FSMAKER, MAX_PATHLEN, ROOT_INODE,
|
||||||
VFS_MAX_FOLLOW_SYMLINK_TIMES,
|
VFS_MAX_FOLLOW_SYMLINK_TIMES,
|
||||||
@ -1638,6 +1640,52 @@ impl Syscall {
|
|||||||
warn!("fchmod not fully implemented");
|
warn!("fchmod not fully implemented");
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn chown(pathname: *const u8, uid: usize, gid: usize) -> Result<usize, SystemError> {
|
||||||
|
let pathname = user_access::check_and_clone_cstr(pathname, Some(MAX_PATHLEN))?
|
||||||
|
.into_string()
|
||||||
|
.map_err(|_| SystemError::EINVAL)?;
|
||||||
|
return do_fchownat(
|
||||||
|
AtFlags::AT_FDCWD.bits(),
|
||||||
|
&pathname,
|
||||||
|
uid,
|
||||||
|
gid,
|
||||||
|
AtFlags::AT_STATX_SYNC_AS_STAT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lchown(pathname: *const u8, uid: usize, gid: usize) -> Result<usize, SystemError> {
|
||||||
|
let pathname = user_access::check_and_clone_cstr(pathname, Some(MAX_PATHLEN))?
|
||||||
|
.into_string()
|
||||||
|
.map_err(|_| SystemError::EINVAL)?;
|
||||||
|
return do_fchownat(
|
||||||
|
AtFlags::AT_FDCWD.bits(),
|
||||||
|
&pathname,
|
||||||
|
uid,
|
||||||
|
gid,
|
||||||
|
AtFlags::AT_SYMLINK_NOFOLLOW,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fchownat(
|
||||||
|
dirfd: i32,
|
||||||
|
pathname: *const u8,
|
||||||
|
uid: usize,
|
||||||
|
gid: usize,
|
||||||
|
flags: i32,
|
||||||
|
) -> Result<usize, SystemError> {
|
||||||
|
let pathname = user_access::check_and_clone_cstr(pathname, Some(MAX_PATHLEN))?
|
||||||
|
.into_string()
|
||||||
|
.map_err(|_| SystemError::EINVAL)?;
|
||||||
|
let pathname = pathname.as_str().trim();
|
||||||
|
let flags = AtFlags::from_bits_truncate(flags);
|
||||||
|
return do_fchownat(dirfd, pathname, uid, gid, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fchown(fd: i32, uid: usize, gid: usize) -> Result<usize, SystemError> {
|
||||||
|
return ksys_fchown(fd, uid, gid);
|
||||||
|
}
|
||||||
|
|
||||||
/// #挂载文件系统
|
/// #挂载文件系统
|
||||||
///
|
///
|
||||||
/// 用于挂载文件系统,目前仅支持ramfs挂载
|
/// 用于挂载文件系统,目前仅支持ramfs挂载
|
||||||
|
@ -164,7 +164,7 @@ impl Cred {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
pub struct GroupInfo {
|
pub struct GroupInfo {
|
||||||
pub gids: Vec<Kgid>,
|
pub gids: Vec<Kgid>,
|
||||||
}
|
}
|
||||||
|
@ -997,8 +997,32 @@ impl Syscall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SYS_FCHOWN => {
|
SYS_FCHOWN => {
|
||||||
warn!("SYS_FCHOWN has not yet been implemented");
|
let dirfd = args[0] as i32;
|
||||||
Ok(0)
|
let uid = args[1];
|
||||||
|
let gid = args[2];
|
||||||
|
Self::fchown(dirfd, uid, gid)
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
SYS_CHOWN => {
|
||||||
|
let pathname = args[0] as *const u8;
|
||||||
|
let uid = args[1];
|
||||||
|
let gid = args[2];
|
||||||
|
Self::chown(pathname, uid, gid)
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
SYS_LCHOWN => {
|
||||||
|
let pathname = args[0] as *const u8;
|
||||||
|
let uid = args[1];
|
||||||
|
let gid = args[2];
|
||||||
|
Self::lchown(pathname, uid, gid)
|
||||||
|
}
|
||||||
|
SYS_FCHOWNAT => {
|
||||||
|
let dirfd = args[0] as i32;
|
||||||
|
let pathname = args[1] as *const u8;
|
||||||
|
let uid = args[2];
|
||||||
|
let gid = args[3];
|
||||||
|
let flag = args[4] as i32;
|
||||||
|
Self::fchownat(dirfd, pathname, uid, gid, flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
SYS_FSYNC => {
|
SYS_FSYNC => {
|
||||||
|
4
user/apps/test-chown/.gitignore
vendored
Normal file
4
user/apps/test-chown/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
testfile.txt
|
||||||
|
/install/
|
13
user/apps/test-chown/Cargo.toml
Normal file
13
user/apps/test-chown/Cargo.toml
Normal 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"
|
56
user/apps/test-chown/Makefile
Normal file
56
user/apps/test-chown/Makefile
Normal 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
|
8
user/apps/test-chown/README.md
Normal file
8
user/apps/test-chown/README.md
Normal 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
|
||||||
|
```
|
160
user/apps/test-chown/src/main.rs
Normal file
160
user/apps/test-chown/src/main.rs
Normal 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!");
|
||||||
|
}
|
29
user/dadk/config/test_chown_0_1_0.dadk
Normal file
29
user/dadk/config/test_chown_0_1_0.dadk
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "test-chown",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "chown系列系统调用",
|
||||||
|
"rust_target": "x86_64-unknown-dragonos",
|
||||||
|
"task_type": {
|
||||||
|
"BuildFromSource": {
|
||||||
|
"Local": {
|
||||||
|
"path": "apps/test-chown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"depends": [],
|
||||||
|
"build": {
|
||||||
|
"build_command": "make install"
|
||||||
|
},
|
||||||
|
"install": {
|
||||||
|
"in_dragonos_path": "/"
|
||||||
|
},
|
||||||
|
"clean": {
|
||||||
|
"clean_command": "make clean"
|
||||||
|
},
|
||||||
|
"envs": [],
|
||||||
|
"build_once": false,
|
||||||
|
"install_once": false,
|
||||||
|
"target_arch": [
|
||||||
|
"x86_64"
|
||||||
|
]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user