diff --git a/Cargo.toml b/Cargo.toml index d10461b349..200aa183e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,10 +99,6 @@ path = "test/sys/test_aio_drop.rs" name = "test-clearenv" path = "test/test_clearenv.rs" -[[test]] -name = "test-mount" -path = "test/test_mount.rs" - [[test]] name = "test-prctl" path = "test/sys/test_prctl.rs" diff --git a/test/test.rs b/test/test.rs index 00f0e74efb..c7231426c2 100644 --- a/test/test.rs +++ b/test/test.rs @@ -11,6 +11,8 @@ mod test_errno; mod test_fcntl; #[cfg(linux_android)] mod test_kmod; +#[cfg(target_os = "linux")] +mod test_mount; #[cfg(any( freebsdlike, target_os = "fushsia", diff --git a/test/test_mount.rs b/test/test_mount.rs index 8bee13ff98..a4f0903dba 100644 --- a/test/test_mount.rs +++ b/test/test_mount.rs @@ -1,187 +1,183 @@ -#[macro_use] -mod common; +use std::fs::{self, File}; +use std::io::{Read, Write}; +use std::os::unix::fs::OpenOptionsExt; +use std::os::unix::fs::PermissionsExt; +use std::process::Command; -#[cfg(target_os = "linux")] -mod test_mount { - use std::fs::{self, File}; - use std::io::{Read, Write}; - use std::os::unix::fs::OpenOptionsExt; - use std::os::unix::fs::PermissionsExt; - use std::process::Command; +use libc::{EACCES, EROFS}; - use libc::{EACCES, EROFS}; +use nix::mount::{mount, umount, MsFlags}; +use nix::sys::stat::{self, Mode}; - use nix::mount::{mount, umount, MsFlags}; - use nix::sys::stat::{self, Mode}; +use crate::*; - static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh +static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh exit 23"; - const EXPECTED_STATUS: i32 = 23; - - const NONE: Option<&'static [u8]> = None; - - #[test] - fn test_mount_tmpfs_without_flags_allows_rwx() { - require_capability!( - "test_mount_tmpfs_without_flags_allows_rwx", - CAP_SYS_ADMIN - ); - 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) - .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}")); - } - - #[test] - fn test_mount_rdonly_disallows_write() { - require_capability!("test_mount_rdonly_disallows_write", CAP_SYS_ADMIN); - let tempdir = tempfile::tempdir().unwrap(); +const EXPECTED_STATUS: i32 = 23; + +const NONE: Option<&'static [u8]> = None; + +#[test] +fn test_mount_tmpfs_without_flags_allows_rwx() { + require_capability!( + "test_mount_tmpfs_without_flags_allows_rwx", + CAP_SYS_ADMIN + ); + 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) + .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}")); +} - mount( - NONE, - tempdir.path(), - Some(b"tmpfs".as_ref()), - MsFlags::MS_RDONLY, - NONE, - ) - .unwrap_or_else(|e| panic!("mount failed: {e}")); +#[test] +fn test_mount_rdonly_disallows_write() { + require_capability!("test_mount_rdonly_disallows_write", CAP_SYS_ADMIN); + 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, + File::create(tempdir.path().join("test")) + .unwrap_err() + .raw_os_error() + .unwrap() + ); + + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); +} - // EROFS: Read-only file system - assert_eq!( - EROFS, - File::create(tempdir.path().join("test")) - .unwrap_err() - .raw_os_error() - .unwrap() - ); +#[test] +fn test_mount_noexec_disallows_exec() { + require_capability!("test_mount_noexec_disallows_exec", CAP_SYS_ADMIN); + 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, + Command::new(&test_path) + .status() + .unwrap_err() + .raw_os_error() + .unwrap() + ); + + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); +} - umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); - } +#[test] +fn test_mount_bind() { + require_capability!("test_mount_bind", CAP_SYS_ADMIN); + let tempdir = tempfile::tempdir().unwrap(); + let file_name = "test"; - #[test] - fn test_mount_noexec_disallows_exec() { - require_capability!("test_mount_noexec_disallows_exec", CAP_SYS_ADMIN); - let tempdir = tempfile::tempdir().unwrap(); + { + let mount_point = tempfile::tempdir().unwrap(); mount( + Some(tempdir.path()), + mount_point.path(), NONE, - tempdir.path(), - Some(b"tmpfs".as_ref()), - MsFlags::MS_NOEXEC, + MsFlags::MS_BIND, 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) + .open(mount_point.path().join(file_name)) .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, - Command::new(&test_path) - .status() - .unwrap_err() - .raw_os_error() - .unwrap() - ); - - umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); + umount(mount_point.path()) + .unwrap_or_else(|e| panic!("umount failed: {e}")); } - #[test] - fn test_mount_bind() { - require_capability!("test_mount_bind", CAP_SYS_ADMIN); - 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); - } + // 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); }