Skip to content

Commit

Permalink
Merge pull request #635 from hugwijst/inotify-ignore-links
Browse files Browse the repository at this point in the history
Add config option to disable following symbolic links.
  • Loading branch information
dfaust authored Nov 24, 2024
2 parents ccdd4df + e14dd51 commit 2692391
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 25 deletions.
23 changes: 21 additions & 2 deletions notify/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ impl RecursiveMode {
/// Some options can be changed during runtime, others have to be set when creating the watcher backend.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct Config {
/// See [BackendConfig::with_poll_interval]
/// See [Config::with_poll_interval]
poll_interval: Option<Duration>,

/// See [BackendConfig::with_compare_contents]
/// See [Config::with_compare_contents]
compare_contents: bool,

follow_symlinks: bool,
}

impl Config {
Expand Down Expand Up @@ -94,13 +96,30 @@ impl Config {
pub fn compare_contents(&self) -> bool {
self.compare_contents
}

/// For the [INotifyWatcher](crate::INotifyWatcher), [KqueueWatcher](crate::KqueueWatcher),
/// and [PollWatcher](crate::PollWatcher).
///
/// Determine if sybolic links should be followed when recursively watching a directory.
///
/// This can't be changed during runtime. On by default.
pub fn with_follow_symlinks(mut self, follow_symlinks: bool) -> Self {
self.follow_symlinks = follow_symlinks;
self
}

/// Returns current setting
pub fn follow_symlinks(&self) -> bool {
self.follow_symlinks
}
}

impl Default for Config {
fn default() -> Self {
Self {
poll_interval: Some(Duration::from_secs(30)),
compare_contents: false,
follow_symlinks: true,
}
}
}
21 changes: 15 additions & 6 deletions notify/src/inotify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct EventLoop {
watches: HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool)>,
paths: HashMap<WatchDescriptor, PathBuf>,
rename_event: Option<Event>,
follow_links: bool,
}

/// Watcher implementation based on inotify
Expand Down Expand Up @@ -90,7 +91,11 @@ fn remove_watch_by_event(
}

