Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config option to disable following symbolic links. #635

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -522,9 +528,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 @@ -566,8 +575,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()
//
Expand Down Expand Up @@ -480,6 +499,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 @@ -523,6 +543,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 @@ -582,8 +603,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