mirror of
https://github.com/containers/youki
synced 2024-05-10 17:46:15 +02:00
464344923f
* Name the test tools `contest` Signed-off-by: utam0k <k0ma@utam0k.jp> * Address the feedbacks Signed-off-by: utam0k <k0ma@utam0k.jp> * Fix a build error Signed-off-by: utam0k <k0ma@utam0k.jp> * Fix a workflow Signed-off-by: utam0k <k0ma@utam0k.jp> * Address the feedbacks Signed-off-by: utam0k <k0ma@utam0k.jp> --------- Signed-off-by: utam0k <k0ma@utam0k.jp>
472 lines
16 KiB
Rust
472 lines
16 KiB
Rust
use nix::sys::stat::stat;
|
|
use nix::sys::stat::SFlag;
|
|
use std::fs;
|
|
use std::fs::metadata;
|
|
use std::fs::symlink_metadata;
|
|
use std::os::unix::prelude::MetadataExt;
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
|
|
fn test_file_read_access(path: &str) -> Result<(), std::io::Error> {
|
|
let _ = std::fs::OpenOptions::new()
|
|
.create(false)
|
|
.read(true)
|
|
.open(path)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn test_dir_read_access(path: &str) -> Result<(), std::io::Error> {
|
|
let _ = std::fs::read_dir(path)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn is_file_like(mode: u32) -> bool {
|
|
// for this please refer
|
|
// https://stackoverflow.com/questions/40163270/what-is-s-isreg-and-what-does-it-do
|
|
// https://linux.die.net/man/2/stat
|
|
mode & SFlag::S_IFREG.bits() != 0 || mode & SFlag::S_IFCHR.bits() != 0
|
|
}
|
|
|
|
fn is_dir(mode: u32) -> bool {
|
|
mode & SFlag::S_IFDIR.bits() != 0
|
|
}
|
|
|
|
pub fn test_read_access(path: &str) -> Result<(), std::io::Error> {
|
|
let fstat = stat(path)?;
|
|
let mode = fstat.st_mode;
|
|
if is_file_like(mode) {
|
|
// we have a file or a char/block device
|
|
return test_file_read_access(path);
|
|
} else if is_dir(mode) {
|
|
return test_dir_read_access(path);
|
|
}
|
|
|
|
Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("cannot test read access for {path:?}, has mode {mode:x}"),
|
|
))
|
|
}
|
|
|
|
fn test_file_write_access(path: &str) -> Result<(), std::io::Error> {
|
|
let _ = std::fs::OpenOptions::new().write(true).open(path)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn test_dir_write_access(path: &str) -> Result<(), std::io::Error> {
|
|
let _ = std::fs::OpenOptions::new()
|
|
.create(true)
|
|
.write(true)
|
|
.open(PathBuf::from(path).join("test.txt"))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn test_write_access(path: &str) -> Result<(), std::io::Error> {
|
|
let fstat = stat(path)?;
|
|
let mode = fstat.st_mode;
|
|
if is_file_like(mode) {
|
|
// we have a file or a char/block device
|
|
return test_file_write_access(path);
|
|
} else if is_dir(mode) {
|
|
return test_dir_write_access(path);
|
|
}
|
|
|
|
Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("cannot test write access for {path:?}, has mode {mode:x}"),
|
|
))
|
|
}
|
|
|
|
pub fn test_file_executable(path: &str) -> Result<(), std::io::Error> {
|
|
let fstat = stat(path)?;
|
|
let mode = fstat.st_mode;
|
|
if is_file_like(mode) {
|
|
Command::new(path).output()?;
|
|
return Ok(());
|
|
}
|
|
|
|
Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("{path:?} is directory, so cannot execute"),
|
|
))
|
|
}
|
|
|
|
pub fn test_dir_update_access_time(path: &str) -> Result<(), std::io::Error> {
|
|
println!("test_dir_update_access_time path: {path:?}");
|
|
let metadata = fs::metadata(PathBuf::from(path))?;
|
|
let rest = metadata.accessed();
|
|
let first_access_time = rest.unwrap();
|
|
println!("{path:?} dir first access time is {first_access_time:?}");
|
|
// execute ls command to update access time
|
|
Command::new("ls")
|
|
.arg(path)
|
|
.output()
|
|
.expect("execute ls command error");
|
|
// second get access time
|
|
let metadata = fs::metadata(PathBuf::from(path))?;
|
|
let rest = metadata.accessed();
|
|
let second_access_time = rest.unwrap();
|
|
println!("{path:?} dir second access time is {second_access_time:?}");
|
|
if first_access_time == second_access_time {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("cannot update access time for path {path:?}"),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn test_dir_not_update_access_time(path: &str) -> Result<(), std::io::Error> {
|
|
println!("test_dir_not_update_access_time path: {path:?}");
|
|
let metadata = fs::metadata(PathBuf::from(path))?;
|
|
let rest = metadata.accessed();
|
|
let first_access_time = rest.unwrap();
|
|
println!("{path:?} dir first access time is {first_access_time:?}");
|
|
// execute ls command to update access time
|
|
Command::new("ls")
|
|
.arg(path)
|
|
.output()
|
|
.expect("execute ls command error");
|
|
// second get access time
|
|
let metadata = fs::metadata(PathBuf::from(path))?;
|
|
let rest = metadata.accessed();
|
|
let second_access_time = rest.unwrap();
|
|
println!("{path:?} dir second access time is {second_access_time:?}");
|
|
if first_access_time != second_access_time {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("cannot update access time for path {path:?}"),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn test_device_access(path: &str) -> Result<(), std::io::Error> {
|
|
println!("test_device_access path: {path:?}");
|
|
let _ = std::fs::OpenOptions::new()
|
|
.create(true)
|
|
.write(true)
|
|
.open(PathBuf::from(path).join("null"))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn test_device_unaccess(path: &str) -> Result<(), std::io::Error> {
|
|
println!("test_device_unaccess path: {path:?}");
|
|
let _ = std::fs::OpenOptions::new()
|
|
.create(true)
|
|
.write(true)
|
|
.open(PathBuf::from(path).join("null"))?;
|
|
Ok(())
|
|
}
|
|
|
|
// https://man7.org/linux/man-pages/man2/mount_setattr.2.html
|
|
// When a file is accessed via this mount, update the
|
|
// file's last access time (atime) only if the current
|
|
// value of atime is less than or equal to the file's
|
|
// last modification time (mtime) or last status
|
|
// change time (ctime).
|
|
// case:
|
|
// 1. create test.txt file, get one atime
|
|
// 2. cat a.txt, get two atime; check atime whether update, conditions are met atime less than or equal mtime or ctime
|
|
// 3. cat a.txt, get three atime, check now two atime whether equal three atime
|
|
pub fn test_mount_releatime_option(path: &str) -> Result<(), std::io::Error> {
|
|
let test_file_path = PathBuf::from(path).join("test.txt");
|
|
Command::new("touch")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()?;
|
|
let one_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file one metadata atime is {:?},mtime is {:?},current time is{:?}",
|
|
test_file_path,
|
|
one_metadata.atime(),
|
|
one_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
|
|
|
// execute cat command to update access time
|
|
Command::new("cat")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()
|
|
.expect("execute cat command error");
|
|
let two_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file two metadata atime is {:?},mtime is {:?},current time is{:?}",
|
|
test_file_path,
|
|
two_metadata.atime(),
|
|
two_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
|
|
if one_metadata.atime() == two_metadata.atime() {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!(
|
|
"not update access time for file {:?}",
|
|
test_file_path.to_str()
|
|
),
|
|
));
|
|
}
|
|
|
|
// execute cat command to update access time
|
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
|
Command::new("cat")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()
|
|
.expect("execute cat command error");
|
|
let three_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file three metadata atime is {:?}",
|
|
test_file_path,
|
|
three_metadata.atime()
|
|
);
|
|
if two_metadata.atime() != three_metadata.atime() {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("update access time for file {:?}", test_file_path.to_str()),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// case: because filesystem having relatime option
|
|
// 1. create test.txt file, get one atime
|
|
// 2. cat a.txt, get two atime; check atime whether update
|
|
// 3. cat a.txt, get three atime, check now two atime whether equal three atime
|
|
pub fn test_mount_noreleatime_option(path: &str) -> Result<(), std::io::Error> {
|
|
let test_file_path = PathBuf::from(path).join("noreleatime.txt");
|
|
Command::new("touch")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()?;
|
|
let one_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file one atime is {:?},mtime is {:?}, current time is {:?}",
|
|
test_file_path,
|
|
one_metadata.atime(),
|
|
one_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
|
// execute cat command to update access time
|
|
Command::new("cat")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()
|
|
.expect("execute cat command error");
|
|
let two_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file two atime is {:?},mtime is {:?},current time is {:?}",
|
|
test_file_path,
|
|
two_metadata.atime(),
|
|
two_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
|
|
if one_metadata.atime() == two_metadata.atime() {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!(
|
|
"not update access time for file {:?}",
|
|
test_file_path.to_str()
|
|
),
|
|
));
|
|
}
|
|
|
|
// execute cat command to update access time
|
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
|
Command::new("cat")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()
|
|
.expect("execute cat command error");
|
|
let three_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file three atime is {:?},mtime is {:?},current time is {:?}",
|
|
test_file_path,
|
|
three_metadata.atime(),
|
|
three_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
|
|
if two_metadata.atime() != three_metadata.atime() {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("update access time for file {:?}", test_file_path.to_str()),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
// Do not update access times for (all types of) files on this mount.
|
|
// case:
|
|
// 1. touch rnoatime.txt file, get atime
|
|
// 2. cat rnoatime.txt, check atime whether update, if update return error, else return Ok
|
|
pub fn test_mount_rnoatime_option(path: &str) -> Result<(), std::io::Error> {
|
|
let test_file_path = PathBuf::from(path).join("rnoatime.txt");
|
|
Command::new("touch")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()?;
|
|
let one_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file one atime is {:?},mtime is {:?}, current time is {:?}",
|
|
test_file_path,
|
|
one_metadata.atime(),
|
|
one_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
|
|
|
// execute cat command to update access time
|
|
Command::new("cat")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()
|
|
.expect("execute cat command error");
|
|
let two_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file two atime is {:?},mtime is {:?},current time is {:?}",
|
|
test_file_path,
|
|
two_metadata.atime(),
|
|
two_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
if one_metadata.atime() != two_metadata.atime() {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!(
|
|
"update access time for file {:?}, expected not update",
|
|
test_file_path.to_str()
|
|
),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
// Always update the last access time (atime) when files are accessed on this mount.
|
|
pub fn test_mount_rstrictatime_option(path: &str) -> Result<(), std::io::Error> {
|
|
let test_file_path = PathBuf::from(path).join("rstrictatime.txt");
|
|
Command::new("touch")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()?;
|
|
let one_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file one atime is {:?},mtime is {:?}, current time is {:?}",
|
|
test_file_path,
|
|
one_metadata.atime(),
|
|
one_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
|
// execute cat command to update access time
|
|
Command::new("cat")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()
|
|
.expect("execute cat command error");
|
|
let two_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file two atime is {:?},mtime is {:?},current time is {:?}",
|
|
test_file_path,
|
|
two_metadata.atime(),
|
|
two_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
|
|
if one_metadata.atime() == two_metadata.atime() {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!(
|
|
"not update access time for file {:?}",
|
|
test_file_path.to_str()
|
|
),
|
|
));
|
|
}
|
|
|
|
// execute cat command to update access time
|
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
|
Command::new("cat")
|
|
.arg(test_file_path.to_str().unwrap())
|
|
.output()
|
|
.expect("execute cat command error");
|
|
let three_metadata = fs::metadata(test_file_path.clone())?;
|
|
println!(
|
|
"{:?} file three atime is {:?},mtime is {:?},current time is {:?}",
|
|
test_file_path,
|
|
two_metadata.atime(),
|
|
two_metadata.mtime(),
|
|
std::time::SystemTime::now()
|
|
);
|
|
|
|
if two_metadata.atime() == three_metadata.atime() {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("update access time for file {:?}", test_file_path.to_str()),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn test_mount_rnosymfollow_option(path: &str) -> Result<(), std::io::Error> {
|
|
let path = format!("{}/{}", path, "link");
|
|
let metadata = match symlink_metadata(path.clone()) {
|
|
Ok(metadata) => metadata,
|
|
Err(e) => {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("get file symlink_metadata err {path:?}, {e}"),
|
|
));
|
|
}
|
|
};
|
|
// check symbolic is followed
|
|
if metadata.file_type().is_symlink() && metadata.mode() & 0o777 == 0o777 {
|
|
Ok(())
|
|
} else {
|
|
Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("get file symlink_metadata err {path:?}"),
|
|
))
|
|
}
|
|
}
|
|
|
|
pub fn test_mount_rsymfollow_option(path: &str) -> Result<(), std::io::Error> {
|
|
let path = format!("{}/{}", path, "link");
|
|
let metadata = match symlink_metadata(path.clone()) {
|
|
Ok(metadata) => metadata,
|
|
Err(e) => {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("get file symlink_metadata err {path:?}, {e}"),
|
|
));
|
|
}
|
|
};
|
|
// check symbolic is followed
|
|
if metadata.file_type().is_symlink() && metadata.mode() & 0o777 == 0o777 {
|
|
Ok(())
|
|
} else {
|
|
Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("get file symlink_metadata err {path:?}"),
|
|
))
|
|
}
|
|
}
|
|
|
|
pub fn test_mount_rsuid_option(path: &str) -> Result<(), std::io::Error> {
|
|
let path = PathBuf::from(path).join("file");
|
|
|
|
let metadata = match metadata(path.clone()) {
|
|
Ok(metadata) => metadata,
|
|
Err(e) => {
|
|
return Err(std::io::Error::new(std::io::ErrorKind::Other, e));
|
|
}
|
|
};
|
|
// check suid and sgid
|
|
let suid = metadata.mode() & 0o4000 == 0o4000;
|
|
let sgid = metadata.mode() & 0o2000 == 0o2000;
|
|
println!("suid: {suid:?},sgid: {sgid:?}");
|
|
if suid && sgid {
|
|
return Ok(());
|
|
}
|
|
Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
format!("rsuid error {path:?}"),
|
|
))
|
|
}
|