Skip to main content

starnix_core/vfs/
inotify_hook.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::vfs::{FileHandleKey, FsStr, WdNumber, WeakFileHandle};
6use starnix_sync::{InotifyWatchersLock, LockDepMutex};
7use starnix_uapi::error;
8use starnix_uapi::errors::Errno;
9use starnix_uapi::file_mode::FileMode;
10use starnix_uapi::inotify_mask::InotifyMask;
11use std::collections::BTreeMap;
12
13#[derive(Clone)]
14pub struct InotifyWatcher {
15    pub watch_id: WdNumber,
16    pub mask: InotifyMask,
17}
18
19#[derive(Default)]
20pub struct InotifyWatchers {
21    pub watchers: LockDepMutex<BTreeMap<FileHandleKey, InotifyWatcher>, InotifyWatchersLock>,
22}
23
24impl InotifyWatchers {
25    pub fn add(&self, mask: InotifyMask, watch_id: WdNumber, inotify: FileHandleKey) {
26        let mut watchers = self.watchers.lock();
27        watchers.insert(inotify, InotifyWatcher { watch_id, mask });
28    }
29
30    // Checks if inotify is already part of watchers. Replaces mask if found and returns the WdNumber.
31    // Combines mask if IN_MASK_ADD is specified in mask. Returns None if no present in watchers.
32    //
33    // Errors if:
34    //  - both IN_MASK_ADD and IN_MASK_CREATE are specified in mask, or
35    //  - IN_MASK_CREATE is specified and existing entry is found.
36    pub fn maybe_update(
37        &self,
38        mask: InotifyMask,
39        inotify: &FileHandleKey,
40    ) -> Result<Option<WdNumber>, Errno> {
41        let combine_existing = mask.contains(InotifyMask::MASK_ADD);
42        let create_new = mask.contains(InotifyMask::MASK_CREATE);
43        if combine_existing && create_new {
44            return error!(EINVAL);
45        }
46
47        let mut watchers = self.watchers.lock();
48        if let Some(watcher) = watchers.get_mut(inotify) {
49            if create_new {
50                return error!(EEXIST);
51            }
52
53            if combine_existing {
54                watcher.mask.insert(mask);
55            } else {
56                watcher.mask = mask;
57            }
58            Ok(Some(watcher.watch_id))
59        } else {
60            Ok(None)
61        }
62    }
63
64    pub fn get(&self, inotify: &FileHandleKey) -> Option<InotifyWatcher> {
65        self.watchers.lock().get(inotify).cloned()
66    }
67
68    pub fn remove(&self, inotify: &FileHandleKey) {
69        let mut watchers = self.watchers.lock();
70        watchers.remove(inotify);
71    }
72
73    pub fn remove_by_ref(&self, inotify: &WeakFileHandle) {
74        let mut watchers = self.watchers.lock();
75        watchers.retain(|weak_key, _| weak_key.0.strong_count() > 0 && weak_key != inotify)
76    }
77}
78
79pub trait NotifyHook: Send + Sync + 'static {
80    fn notify(
81        &self,
82        watchers: &InotifyWatchers,
83        event_mask: InotifyMask,
84        cookie: u32,
85        name: &FsStr,
86        mode: FileMode,
87        is_dead: bool,
88    );
89
90    fn get_next_cookie(&self) -> u32;
91}