1use crate::mm::MemoryAccessorExt;
6use crate::security;
7use crate::task::{CurrentTask, EventHandler, Kernel, WaitCanceler, WaitQueue, Waiter};
8use crate::vfs::buffers::{InputBuffer, OutputBuffer};
9use crate::vfs::pseudo::simple_file::{BytesFile, BytesFileOps};
10use crate::vfs::{
11 Anon, DirEntryHandle, FileHandle, FileHandleKey, FileObject, FileObjectState, FileOps,
12 FsNodeOps, FsStr, FsString, WdNumber, WeakFileHandle, default_ioctl, fileops_impl_nonseekable,
13 fileops_impl_noop_sync, fs_args, inotify,
14};
15use starnix_sync::{
16 FileOpsCore, InotifyWatchersLock, LockDepMutex, LockEqualOrBefore, Locked, Mutex, Unlocked,
17};
18use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
19use starnix_uapi::arc_key::WeakKey;
20use starnix_uapi::auth::CAP_SYS_ADMIN;
21use starnix_uapi::errors::Errno;
22use starnix_uapi::file_mode::FileMode;
23use starnix_uapi::inotify_mask::InotifyMask;
24use starnix_uapi::math::round_up_to_increment;
25use starnix_uapi::open_flags::OpenFlags;
26use starnix_uapi::user_address::{UserAddress, UserRef};
27use starnix_uapi::vfs::FdEvents;
28use starnix_uapi::{FIONREAD, errno, error, inotify_event};
29use std::borrow::Cow;
30use std::collections::{BTreeMap, HashMap, VecDeque};
31use std::mem::size_of;
32use std::sync::atomic::{AtomicI32, Ordering};
33use zerocopy::IntoBytes;
34
35const DATA_SIZE: usize = size_of::<inotify_event>();
36
37pub struct InotifyFileObject {
39 state: Mutex<InotifyState>,
40}
41
42struct InotifyState {
43 events: InotifyEventQueue,
44
45 watches: HashMap<WdNumber, DirEntryHandle>,
46
47 last_watch_id: i32,
49}
50
51#[derive(Clone)]
53pub struct InotifyWatcher {
54 pub watch_id: WdNumber,
55
56 pub mask: InotifyMask,
57}
58
59#[derive(Default)]
60pub struct InotifyWatchers {
61 watchers: LockDepMutex<BTreeMap<FileHandleKey, InotifyWatcher>, InotifyWatchersLock>,
62}
63
64#[derive(Default)]
65struct InotifyEventQueue {
66 queue: VecDeque<InotifyEvent>,
69
70 waiters: WaitQueue,
72
73 size_bytes: usize,
75
76 max_queued_events: usize,
79}
80
81#[derive(Debug, PartialEq, Eq)]
83struct InotifyEvent {
84 watch_id: WdNumber,
85
86 mask: InotifyMask,
87
88 cookie: u32,
89
90 name: FsString,
91}
92
93impl InotifyState {
94 fn next_watch_id(&mut self) -> WdNumber {
95 self.last_watch_id += 1;
96 WdNumber::from_raw(self.last_watch_id)
97 }
98}
99
100impl InotifyFileObject {
101 pub fn new_file<L>(
103 locked: &mut Locked<L>,
104 current_task: &CurrentTask,
105 non_blocking: bool,
106 ) -> FileHandle
107 where
108 L: LockEqualOrBefore<FileOpsCore>,
109 {
110 let flags =
111 OpenFlags::RDONLY | if non_blocking { OpenFlags::NONBLOCK } else { OpenFlags::empty() };
112 let max_queued_events =
113 current_task.kernel().system_limits.inotify.max_queued_events.load(Ordering::Relaxed);
114 assert!(max_queued_events >= 0);
115 Anon::new_private_file(
116 locked,
117 current_task,
118 Box::new(InotifyFileObject {
119 state: InotifyState {
120 events: InotifyEventQueue::new_with_max(max_queued_events as usize),
121 watches: Default::default(),
122 last_watch_id: 0,
123 }
124 .into(),
125 }),
126 flags,
127 "inotify",
128 )
129 }
130
131 pub fn add_watch(
136 &self,
137 dir_entry: DirEntryHandle,
138 mask: InotifyMask,
139 inotify_file: &FileHandle,
140 ) -> Result<WdNumber, Errno> {
141 let weak_key = WeakKey::from(inotify_file);
142 if let Some(watch_id) = dir_entry.node.ensure_watchers().maybe_update(mask, &weak_key)? {
143 return Ok(watch_id);
144 }
145
146 let watch_id;
147 {
148 let mut state = self.state.lock();
149 watch_id = state.next_watch_id();
150 state.watches.insert(watch_id, dir_entry.clone());
151 }
152 dir_entry.node.ensure_watchers().add(mask, watch_id, weak_key);
153 Ok(watch_id)
154 }
155
156 pub fn remove_watch(&self, watch_id: WdNumber, file: &FileHandle) -> Result<(), Errno> {
160 let dir_entry;
161 {
162 let mut state = self.state.lock();
163 dir_entry = state.watches.remove(&watch_id).ok_or_else(|| errno!(EINVAL))?;
164 state.events.enqueue(InotifyEvent::new(
165 watch_id,
166 InotifyMask::IGNORED,
167 0,
168 FsString::default(),
169 ));
170 }
171 dir_entry.node.ensure_watchers().remove(&WeakKey::from(file));
172 Ok(())
173 }
174
175 fn notify(
176 &self,
177 watch_id: WdNumber,
178 event_mask: InotifyMask,
179 cookie: u32,
180 name: &FsStr,
181 remove_watcher_after_notify: bool,
182 ) {
183 #[allow(clippy::collection_is_never_read)]
185 let _dir_entry: Option<DirEntryHandle>;
186 {
187 let mut state = self.state.lock();
188 state.events.enqueue(InotifyEvent::new(watch_id, event_mask, cookie, name.to_owned()));
189 if remove_watcher_after_notify {
190 _dir_entry = state.watches.remove(&watch_id);
191 state.events.enqueue(InotifyEvent::new(
192 watch_id,
193 InotifyMask::IGNORED,
194 0,
195 FsString::default(),
196 ));
197 }
198 }
199 }
200
201 fn available(&self) -> usize {
202 let state = self.state.lock();
203 state.events.size_bytes
204 }
205}
206
207impl FileOps for InotifyFileObject {
208 fileops_impl_nonseekable!();
209 fileops_impl_noop_sync!();
210
211 fn write(
212 &self,
213 _locked: &mut Locked<FileOpsCore>,
214 _file: &FileObject,
215 _current_task: &CurrentTask,
216 offset: usize,
217 _data: &mut dyn InputBuffer,
218 ) -> Result<usize, Errno> {
219 debug_assert!(offset == 0);
220 error!(EINVAL)
221 }
222
223 fn read(
224 &self,
225 locked: &mut Locked<FileOpsCore>,
226 file: &FileObject,
227 current_task: &CurrentTask,
228 offset: usize,
229 data: &mut dyn OutputBuffer,
230 ) -> Result<usize, Errno> {
231 debug_assert!(offset == 0);
232
233 file.blocking_op(locked, current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, |_| {
234 let mut state = self.state.lock();
235 if let Some(front) = state.events.front() {
236 if data.available() < front.size() {
237 return error!(EINVAL);
238 }
239 } else {
240 return error!(EAGAIN);
241 }
242
243 let mut bytes_read: usize = 0;
244 while let Some(front) = state.events.front() {
245 if data.available() < front.size() {
246 break;
247 }
248 bytes_read += state.events.dequeue().unwrap().write_to(data)?;
251 }
252 Ok(bytes_read)
253 })
254 }
255
256 fn ioctl(
257 &self,
258 locked: &mut Locked<Unlocked>,
259 file: &FileObject,
260 current_task: &CurrentTask,
261 request: u32,
262 arg: SyscallArg,
263 ) -> Result<SyscallResult, Errno> {
264 let user_addr = UserAddress::from(arg);
265 match request {
266 FIONREAD => {
267 let addr = UserRef::<i32>::new(user_addr);
268 let size = i32::try_from(self.available()).unwrap_or(i32::MAX);
269 current_task.write_object(addr, &size).map(|_| SUCCESS)
270 }
271 _ => default_ioctl(file, locked, current_task, request, arg),
272 }
273 }
274
275 fn wait_async(
276 &self,
277 _locked: &mut Locked<FileOpsCore>,
278 _file: &FileObject,
279 _current_task: &CurrentTask,
280 waiter: &Waiter,
281 events: FdEvents,
282 handler: EventHandler,
283 ) -> Option<WaitCanceler> {
284 Some(self.state.lock().events.waiters.wait_async_fd_events(waiter, events, handler))
285 }
286
287 fn query_events(
288 &self,
289 _locked: &mut Locked<FileOpsCore>,
290 _file: &FileObject,
291 _current_task: &CurrentTask,
292 ) -> Result<FdEvents, Errno> {
293 if self.available() > 0 { Ok(FdEvents::POLLIN) } else { Ok(FdEvents::empty()) }
294 }
295
296 fn close(
297 self: Box<Self>,
298 _locked: &mut Locked<FileOpsCore>,
299 file: &FileObjectState,
300 _current_task: &CurrentTask,
301 ) {
302 let dir_entries = {
303 let mut state = self.state.lock();
304 state.watches.drain().map(|(_key, value)| value).collect::<Vec<_>>()
305 };
306
307 for dir_entry in dir_entries {
308 dir_entry.node.ensure_watchers().remove_by_ref(&file.weak_handle);
309 }
310 }
311
312 fn extra_fdinfo(
313 &self,
314 _locked: &mut Locked<FileOpsCore>,
315 file: &FileHandle,
316 _current_task: &CurrentTask,
317 ) -> Option<FsString> {
318 let state = self.state.lock();
319 let mut info = String::new();
320 for dir_entry in state.watches.values() {
321 let ino = dir_entry.node.ino;
322 let sdev = dir_entry.node.fs().dev_id;
323 if let Some(watcher) = dir_entry.node.ensure_watchers().get(&WeakKey::from(file)) {
324 let wd = watcher.watch_id;
325 let mask = watcher.mask;
326 info.push_str(&format!(
327 "inotify wd:{} ino:{:x} sdev:{:x} mask:{:x}\n",
328 wd.raw(),
329 ino,
330 sdev.bits(),
331 mask.bits()
332 ));
333 }
334 }
335 Some(info.into())
336 }
337}
338
339impl InotifyEventQueue {
340 fn new_with_max(max_queued_events: usize) -> Self {
341 InotifyEventQueue {
342 queue: Default::default(),
343 waiters: Default::default(),
344 size_bytes: 0,
345 max_queued_events,
346 }
347 }
348
349 fn enqueue(&mut self, mut event: InotifyEvent) {
350 if self.queue.len() > self.max_queued_events {
351 return;
352 }
353 if self.queue.len() == self.max_queued_events {
354 event = InotifyEvent::new(
356 WdNumber::from_raw(-1),
357 InotifyMask::Q_OVERFLOW,
358 0,
359 FsString::default(),
360 );
361 }
362 if Some(&event) == self.queue.back() {
363 return;
369 }
370 self.size_bytes += event.size();
371 self.queue.push_back(event);
372 self.waiters.notify_fd_events(FdEvents::POLLIN);
373 }
374
375 fn front(&self) -> Option<&InotifyEvent> {
376 self.queue.front()
377 }
378
379 fn dequeue(&mut self) -> Option<InotifyEvent> {
380 let maybe_event = self.queue.pop_front();
381 if let Some(event) = maybe_event.as_ref() {
382 self.size_bytes -= event.size();
383 }
384 maybe_event
385 }
386}
387
388impl InotifyEvent {
389 fn new(watch_id: WdNumber, mask: InotifyMask, cookie: u32, mut name: FsString) -> Self {
391 if !name.is_empty() {
392 let len = round_up_to_increment(name.len() + 1, DATA_SIZE)
393 .expect("padded name should not overflow");
394 name.resize(len, 0);
395 }
396 InotifyEvent { watch_id, mask, cookie, name }
397 }
398
399 fn size(&self) -> usize {
400 DATA_SIZE + self.name.len()
401 }
402
403 fn write_to(&self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
404 let event = inotify_event {
405 wd: self.watch_id.raw(),
406 mask: self.mask.bits(),
407 cookie: self.cookie,
408 len: self.name.len().try_into().map_err(|_| errno!(EINVAL))?,
409 name: Default::default(),
411 };
412
413 let mut bytes_written = data.write(event.as_bytes())?;
414 if !self.name.is_empty() {
415 bytes_written += data.write(self.name.as_bytes())?;
416 }
417
418 debug_assert!(bytes_written == self.size());
419 Ok(bytes_written)
420 }
421}
422
423impl InotifyWatchers {
424 fn add(&self, mask: InotifyMask, watch_id: WdNumber, inotify: FileHandleKey) {
425 let mut watchers = self.watchers.lock();
426 watchers.insert(inotify, inotify::InotifyWatcher { watch_id, mask });
427 }
428
429 fn maybe_update(
436 &self,
437 mask: InotifyMask,
438 inotify: &FileHandleKey,
439 ) -> Result<Option<WdNumber>, Errno> {
440 let combine_existing = mask.contains(InotifyMask::MASK_ADD);
441 let create_new = mask.contains(InotifyMask::MASK_CREATE);
442 if combine_existing && create_new {
443 return error!(EINVAL);
444 }
445
446 let mut watchers = self.watchers.lock();
447 if let Some(watcher) = watchers.get_mut(inotify) {
448 if create_new {
449 return error!(EEXIST);
450 }
451
452 if combine_existing {
453 watcher.mask.insert(mask);
454 } else {
455 watcher.mask = mask;
456 }
457 Ok(Some(watcher.watch_id))
458 } else {
459 Ok(None)
460 }
461 }
462
463 fn get(&self, inotify: &FileHandleKey) -> Option<InotifyWatcher> {
464 self.watchers.lock().get(inotify).cloned()
465 }
466
467 fn remove(&self, inotify: &FileHandleKey) {
468 let mut watchers = self.watchers.lock();
469 watchers.remove(inotify);
470 }
471
472 fn remove_by_ref(&self, inotify: &WeakFileHandle) {
473 let mut watchers = self.watchers.lock();
474 watchers.retain(|weak_key, _| weak_key.0.strong_count() > 0 && weak_key != inotify)
475 }
476
477 pub fn notify(
484 &self,
485 mut event_mask: InotifyMask,
486 cookie: u32,
487 name: &FsStr,
488 mode: FileMode,
489 is_dead: bool,
490 ) {
491 if cookie != 0 {
492 debug_assert!(
495 event_mask.contains(InotifyMask::MOVE_FROM)
496 || event_mask.contains(InotifyMask::MOVE_TO)
497 );
498 }
499 struct InotifyWatch {
501 watch_id: WdNumber,
502 file: FileHandle,
503 should_remove: bool,
504 }
505 let mut watches: Vec<InotifyWatch> = vec![];
506 {
507 let mut watchers = self.watchers.lock();
508 watchers.retain(|inotify, watcher| {
509 let mut should_remove = event_mask == InotifyMask::DELETE_SELF;
510 if watcher.mask.contains(event_mask)
511 && !(is_dead && watcher.mask.contains(InotifyMask::EXCL_UNLINK))
512 {
513 should_remove = should_remove || watcher.mask.contains(InotifyMask::ONESHOT);
514 if let Some(file) = inotify.0.upgrade() {
515 watches.push(InotifyWatch {
516 watch_id: watcher.watch_id,
517 file,
518 should_remove,
519 });
520 } else {
521 should_remove = true;
522 }
523 }
524 !should_remove
525 });
526 }
527
528 if mode.is_dir() {
529 if event_mask != InotifyMask::DELETE_SELF && event_mask != InotifyMask::MOVE_SELF {
531 event_mask |= InotifyMask::ISDIR;
532 }
533 }
534
535 for watch in watches {
536 let inotify = watch
537 .file
538 .downcast_file::<InotifyFileObject>()
539 .expect("failed to downcast to inotify");
540 inotify.notify(watch.watch_id, event_mask, cookie, name, watch.should_remove);
541 }
542 }
543}
544
545impl Kernel {
546 pub fn get_next_inotify_cookie(&self) -> u32 {
547 let cookie = self.next_inotify_cookie.next();
548 if cookie == 0 {
550 return self.next_inotify_cookie.next();
551 }
552 cookie
553 }
554}
555
556#[derive(Debug)]
558pub struct InotifyLimits {
559 pub max_queued_events: AtomicI32,
562
563 pub max_user_instances: AtomicI32,
565
566 pub max_user_watches: AtomicI32,
568}
569
570pub trait AtomicGetter {
571 fn get_atomic<'a>(current_task: &'a CurrentTask) -> &'a AtomicI32;
572}
573
574pub struct MaxQueuedEventsGetter;
575impl AtomicGetter for MaxQueuedEventsGetter {
576 fn get_atomic<'a>(current_task: &'a CurrentTask) -> &'a AtomicI32 {
577 ¤t_task.kernel().system_limits.inotify.max_queued_events
578 }
579}
580
581pub struct MaxUserInstancesGetter;
582impl AtomicGetter for MaxUserInstancesGetter {
583 fn get_atomic<'a>(current_task: &'a CurrentTask) -> &'a AtomicI32 {
584 ¤t_task.kernel().system_limits.inotify.max_user_instances
585 }
586}
587
588pub struct MaxUserWatchesGetter;
589impl AtomicGetter for MaxUserWatchesGetter {
590 fn get_atomic<'a>(current_task: &'a CurrentTask) -> &'a AtomicI32 {
591 ¤t_task.kernel().system_limits.inotify.max_user_watches
592 }
593}
594
595pub struct InotifyLimitProcFile<G: AtomicGetter + Send + Sync + 'static> {
596 marker: std::marker::PhantomData<G>,
597}
598
599impl<G: AtomicGetter + Send + Sync + 'static> InotifyLimitProcFile<G> {
600 pub fn new_node() -> impl FsNodeOps {
601 BytesFile::new_node(Self { marker: Default::default() })
602 }
603}
604
605impl<G: AtomicGetter + Send + Sync + 'static> BytesFileOps for InotifyLimitProcFile<G> {
606 fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
607 security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
608 let value = fs_args::parse::<i32>(data.as_slice().into())?;
609 if value < 0 {
610 return error!(EINVAL);
611 }
612 G::get_atomic(current_task).store(value, Ordering::Relaxed);
613 Ok(())
614 }
615
616 fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
617 Ok(G::get_atomic(current_task).load(Ordering::Relaxed).to_string().into_bytes().into())
618 }
619}
620
621pub type InotifyMaxQueuedEvents = InotifyLimitProcFile<MaxQueuedEventsGetter>;
622pub type InotifyMaxUserInstances = InotifyLimitProcFile<MaxUserInstancesGetter>;
623pub type InotifyMaxUserWatches = InotifyLimitProcFile<MaxUserWatchesGetter>;
624
625#[cfg(test)]
626mod tests {
627 use super::{DATA_SIZE, InotifyEvent, InotifyEventQueue, InotifyFileObject};
628 use crate::testing::spawn_kernel_and_run_with_pkgfs;
629 use crate::vfs::buffers::VecOutputBuffer;
630 use crate::vfs::{OutputBuffer, WdNumber};
631 use starnix_uapi::arc_key::WeakKey;
632 use starnix_uapi::file_mode::FileMode;
633 use starnix_uapi::inotify_mask::InotifyMask;
634
635 #[::fuchsia::test]
636 fn inotify_event() {
637 let event = InotifyEvent::new(WdNumber::from_raw(1), InotifyMask::ACCESS, 0, "".into());
638 let mut buffer = VecOutputBuffer::new(DATA_SIZE + 100);
639 let bytes_written = event.write_to(&mut buffer).expect("write_to buffer");
640
641 assert_eq!(bytes_written, DATA_SIZE);
642 assert_eq!(buffer.bytes_written(), DATA_SIZE);
643 }
644
645 #[::fuchsia::test]
646 fn inotify_event_with_name() {
647 let name = "file1";
649 let event = InotifyEvent::new(WdNumber::from_raw(1), InotifyMask::ACCESS, 0, name.into());
650 let mut buffer = VecOutputBuffer::new(DATA_SIZE + 100);
651 let bytes_written = event.write_to(&mut buffer).expect("write_to buffer");
652
653 assert!(bytes_written > DATA_SIZE);
654 assert_eq!(bytes_written % DATA_SIZE, 0);
655 assert_eq!(buffer.bytes_written(), bytes_written);
656 }
657
658 #[::fuchsia::test]
659 fn inotify_event_queue() {
660 let mut event_queue = InotifyEventQueue::new_with_max(10);
661
662 event_queue.enqueue(InotifyEvent::new(
663 WdNumber::from_raw(1),
664 InotifyMask::ACCESS,
665 0,
666 "".into(),
667 ));
668
669 assert_eq!(event_queue.queue.len(), 1);
670 assert_eq!(event_queue.size_bytes, DATA_SIZE);
671
672 let event = event_queue.dequeue();
673
674 assert_eq!(
675 event,
676 Some(InotifyEvent::new(WdNumber::from_raw(1), InotifyMask::ACCESS, 0, "".into()))
677 );
678 assert_eq!(event_queue.queue.len(), 0);
679 assert_eq!(event_queue.size_bytes, 0);
680 }
681
682 #[::fuchsia::test]
683 fn inotify_event_queue_coalesce_events() {
684 let mut event_queue = InotifyEventQueue::new_with_max(10);
685
686 event_queue.enqueue(InotifyEvent::new(
688 WdNumber::from_raw(1),
689 InotifyMask::ACCESS,
690 0,
691 "".into(),
692 ));
693 event_queue.enqueue(InotifyEvent::new(
694 WdNumber::from_raw(1),
695 InotifyMask::ACCESS,
696 0,
697 "".into(),
698 ));
699
700 assert_eq!(event_queue.queue.len(), 1);
701 }
702
703 #[::fuchsia::test]
704 fn inotify_event_queue_max_queued_events() {
705 let mut event_queue = InotifyEventQueue::new_with_max(1);
706
707 event_queue.enqueue(InotifyEvent::new(
709 WdNumber::from_raw(1),
710 InotifyMask::ACCESS,
711 0,
712 "".into(),
713 ));
714 event_queue.enqueue(InotifyEvent::new(
715 WdNumber::from_raw(1),
716 InotifyMask::MODIFY,
717 0,
718 "".into(),
719 ));
720
721 assert_eq!(event_queue.queue.len(), 2);
722 assert_eq!(event_queue.queue.get(0).unwrap().mask, InotifyMask::ACCESS);
723 assert_eq!(event_queue.queue.get(1).unwrap().mask, InotifyMask::Q_OVERFLOW);
724
725 event_queue.enqueue(InotifyEvent::new(
727 WdNumber::from_raw(1),
728 InotifyMask::ATTRIB,
729 0,
730 "".into(),
731 ));
732 assert_eq!(event_queue.queue.len(), 2);
733 assert_eq!(event_queue.queue.get(0).unwrap().mask, InotifyMask::ACCESS);
734 assert_eq!(event_queue.queue.get(1).unwrap().mask, InotifyMask::Q_OVERFLOW);
735
736 let _event = event_queue.dequeue();
738 assert_eq!(event_queue.queue.len(), 1);
739
740 event_queue.enqueue(InotifyEvent::new(
743 WdNumber::from_raw(1),
744 InotifyMask::DELETE,
745 0,
746 "".into(),
747 ));
748 assert_eq!(event_queue.queue.len(), 1);
749 assert_eq!(event_queue.queue.get(0).unwrap().mask, InotifyMask::Q_OVERFLOW);
750 }
751
752 #[::fuchsia::test]
753 async fn notify_from_watchers() {
754 spawn_kernel_and_run_with_pkgfs(async |locked, current_task| {
755 let file = InotifyFileObject::new_file(locked, ¤t_task, true);
756 let inotify =
757 file.downcast_file::<InotifyFileObject>().expect("failed to downcast to inotify");
758
759 let root = current_task.fs().root().entry;
761 assert!(inotify.add_watch(root.clone(), InotifyMask::ALL_EVENTS, &file).is_ok());
762
763 {
764 let watchers = root.node.ensure_watchers().watchers.lock();
765 assert_eq!(watchers.len(), 1);
766 }
767
768 root.node.notify(InotifyMask::ACCESS, 0, Default::default(), FileMode::IFREG, false);
770
771 assert_eq!(inotify.available(), DATA_SIZE);
772 {
773 let state = inotify.state.lock();
774 assert_eq!(state.watches.len(), 1);
775 assert_eq!(state.events.queue.len(), 1);
776 }
777
778 root.node.notify(InotifyMask::ATTRIB, 0, Default::default(), FileMode::IFREG, false);
780
781 assert_eq!(inotify.available(), DATA_SIZE * 2);
782 {
783 let state = inotify.state.lock();
784 assert_eq!(state.events.queue.len(), 2);
785 }
786
787 let mut buffer = VecOutputBuffer::new(DATA_SIZE);
789 let bytes_read =
790 file.read(locked, ¤t_task, &mut buffer).expect("read into buffer");
791
792 assert_eq!(bytes_read, DATA_SIZE);
793 assert_eq!(inotify.available(), DATA_SIZE);
794 {
795 let state = inotify.state.lock();
796 assert_eq!(state.events.queue.len(), 1);
797 }
798
799 buffer.reset();
801 let bytes_read =
802 file.read(locked, ¤t_task, &mut buffer).expect("read into buffer");
803
804 assert_eq!(bytes_read, DATA_SIZE);
805 assert_eq!(inotify.available(), 0);
806 {
807 let state = inotify.state.lock();
808 assert_eq!(state.events.queue.len(), 0);
809 }
810 })
811 .await;
812 }
813
814 #[::fuchsia::test]
815 async fn notify_deletion_from_watchers() {
816 spawn_kernel_and_run_with_pkgfs(async |locked, current_task| {
817 let file = InotifyFileObject::new_file(locked, ¤t_task, true);
818 let inotify =
819 file.downcast_file::<InotifyFileObject>().expect("failed to downcast to inotify");
820
821 let root = current_task.fs().root().entry;
823 assert!(inotify.add_watch(root.clone(), InotifyMask::ALL_EVENTS, &file).is_ok());
824
825 {
826 let watchers = root.node.ensure_watchers().watchers.lock();
827 assert_eq!(watchers.len(), 1);
828 }
829
830 root.node.notify(
831 InotifyMask::DELETE_SELF,
832 0,
833 Default::default(),
834 FileMode::IFREG,
835 false,
836 );
837
838 {
839 let watchers = root.node.ensure_watchers().watchers.lock();
840 assert_eq!(watchers.len(), 0);
841 }
842
843 {
844 let state = inotify.state.lock();
845 assert_eq!(state.watches.len(), 0);
846 assert_eq!(state.events.queue.len(), 2);
847
848 assert_eq!(state.events.queue.get(0).unwrap().mask, InotifyMask::DELETE_SELF);
849 assert_eq!(state.events.queue.get(1).unwrap().mask, InotifyMask::IGNORED);
850 }
851 })
852 .await;
853 }
854
855 #[::fuchsia::test]
856 async fn inotify_on_same_file() {
857 spawn_kernel_and_run_with_pkgfs(async |locked, current_task| {
858 let file = InotifyFileObject::new_file(locked, ¤t_task, true);
859 let file_key = WeakKey::from(&file);
860 let inotify =
861 file.downcast_file::<InotifyFileObject>().expect("failed to downcast to inotify");
862
863 let root = current_task.fs().root().entry;
865
866 assert!(
868 inotify
869 .add_watch(
870 root.clone(),
871 InotifyMask::MODIFY | InotifyMask::MASK_ADD | InotifyMask::MASK_CREATE,
872 &file
873 )
874 .is_err()
875 );
876
877 assert!(
878 inotify
879 .add_watch(root.clone(), InotifyMask::MODIFY | InotifyMask::MASK_CREATE, &file)
880 .is_ok()
881 );
882
883 {
884 let watchers = root.node.ensure_watchers().watchers.lock();
885 assert_eq!(watchers.len(), 1);
886 assert!(watchers.get(&file_key).unwrap().mask.contains(InotifyMask::MODIFY));
887 }
888
889 assert!(inotify.add_watch(root.clone(), InotifyMask::ACCESS, &file).is_ok());
891
892 {
893 let watchers = root.node.ensure_watchers().watchers.lock();
894 assert_eq!(watchers.len(), 1);
895 assert!(watchers.get(&file_key).unwrap().mask.contains(InotifyMask::ACCESS));
896 assert!(!watchers.get(&file_key).unwrap().mask.contains(InotifyMask::MODIFY));
897 }
898
899 assert!(
901 inotify
902 .add_watch(root.clone(), InotifyMask::MODIFY | InotifyMask::MASK_ADD, &file)
903 .is_ok()
904 );
905
906 {
907 let watchers = root.node.ensure_watchers().watchers.lock();
908 assert_eq!(watchers.len(), 1);
909 assert!(watchers.get(&file_key).unwrap().mask.contains(InotifyMask::ACCESS));
910 assert!(watchers.get(&file_key).unwrap().mask.contains(InotifyMask::MODIFY));
911 }
912 })
913 .await;
914 }
915
916 #[::fuchsia::test]
917 async fn coalesce_events() {
918 spawn_kernel_and_run_with_pkgfs(async |locked, current_task| {
919 let file = InotifyFileObject::new_file(locked, ¤t_task, true);
920 let inotify =
921 file.downcast_file::<InotifyFileObject>().expect("failed to downcast to inotify");
922
923 let root = current_task.fs().root().entry;
925 assert!(inotify.add_watch(root.clone(), InotifyMask::ALL_EVENTS, &file).is_ok());
926
927 {
928 let watchers = root.node.ensure_watchers().watchers.lock();
929 assert_eq!(watchers.len(), 1);
930 }
931
932 root.node.notify(InotifyMask::ACCESS, 0, Default::default(), FileMode::IFREG, false);
934 root.node.notify(InotifyMask::ACCESS, 0, Default::default(), FileMode::IFREG, false);
935
936 assert_eq!(inotify.available(), DATA_SIZE);
937 {
938 let state = inotify.state.lock();
939 assert_eq!(state.watches.len(), 1);
940 assert_eq!(state.events.queue.len(), 1);
941 }
942 })
943 .await;
944 }
945}