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