starnix_core/vfs/
file_write_guard.rs1use starnix_uapi::errors::Errno;
6use starnix_uapi::seal_flags::SealFlags;
7use starnix_uapi::{errno, error};
8
9#[derive(Copy, Clone, Debug, Eq, PartialEq)]
10pub enum FileWriteGuardMode {
11 WriteFile,
13
14 WriteMapping,
16
17 ExecMapping,
19}
20
21#[derive(Default)]
25pub struct FileWriteGuardState {
26 write_exec_locks: isize,
29
30 num_write_mappings: usize,
32
33 seals: Option<SealFlags>,
35}
36
37impl FileWriteGuardState {
38 pub fn acquire(&mut self, mode: FileWriteGuardMode) -> Result<(), Errno> {
39 match mode {
40 FileWriteGuardMode::WriteFile => {
41 if self.write_exec_locks < 0 {
42 return error!(ETXTBSY);
43 }
44
45 self.write_exec_locks += 1;
49 }
50 FileWriteGuardMode::WriteMapping => {
51 self.check_no_seal(SealFlags::WRITE | SealFlags::FUTURE_WRITE)?;
52
53 assert!(self.write_exec_locks > 0);
55
56 self.write_exec_locks += 1;
57 self.num_write_mappings += 1;
58 }
59 FileWriteGuardMode::ExecMapping => {
60 if self.write_exec_locks > 0 {
61 return error!(ETXTBSY);
62 }
63 self.write_exec_locks -= 1;
64 }
65 }
66 Ok(())
67 }
68
69 pub fn release(&mut self, mode: FileWriteGuardMode) {
70 match mode {
71 FileWriteGuardMode::WriteFile => {
72 assert!(self.write_exec_locks > 0);
73 self.write_exec_locks -= 1;
74 }
75 FileWriteGuardMode::WriteMapping => {
76 assert!(self.write_exec_locks > 0);
77 self.write_exec_locks -= 1;
78 assert!(self.num_write_mappings > 0);
79 self.num_write_mappings -= 1;
80 }
81 FileWriteGuardMode::ExecMapping => {
82 assert!(self.write_exec_locks < 0);
83 self.write_exec_locks += 1;
84 }
85 };
86 }
87
88 pub fn enable_sealing(&mut self, initial_seals: SealFlags) {
89 self.seals = Some(initial_seals);
90 }
91
92 pub fn try_add_seal(&mut self, flags: SealFlags) -> Result<(), Errno> {
94 if let Some(seals) = self.seals.as_mut() {
95 if seals.contains(SealFlags::SEAL) {
96 return error!(EPERM);
98 }
99
100 if flags.contains(SealFlags::WRITE) && self.num_write_mappings > 0 {
102 return error!(EBUSY);
103 }
104
105 seals.insert(flags);
106
107 Ok(())
108 } else {
109 error!(EINVAL)
111 }
112 }
113
114 pub fn check_no_seal(&self, flags: SealFlags) -> Result<(), Errno> {
116 if let Some(seals) = self.seals.as_ref() {
117 if seals.intersects(flags) {
118 return error!(EPERM);
119 }
120 }
121 Ok(())
122 }
123
124 pub fn get_seals(&self) -> Result<SealFlags, Errno> {
126 self.seals.ok_or_else(|| errno!(EINVAL))
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::testing::spawn_kernel_and_run;
134 use crate::vfs::FsNodeHandle;
135 use starnix_uapi::device_type::DeviceType;
136 use starnix_uapi::file_mode::FileMode;
137
138 fn create_fs_node(
139 locked: &mut starnix_sync::Locked<starnix_sync::Unlocked>,
140 current_task: &crate::task::CurrentTask,
141 ) -> FsNodeHandle {
142 current_task
143 .fs()
144 .root()
145 .create_node(locked, current_task, "foo".into(), FileMode::IFREG, DeviceType::NONE)
146 .expect("create_node")
147 .entry
148 .node
149 .clone()
150 }
151
152 #[derive(Debug)]
153 struct FileWriteGuard {
154 mode: FileWriteGuardMode,
155 node: FsNodeHandle,
156 }
157
158 impl FileWriteGuard {
159 pub fn new(node: &FsNodeHandle, mode: FileWriteGuardMode) -> Result<FileWriteGuard, Errno> {
160 let mut state = node.write_guard_state.lock();
161 state.acquire(mode)?;
162 Ok(FileWriteGuard { mode, node: node.clone() })
163 }
164 }
165
166 impl Drop for FileWriteGuard {
167 fn drop(&mut self) {
168 let mut state = self.node.write_guard_state.lock();
169 state.release(self.mode);
170 }
171 }
172
173 #[::fuchsia::test]
174 async fn test_write_exec_locking() {
175 spawn_kernel_and_run(async |locked, current_task| {
176 let fs_node = create_fs_node(locked, current_task);
177
178 let write_guard = FileWriteGuard::new(&fs_node, FileWriteGuardMode::WriteFile)
179 .expect("FsNode::lock failed unexpectedly");
180
181 assert_eq!(
182 FileWriteGuard::new(&fs_node, FileWriteGuardMode::ExecMapping).unwrap_err(),
183 errno!(ETXTBSY)
184 );
185
186 let write_mapping_guard =
187 FileWriteGuard::new(&fs_node, FileWriteGuardMode::WriteMapping)
188 .expect("FsNode::lock failed unexpectedly");
189
190 assert_eq!(
191 FileWriteGuard::new(&fs_node, FileWriteGuardMode::ExecMapping).unwrap_err(),
192 errno!(ETXTBSY)
193 );
194
195 std::mem::drop(write_guard);
196
197 assert_eq!(
198 FileWriteGuard::new(&fs_node, FileWriteGuardMode::ExecMapping).unwrap_err(),
199 errno!(ETXTBSY)
200 );
201
202 std::mem::drop(write_mapping_guard);
203
204 let exec_guard = FileWriteGuard::new(&fs_node, FileWriteGuardMode::ExecMapping)
205 .expect("FsNode::lock failed unexpectedly");
206
207 assert_eq!(
208 FileWriteGuard::new(&fs_node, FileWriteGuardMode::WriteFile).unwrap_err(),
209 errno!(ETXTBSY)
210 );
211
212 std::mem::drop(exec_guard);
213
214 FileWriteGuard::new(&fs_node, FileWriteGuardMode::WriteFile)
215 .expect("FsNode::lock failed unexpectedly");
216 })
217 .await;
218 }
219
220 #[::fuchsia::test]
221 async fn test_no_seals() {
222 let mut state = FileWriteGuardState::default();
223
224 assert_eq!(state.try_add_seal(SealFlags::WRITE), error!(EINVAL));
226 assert_eq!(state.check_no_seal(SealFlags::WRITE), Ok(()));
227 assert_eq!(state.get_seals(), error!(EINVAL));
228 }
229
230 #[::fuchsia::test]
231 async fn test_seals() {
232 spawn_kernel_and_run(async |locked, current_task| {
233 let fs_node = create_fs_node(locked, current_task);
234
235 {
236 let mut state = fs_node.write_guard_state.lock();
237
238 state.enable_sealing(SealFlags::empty());
239
240 assert_eq!(state.check_no_seal(SealFlags::WRITE), Ok(()));
241 assert_eq!(state.get_seals(), Ok(SealFlags::empty()));
242
243 assert_eq!(state.try_add_seal(SealFlags::WRITE), Ok(()));
245 assert_eq!(state.check_no_seal(SealFlags::WRITE), error!(EPERM));
246 assert_eq!(state.get_seals(), Ok(SealFlags::WRITE));
247 }
248
249 let file_guard = FileWriteGuard::new(&fs_node, FileWriteGuardMode::WriteFile)
251 .expect("lock(WriteFile) failed");
252
253 assert_eq!(
255 FileWriteGuard::new(&fs_node, FileWriteGuardMode::WriteMapping).unwrap_err(),
256 errno!(EPERM)
257 );
258
259 std::mem::drop(file_guard);
260 })
261 .await;
262 }
263
264 #[::fuchsia::test]
265 async fn test_seals_block_when_mapped() {
266 spawn_kernel_and_run(async |locked, current_task| {
267 let fs_node = create_fs_node(locked, current_task);
268 fs_node.write_guard_state.lock().enable_sealing(SealFlags::empty());
269
270 let _write_guard = FileWriteGuard::new(&fs_node, FileWriteGuardMode::WriteFile)
271 .expect("FsNode::lock failed unexpectedly");
272 let write_mapping_guard =
273 FileWriteGuard::new(&fs_node, FileWriteGuardMode::WriteMapping)
274 .expect("FsNode::lock failed unexpectedly");
275
276 {
278 let mut state = fs_node.write_guard_state.lock();
279 assert_eq!(state.try_add_seal(SealFlags::WRITE), error!(EBUSY));
280 assert_eq!(state.check_no_seal(SealFlags::WRITE), Ok(()));
281 }
282
283 std::mem::drop(write_mapping_guard);
284
285 {
287 let mut state = fs_node.write_guard_state.lock();
288 assert_eq!(state.try_add_seal(SealFlags::WRITE), Ok(()));
289 assert_eq!(state.check_no_seal(SealFlags::WRITE), error!(EPERM));
290 }
291 })
292 .await;
293 }
294
295 #[::fuchsia::test]
296 async fn test_seals_sealed() {
297 spawn_kernel_and_run(async |locked, current_task| {
298 let fs_node = create_fs_node(locked, current_task);
299 let mut state = fs_node.write_guard_state.lock();
300
301 state.enable_sealing(SealFlags::SEAL);
302
303 assert_eq!(state.get_seals(), Ok(SealFlags::SEAL));
304
305 assert_eq!(state.try_add_seal(SealFlags::WRITE), error!(EPERM));
306 assert_eq!(state.get_seals(), Ok(SealFlags::SEAL));
307 })
308 .await;
309 }
310}