mirror of
https://gitee.com/openharmony/third_party_rust_nix
synced 2025-01-07 16:00:42 +00:00
bf7a5fd606
Travis has been super-slow lately (> 6 hours per build). Cirrus is much faster: about 20 minutes. Cirrus also has slightly better test coverage, mainly because it doesn't use SECCOMP. Also, * Fix the Redox CI build. The old Travis configuration didn't actually build for Redox, so we never noticed that Redox can't be built with a stable compiler. Thanks to @coolreader18 for finding this. * Disable the udp_offload tests on cross-tested platforms. These tests are failing with ENOPROTOOPT in Cirrus-CI. I suspect it's due to a lack of support in QEMU. These tests were skipped on Travis because its kernel was too old. * Fix require_kernel_version on Cirrus-CI. Cirrus reports the Linux kernel version as 4.19.112+, which the semver crate can't handle. * Fix test_setfsuid on Cirrus. When run on Cirrus, it seems like the file in /tmp gets deleted as soon as it's closed. Probably an overzealous temporary file cleaner. Use /var/tmp, because no temporary file cleaner should run in there. * Skip mount tests on Cirrus. They fail for an unknown reason. Issue #1351 * Skip the AF_ALG tests on Cirrus-CI Issue #1352
238 lines
8.2 KiB
Rust
238 lines
8.2 KiB
Rust
mod common;
|
|
|
|
// Impelmentation note: to allow unprivileged users to run it, this test makes
|
|
// use of user and mount namespaces. On systems that allow unprivileged user
|
|
// namespaces (Linux >= 3.8 compiled with CONFIG_USER_NS), the test should run
|
|
// without root.
|
|
|
|
#[cfg(target_os = "linux")]
|
|
mod test_mount {
|
|
use std::fs::{self, File};
|
|
use std::io::{self, Read, Write};
|
|
use std::os::unix::fs::OpenOptionsExt;
|
|
use std::os::unix::fs::PermissionsExt;
|
|
use std::process::{self, Command};
|
|
|
|
use libc::{EACCES, EROFS};
|
|
|
|
use nix::errno::Errno;
|
|
use nix::mount::{mount, umount, MsFlags};
|
|
use nix::sched::{unshare, CloneFlags};
|
|
use nix::sys::stat::{self, Mode};
|
|
use nix::unistd::getuid;
|
|
|
|
use tempfile;
|
|
|
|
static SCRIPT_CONTENTS: &'static [u8] = b"#!/bin/sh
|
|
exit 23";
|
|
|
|
const EXPECTED_STATUS: i32 = 23;
|
|
|
|
const NONE: Option<&'static [u8]> = None;
|
|
pub fn test_mount_tmpfs_without_flags_allows_rwx() {
|
|
let tempdir = tempfile::tempdir().unwrap();
|
|
|
|
mount(NONE,
|
|
tempdir.path(),
|
|
Some(b"tmpfs".as_ref()),
|
|
MsFlags::empty(),
|
|
NONE)
|
|
.unwrap_or_else(|e| panic!("mount failed: {}", e));
|
|
|
|
let test_path = tempdir.path().join("test");
|
|
|
|
// Verify write.
|
|
fs::OpenOptions::new()
|
|
.create(true)
|
|
.write(true)
|
|
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
|
|
.open(&test_path)
|
|
.or_else(|e|
|
|
if Errno::from_i32(e.raw_os_error().unwrap()) == Errno::EOVERFLOW {
|
|
// Skip tests on certain Linux kernels which have a bug
|
|
// regarding tmpfs in namespaces.
|
|
// Ubuntu 14.04 and 16.04 are known to be affected; 16.10 is
|
|
// not. There is no legitimate reason for open(2) to return
|
|
// EOVERFLOW here.
|
|
// https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1659087
|
|
let stderr = io::stderr();
|
|
let mut handle = stderr.lock();
|
|
writeln!(handle, "Buggy Linux kernel detected. Skipping test.")
|
|
.unwrap();
|
|
process::exit(0);
|
|
} else {
|
|
panic!("open failed: {}", e);
|
|
}
|
|
)
|
|
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
|
|
.unwrap_or_else(|e| panic!("write failed: {}", e));
|
|
|
|
// Verify read.
|
|
let mut buf = Vec::new();
|
|
File::open(&test_path)
|
|
.and_then(|mut f| f.read_to_end(&mut buf))
|
|
.unwrap_or_else(|e| panic!("read failed: {}", e));
|
|
assert_eq!(buf, SCRIPT_CONTENTS);
|
|
|
|
// Verify execute.
|
|
assert_eq!(EXPECTED_STATUS,
|
|
Command::new(&test_path)
|
|
.status()
|
|
.unwrap_or_else(|e| panic!("exec failed: {}", e))
|
|
.code()
|
|
.unwrap_or_else(|| panic!("child killed by signal")));
|
|
|
|
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
|
|
}
|
|
|
|
pub fn test_mount_rdonly_disallows_write() {
|
|
let tempdir = tempfile::tempdir().unwrap();
|
|
|
|
mount(NONE,
|
|
tempdir.path(),
|
|
Some(b"tmpfs".as_ref()),
|
|
MsFlags::MS_RDONLY,
|
|
NONE)
|
|
.unwrap_or_else(|e| panic!("mount failed: {}", e));
|
|
|
|
// EROFS: Read-only file system
|
|
assert_eq!(EROFS as i32,
|
|
File::create(tempdir.path().join("test")).unwrap_err().raw_os_error().unwrap());
|
|
|
|
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
|
|
}
|
|
|
|
pub fn test_mount_noexec_disallows_exec() {
|
|
let tempdir = tempfile::tempdir().unwrap();
|
|
|
|
mount(NONE,
|
|
tempdir.path(),
|
|
Some(b"tmpfs".as_ref()),
|
|
MsFlags::MS_NOEXEC,
|
|
NONE)
|
|
.unwrap_or_else(|e| panic!("mount failed: {}", e));
|
|
|
|
let test_path = tempdir.path().join("test");
|
|
|
|
fs::OpenOptions::new()
|
|
.create(true)
|
|
.write(true)
|
|
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
|
|
.open(&test_path)
|
|
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
|
|
.unwrap_or_else(|e| panic!("write failed: {}", e));
|
|
|
|
// Verify that we cannot execute despite a+x permissions being set.
|
|
let mode = stat::Mode::from_bits_truncate(fs::metadata(&test_path)
|
|
.map(|md| md.permissions().mode())
|
|
.unwrap_or_else(|e| {
|
|
panic!("metadata failed: {}", e)
|
|
}));
|
|
|
|
assert!(mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH),
|
|
"{:?} did not have execute permissions",
|
|
&test_path);
|
|
|
|
// EACCES: Permission denied
|
|
assert_eq!(EACCES as i32,
|
|
Command::new(&test_path).status().unwrap_err().raw_os_error().unwrap());
|
|
|
|
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
|
|
}
|
|
|
|
pub fn test_mount_bind() {
|
|
let tempdir = tempfile::tempdir().unwrap();
|
|
let file_name = "test";
|
|
|
|
{
|
|
let mount_point = tempfile::tempdir().unwrap();
|
|
|
|
mount(Some(tempdir.path()),
|
|
mount_point.path(),
|
|
NONE,
|
|
MsFlags::MS_BIND,
|
|
NONE)
|
|
.unwrap_or_else(|e| panic!("mount failed: {}", e));
|
|
|
|
fs::OpenOptions::new()
|
|
.create(true)
|
|
.write(true)
|
|
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
|
|
.open(mount_point.path().join(file_name))
|
|
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
|
|
.unwrap_or_else(|e| panic!("write failed: {}", e));
|
|
|
|
umount(mount_point.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
|
|
}
|
|
|
|
// Verify the file written in the mount shows up in source directory, even
|
|
// after unmounting.
|
|
|
|
let mut buf = Vec::new();
|
|
File::open(tempdir.path().join(file_name))
|
|
.and_then(|mut f| f.read_to_end(&mut buf))
|
|
.unwrap_or_else(|e| panic!("read failed: {}", e));
|
|
assert_eq!(buf, SCRIPT_CONTENTS);
|
|
}
|
|
|
|
pub fn setup_namespaces() {
|
|
// Hold on to the uid in the parent namespace.
|
|
let uid = getuid();
|
|
|
|
unshare(CloneFlags::CLONE_NEWNS | CloneFlags::CLONE_NEWUSER).unwrap_or_else(|e| {
|
|
let stderr = io::stderr();
|
|
let mut handle = stderr.lock();
|
|
writeln!(handle,
|
|
"unshare failed: {}. Are unprivileged user namespaces available?",
|
|
e).unwrap();
|
|
writeln!(handle, "mount is not being tested").unwrap();
|
|
// Exit with success because not all systems support unprivileged user namespaces, and
|
|
// that's not what we're testing for.
|
|
process::exit(0);
|
|
});
|
|
|
|
// Map user as uid 1000.
|
|
fs::OpenOptions::new()
|
|
.write(true)
|
|
.open("/proc/self/uid_map")
|
|
.and_then(|mut f| f.write(format!("1000 {} 1\n", uid).as_bytes()))
|
|
.unwrap_or_else(|e| panic!("could not write uid map: {}", e));
|
|
}
|
|
}
|
|
|
|
|
|
// Test runner
|
|
|
|
/// Mimic normal test output (hackishly).
|
|
#[cfg(target_os = "linux")]
|
|
macro_rules! run_tests {
|
|
( $($test_fn:ident),* ) => {{
|
|
println!();
|
|
|
|
$(
|
|
print!("test test_mount::{} ... ", stringify!($test_fn));
|
|
$test_fn();
|
|
println!("ok");
|
|
)*
|
|
|
|
println!();
|
|
}}
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
fn main() {
|
|
use test_mount::{setup_namespaces, test_mount_tmpfs_without_flags_allows_rwx,
|
|
test_mount_rdonly_disallows_write, test_mount_noexec_disallows_exec,
|
|
test_mount_bind};
|
|
skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1351");
|
|
setup_namespaces();
|
|
|
|
run_tests!(test_mount_tmpfs_without_flags_allows_rwx,
|
|
test_mount_rdonly_disallows_write,
|
|
test_mount_noexec_disallows_exec,
|
|
test_mount_bind);
|
|
}
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
fn main() {}
|