impl EventLoop {
pub fn new(inotify: Inotify, event_handler: Box<dyn EventHandler>) -> Result<Self> {
pub fn new(
inotify: Inotify,
event_handler: Box<dyn EventHandler>,
follow_links: bool,
) -> Result<Self> {
let (event_loop_tx, event_loop_rx) = unbounded::<EventLoopMsg>();
let poll = mio::Poll::new()?;

Expand All @@ -112,6 +117,7 @@ impl EventLoop {
watches: HashMap::new(),
paths: HashMap::new(),
rename_event: None,
follow_links,
};
Ok(event_loop)
}
Expand Down Expand Up @@ -398,7 +404,7 @@ impl EventLoop {
}

for entry in WalkDir::new(path)
.follow_links(true)
.follow_links(self.follow_links)
.into_iter()
.filter_map(filter_dir)
{
Expand Down Expand Up @@ -523,9 +529,12 @@ fn filter_dir(e: walkdir::Result<walkdir::DirEntry>) -> Option<walkdir::DirEntry
}

impl INotifyWatcher {
fn from_event_handler(event_handler: Box<dyn EventHandler>) -> Result<Self> {
fn from_event_handler(
event_handler: Box<dyn EventHandler>,
follow_links: bool,
) -> Result<Self> {
let inotify = Inotify::init()?;
let event_loop = EventLoop::new(inotify, event_handler)?;
let event_loop = EventLoop::new(inotify, event_handler, follow_links)?;
let channel = event_loop.event_loop_tx.clone();
let waker = event_loop.event_loop_waker.clone();
event_loop.run();
Expand Down Expand Up @@ -567,8 +576,8 @@ impl INotifyWatcher {

impl Watcher for INotifyWatcher {
/// Create a new watcher.
fn new<F: EventHandler>(event_handler: F, _config: Config) -> Result<Self> {
Self::from_event_handler(Box::new(event_handler))
fn new<F: EventHandler>(event_handler: F, config: Config) -> Result<Self> {
Self::from_event_handler(Box::new(event_handler), config.follow_symlinks())
}

fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
Expand Down
29 changes: 22 additions & 7 deletions notify/src/kqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct EventLoop {
kqueue: kqueue::Watcher,
event_handler: Box<dyn EventHandler>,
watches: HashMap<PathBuf, bool>,
follow_symlinks: bool,
}

/// Watcher implementation based on inotify
Expand All @@ -50,7 +51,11 @@ enum EventLoopMsg {
}

impl EventLoop {
pub fn new(kqueue: kqueue::Watcher, event_handler: Box<dyn EventHandler>) -> Result<Self> {
pub fn new(
kqueue: kqueue::Watcher,
event_handler: Box<dyn EventHandler>,
follow_symlinks: bool,
) -> Result<Self> {
let (event_loop_tx, event_loop_rx) = unbounded::<EventLoopMsg>();
let poll = mio::Poll::new()?;

Expand All @@ -70,6 +75,7 @@ impl EventLoop {
kqueue,
event_handler,
watches: HashMap::new(),
follow_symlinks,
};
Ok(event_loop)
}
Expand Down Expand Up @@ -292,7 +298,10 @@ impl EventLoop {
if !is_recursive || !metadata(&path).map_err(Error::io)?.is_dir() {
self.add_single_watch(path, false)?;
} else {
for entry in WalkDir::new(path).follow_links(true).into_iter() {
for entry in WalkDir::new(path)
.follow_links(self.follow_symlinks)
.into_iter()
{
let entry = entry.map_err(map_walkdir_error)?;
self.add_single_watch(entry.path().to_path_buf(), is_recursive)?;
}
Expand Down Expand Up @@ -338,7 +347,10 @@ impl EventLoop {
.map_err(|e| Error::io(e).add_path(path.clone()))?;

if is_recursive || remove_recursive {
for entry in WalkDir::new(path).follow_links(true).into_iter() {
for entry in WalkDir::new(path)
.follow_links(self.follow_symlinks)
.into_iter()
{
let p = entry.map_err(map_walkdir_error)?.path().to_path_buf();
self.kqueue
.remove_filename(&p, EventFilter::EVFILT_VNODE)
Expand All @@ -362,9 +374,12 @@ fn map_walkdir_error(e: walkdir::Error) -> Error {
}

impl KqueueWatcher {
fn from_event_handler(event_handler: Box<dyn EventHandler>) -> Result<Self> {
fn from_event_handler(
event_handler: Box<dyn EventHandler>,
follow_symlinks: bool,
) -> Result<Self> {
let kqueue = kqueue::Watcher::new()?;
let event_loop = EventLoop::new(kqueue, event_handler)?;
let event_loop = EventLoop::new(kqueue, event_handler, follow_symlinks)?;
let channel = event_loop.event_loop_tx.clone();
let waker = event_loop.event_loop_waker.clone();
event_loop.run();
Expand Down Expand Up @@ -416,8 +431,8 @@ impl KqueueWatcher {

impl Watcher for KqueueWatcher {
/// Create a new watcher.
fn new<F: EventHandler>(event_handler: F, _config: Config) -> Result<Self> {
Self::from_event_handler(Box::new(event_handler))
fn new<F: EventHandler>(event_handler: F, config: Config) -> Result<Self> {
Self::from_event_handler(Box::new(event_handler), config.follow_symlinks())
}

fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
Expand Down
44 changes: 34 additions & 10 deletions notify/src/poll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ mod data {
&self,
root: PathBuf,
is_recursive: bool,
follow_symlinks: bool,
) -> Option<WatchData> {
WatchData::new(self, root, is_recursive)
WatchData::new(self, root, is_recursive, follow_symlinks)
}

/// Create [`PathData`].
Expand All @@ -151,6 +152,7 @@ mod data {
// config part, won't change.
root: PathBuf,
is_recursive: bool,
follow_symlinks: bool,

// current status part.
all_path_data: HashMap<PathBuf, PathData>,
Expand All @@ -162,7 +164,12 @@ mod data {
/// # Side effect
///
/// This function may send event by `data_builder.emitter`.
fn new(data_builder: &DataBuilder, root: PathBuf, is_recursive: bool) -> Option<Self> {
fn new(
data_builder: &DataBuilder,
root: PathBuf,
is_recursive: bool,
follow_symlinks: bool,
) -> Option<Self> {
// If metadata read error at `root` path, it will emit
// a error event and stop to create the whole `WatchData`.
//
Expand All @@ -186,12 +193,19 @@ mod data {
return None;
}

let all_path_data =
Self::scan_all_path_data(data_builder, root.clone(), is_recursive, true).collect();
let all_path_data = Self::scan_all_path_data(
data_builder,
root.clone(),
is_recursive,
follow_symlinks,
true,
)
.collect();

Some(Self {
root,
is_recursive,
follow_symlinks,
all_path_data,
})
}
Expand All @@ -203,9 +217,13 @@ mod data {
/// This function may emit event by `data_builder.emitter`.
pub(super) fn rescan(&mut self, data_builder: &mut DataBuilder) {
// scan current filesystem.
for (path, new_path_data) in
Self::scan_all_path_data(data_builder, self.root.clone(), self.is_recursive, false)
{
for (path, new_path_data) in Self::scan_all_path_data(
data_builder,
self.root.clone(),
self.is_recursive,
self.follow_symlinks,
false,
) {
let old_path_data = self
.all_path_data
.insert(path.clone(), new_path_data.clone());
Expand Down Expand Up @@ -247,6 +265,7 @@ mod data {
data_builder: &'_ DataBuilder,
root: PathBuf,
is_recursive: bool,
follow_symlinks: bool,
// whether this is an initial scan, used only for events
is_initial: bool,
) -> impl Iterator<Item = (PathBuf, PathData)> + '_ {
Expand All @@ -256,7 +275,7 @@ mod data {
//
// See: https://docs.rs/walkdir/2.0.1/walkdir/struct.WalkDir.html#method.new
WalkDir::new(root)
.follow_links(true)
.follow_links(follow_symlinks)
.max_depth(Self::dir_scan_depth(is_recursive))
.into_iter()
.filter_map(|entry_res| match entry_res {
Expand Down Expand Up @@ -479,6 +498,7 @@ pub struct PollWatcher {
/// currently used only for manual polling
message_channel: Sender<()>,
delay: Option<Duration>,
follow_sylinks: bool,
}

impl PollWatcher {
Expand Down Expand Up @@ -522,6 +542,7 @@ impl PollWatcher {
data_builder: Arc::new(Mutex::new(data_builder)),
want_to_stop: Arc::new(AtomicBool::new(false)),
delay: config.poll_interval(),
follow_sylinks: config.follow_symlinks(),
message_channel: tx,
};

Expand Down Expand Up @@ -581,8 +602,11 @@ impl PollWatcher {
{
data_builder.update_timestamp();

let watch_data =
data_builder.build_watch_data(path.to_path_buf(), recursive_mode.is_recursive());
let watch_data = data_builder.build_watch_data(
path.to_path_buf(),
recursive_mode.is_recursive(),
self.follow_sylinks,
);

// if create watch_data successful, add it to watching list.
if let Some(watch_data) = watch_data {
Expand Down

0 comments on commit 2692391

Please sign in to comment.