1 use std::fs::{self, File};
2 use std::io::{Read, Write};
3 use std::os::unix::fs::OpenOptionsExt;
4 use std::os::unix::fs::PermissionsExt;
5 use std::process::Command;
6
7 use libc::{EACCES, EROFS};
8
9 use nix::mount::{mount, umount, MsFlags};
10 use nix::sys::stat::{self, Mode};
11
12 use crate::*;
13
14 static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh
15 exit 23";
16
17 const EXPECTED_STATUS: i32 = 23;
18
19 const NONE: Option<&'static [u8]> = None;
20
21 #[test]
test_mount_tmpfs_without_flags_allows_rwx()22 fn test_mount_tmpfs_without_flags_allows_rwx() {
23 require_capability!(
24 "test_mount_tmpfs_without_flags_allows_rwx",
25 CAP_SYS_ADMIN
26 );
27 let tempdir = tempfile::tempdir().unwrap();
28
29 mount(
30 NONE,
31 tempdir.path(),
32 Some(b"tmpfs".as_ref()),
33 MsFlags::empty(),
34 NONE,
35 )
36 .unwrap_or_else(|e| panic!("mount failed: {e}"));
37
38 let test_path = tempdir.path().join("test");
39
40 // Verify write.
41 fs::OpenOptions::new()
42 .create(true)
43 .write(true)
44 .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
45 .open(&test_path)
46 .and_then(|mut f| f.write(SCRIPT_CONTENTS))
47 .unwrap_or_else(|e| panic!("write failed: {e}"));
48
49 // Verify read.
50 let mut buf = Vec::new();
51 File::open(&test_path)
52 .and_then(|mut f| f.read_to_end(&mut buf))
53 .unwrap_or_else(|e| panic!("read failed: {e}"));
54 assert_eq!(buf, SCRIPT_CONTENTS);
55
56 // Verify execute.
57 assert_eq!(
58 EXPECTED_STATUS,
59 Command::new(&test_path)
60 .status()
61 .unwrap_or_else(|e| panic!("exec failed: {e}"))
62 .code()
63 .unwrap_or_else(|| panic!("child killed by signal"))
64 );
65
66 umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
67 }
68
69 #[test]
test_mount_rdonly_disallows_write()70 fn test_mount_rdonly_disallows_write() {
71 require_capability!("test_mount_rdonly_disallows_write", CAP_SYS_ADMIN);
72 let tempdir = tempfile::tempdir().unwrap();
73
74 mount(
75 NONE,
76 tempdir.path(),
77 Some(b"tmpfs".as_ref()),
78 MsFlags::MS_RDONLY,
79 NONE,
80 )
81 .unwrap_or_else(|e| panic!("mount failed: {e}"));
82
83 // EROFS: Read-only file system
84 assert_eq!(
85 EROFS,
86 File::create(tempdir.path().join("test"))
87 .unwrap_err()
88 .raw_os_error()
89 .unwrap()
90 );
91
92 umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
93 }
94
95 #[test]
test_mount_noexec_disallows_exec()96 fn test_mount_noexec_disallows_exec() {
97 require_capability!("test_mount_noexec_disallows_exec", CAP_SYS_ADMIN);
98 let tempdir = tempfile::tempdir().unwrap();
99
100 mount(
101 NONE,
102 tempdir.path(),
103 Some(b"tmpfs".as_ref()),
104 MsFlags::MS_NOEXEC,
105 NONE,
106 )
107 .unwrap_or_else(|e| panic!("mount failed: {e}"));
108
109 let test_path = tempdir.path().join("test");
110
111 fs::OpenOptions::new()
112 .create(true)
113 .write(true)
114 .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
115 .open(&test_path)
116 .and_then(|mut f| f.write(SCRIPT_CONTENTS))
117 .unwrap_or_else(|e| panic!("write failed: {e}"));
118
119 // Verify that we cannot execute despite a+x permissions being set.
120 let mode = stat::Mode::from_bits_truncate(
121 fs::metadata(&test_path)
122 .map(|md| md.permissions().mode())
123 .unwrap_or_else(|e| panic!("metadata failed: {e}")),
124 );
125
126 assert!(
127 mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH),
128 "{:?} did not have execute permissions",
129 &test_path
130 );
131
132 // EACCES: Permission denied
133 assert_eq!(
134 EACCES,
135 Command::new(&test_path)
136 .status()
137 .unwrap_err()
138 .raw_os_error()
139 .unwrap()
140 );
141
142 umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
143 }
144
145 #[test]
test_mount_bind()146 fn test_mount_bind() {
147 require_capability!("test_mount_bind", CAP_SYS_ADMIN);
148 let tempdir = tempfile::tempdir().unwrap();
149 let file_name = "test";
150
151 {
152 let mount_point = tempfile::tempdir().unwrap();
153
154 mount(
155 Some(tempdir.path()),
156 mount_point.path(),
157 NONE,
158 MsFlags::MS_BIND,
159 NONE,
160 )
161 .unwrap_or_else(|e| panic!("mount failed: {e}"));
162
163 fs::OpenOptions::new()
164 .create(true)
165 .write(true)
166 .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
167 .open(mount_point.path().join(file_name))
168 .and_then(|mut f| f.write(SCRIPT_CONTENTS))
169 .unwrap_or_else(|e| panic!("write failed: {e}"));
170
171 umount(mount_point.path())
172 .unwrap_or_else(|e| panic!("umount failed: {e}"));
173 }
174
175 // Verify the file written in the mount shows up in source directory, even
176 // after unmounting.
177
178 let mut buf = Vec::new();
179 File::open(tempdir.path().join(file_name))
180 .and_then(|mut f| f.read_to_end(&mut buf))
181 .unwrap_or_else(|e| panic!("read failed: {e}"));
182 assert_eq!(buf, SCRIPT_CONTENTS);
183 }
184