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 // while forking and unmounting prevent other child processes
57 let _m = FORK_MTX.lock();
58 // Verify execute.
59 assert_eq!(
60 EXPECTED_STATUS,
61 Command::new(&test_path)
62 .status()
63 .unwrap_or_else(|e| panic!("exec failed: {e}"))
64 .code()
65 .unwrap_or_else(|| panic!("child killed by signal"))
66 );
67
68 umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
69 }
70
71 #[test]
test_mount_rdonly_disallows_write()72 fn test_mount_rdonly_disallows_write() {
73 require_capability!("test_mount_rdonly_disallows_write", CAP_SYS_ADMIN);
74 let tempdir = tempfile::tempdir().unwrap();
75
76 mount(
77 NONE,
78 tempdir.path(),
79 Some(b"tmpfs".as_ref()),
80 MsFlags::MS_RDONLY,
81 NONE,
82 )
83 .unwrap_or_else(|e| panic!("mount failed: {e}"));
84
85 // EROFS: Read-only file system
86 assert_eq!(
87 EROFS,
88 File::create(tempdir.path().join("test"))
89 .unwrap_err()
90 .raw_os_error()
91 .unwrap()
92 );
93
94 umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
95 }
96
97 #[test]
test_mount_noexec_disallows_exec()98 fn test_mount_noexec_disallows_exec() {
99 require_capability!("test_mount_noexec_disallows_exec", CAP_SYS_ADMIN);
100 let tempdir = tempfile::tempdir().unwrap();
101
102 mount(
103 NONE,
104 tempdir.path(),
105 Some(b"tmpfs".as_ref()),
106 MsFlags::MS_NOEXEC,
107 NONE,
108 )
109 .unwrap_or_else(|e| panic!("mount failed: {e}"));
110
111 let test_path = tempdir.path().join("test");
112
113 fs::OpenOptions::new()
114 .create(true)
115 .write(true)
116 .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
117 .open(&test_path)
118 .and_then(|mut f| f.write(SCRIPT_CONTENTS))
119 .unwrap_or_else(|e| panic!("write failed: {e}"));
120
121 // Verify that we cannot execute despite a+x permissions being set.
122 let mode = stat::Mode::from_bits_truncate(
123 fs::metadata(&test_path)
124 .map(|md| md.permissions().mode())
125 .unwrap_or_else(|e| panic!("metadata failed: {e}")),
126 );
127
128 assert!(
129 mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH),
130 "{:?} did not have execute permissions",
131 &test_path
132 );
133
134 // while forking and unmounting prevent other child processes
135 let _m = FORK_MTX.lock();
136 // EACCES: Permission denied
137 assert_eq!(
138 EACCES,
139 Command::new(&test_path)
140 .status()
141 .unwrap_err()
142 .raw_os_error()
143 .unwrap()
144 );
145
146 umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
147 }
148
149 #[test]
test_mount_bind()150 fn test_mount_bind() {
151 require_capability!("test_mount_bind", CAP_SYS_ADMIN);
152 let tempdir = tempfile::tempdir().unwrap();
153 let file_name = "test";
154
155 {
156 let mount_point = tempfile::tempdir().unwrap();
157
158 mount(
159 Some(tempdir.path()),
160 mount_point.path(),
161 NONE,
162 MsFlags::MS_BIND,
163 NONE,
164 )
165 .unwrap_or_else(|e| panic!("mount failed: {e}"));
166
167 fs::OpenOptions::new()
168 .create(true)
169 .write(true)
170 .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
171 .open(mount_point.path().join(file_name))
172 .and_then(|mut f| f.write(SCRIPT_CONTENTS))
173 .unwrap_or_else(|e| panic!("write failed: {e}"));
174
175 // wait for child processes to prevent EBUSY
176 let _m = FORK_MTX.lock();
177 umount(mount_point.path())
178 .unwrap_or_else(|e| panic!("umount failed: {e}"));
179 }
180
181 // Verify the file written in the mount shows up in source directory, even
182 // after unmounting.
183
184 let mut buf = Vec::new();
185 File::open(tempdir.path().join(file_name))
186 .and_then(|mut f| f.read_to_end(&mut buf))
187 .unwrap_or_else(|e| panic!("read failed: {e}"));
188 assert_eq!(buf, SCRIPT_CONTENTS);
189 }
190