Start work on excluding separate mount dirs
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
3e44a0adae
commit
b37b047400
3 changed files with 73 additions and 17 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2220,6 +2220,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
"cocoa",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"ctor",
|
"ctor",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
@ -2233,6 +2234,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
"objc",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"postage",
|
"postage",
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
|
|
|
@ -42,3 +42,7 @@ env_logger = "0.8"
|
||||||
serde_json = {version = "1.0.64", features = ["preserve_order"]}
|
serde_json = {version = "1.0.64", features = ["preserve_order"]}
|
||||||
tempdir = "0.3.7"
|
tempdir = "0.3.7"
|
||||||
unindent = "0.1.7"
|
unindent = "0.1.7"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
cocoa = "0.24"
|
||||||
|
objc = "0.2"
|
||||||
|
|
|
@ -354,7 +354,11 @@ impl Snapshot {
|
||||||
self.insert_ignore_file(new_parent_inode);
|
self.insert_ignore_file(new_parent_inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_parent = self.entries.get(&new_parent_inode).unwrap().clone();
|
let mut new_parent = self
|
||||||
|
.entries
|
||||||
|
.get(&new_parent_inode)
|
||||||
|
.expect(&format!("no entry for inode {}", new_parent_inode))
|
||||||
|
.clone();
|
||||||
if let Entry::Dir { children, .. } = &mut new_parent {
|
if let Entry::Dir { children, .. } = &mut new_parent {
|
||||||
*children = children
|
*children = children
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -417,7 +421,7 @@ impl Snapshot {
|
||||||
*children = new_children.into();
|
*children = new_children.into();
|
||||||
*pending = false;
|
*pending = false;
|
||||||
} else {
|
} else {
|
||||||
unreachable!("non-directory parent");
|
unreachable!("non-directory parent {}", parent_inode);
|
||||||
}
|
}
|
||||||
edits.push(Edit::Insert(parent));
|
edits.push(Edit::Insert(parent));
|
||||||
|
|
||||||
|
@ -703,16 +707,30 @@ impl<'a> sum_tree::Dimension<'a, EntrySummary> for VisibleFileCount {
|
||||||
struct BackgroundScanner {
|
struct BackgroundScanner {
|
||||||
snapshot: Arc<Mutex<Snapshot>>,
|
snapshot: Arc<Mutex<Snapshot>>,
|
||||||
notify: Sender<ScanState>,
|
notify: Sender<ScanState>,
|
||||||
|
other_mount_paths: HashSet<PathBuf>,
|
||||||
thread_pool: scoped_pool::Pool,
|
thread_pool: scoped_pool::Pool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackgroundScanner {
|
impl BackgroundScanner {
|
||||||
fn new(snapshot: Arc<Mutex<Snapshot>>, notify: Sender<ScanState>, worktree_id: usize) -> Self {
|
fn new(snapshot: Arc<Mutex<Snapshot>>, notify: Sender<ScanState>, worktree_id: usize) -> Self {
|
||||||
Self {
|
let mut scanner = Self {
|
||||||
snapshot,
|
snapshot,
|
||||||
notify,
|
notify,
|
||||||
|
other_mount_paths: Default::default(),
|
||||||
thread_pool: scoped_pool::Pool::new(16, format!("worktree-{}-scanner", worktree_id)),
|
thread_pool: scoped_pool::Pool::new(16, format!("worktree-{}-scanner", worktree_id)),
|
||||||
}
|
};
|
||||||
|
scanner.update_other_mount_paths();
|
||||||
|
scanner
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_other_mount_paths(&mut self) {
|
||||||
|
let path = self.snapshot.lock().path.clone();
|
||||||
|
self.other_mount_paths.clear();
|
||||||
|
self.other_mount_paths.extend(
|
||||||
|
mounted_volume_paths()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|mount_path| !path.starts_with(mount_path)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path(&self) -> Arc<Path> {
|
fn path(&self) -> Arc<Path> {
|
||||||
|
@ -723,7 +741,7 @@ impl BackgroundScanner {
|
||||||
self.snapshot.lock().clone()
|
self.snapshot.lock().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, event_stream: fsevent::EventStream) {
|
fn run(mut self, event_stream: fsevent::EventStream) {
|
||||||
if smol::block_on(self.notify.send(ScanState::Scanning)).is_err() {
|
if smol::block_on(self.notify.send(ScanState::Scanning)).is_err() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -792,23 +810,17 @@ impl BackgroundScanner {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
drop(tx);
|
drop(tx);
|
||||||
|
|
||||||
let mut results = Vec::new();
|
|
||||||
results.resize_with(self.thread_pool.thread_count(), || Ok(()));
|
|
||||||
self.thread_pool.scoped(|pool| {
|
self.thread_pool.scoped(|pool| {
|
||||||
for result in &mut results {
|
for _ in 0..self.thread_pool.thread_count() {
|
||||||
pool.execute(|| {
|
pool.execute(|| {
|
||||||
let result = result;
|
|
||||||
while let Ok(job) = rx.recv() {
|
while let Ok(job) = rx.recv() {
|
||||||
if let Err(err) = self.scan_dir(&job) {
|
if let Err(err) = self.scan_dir(&job) {
|
||||||
log::error!("error scanning {:?}: {}", job.path, err);
|
log::error!("error scanning {:?}: {}", job.path, err);
|
||||||
*result = Err(err);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
results.into_iter().collect::<io::Result<()>>()?;
|
|
||||||
} else {
|
} else {
|
||||||
let mut snapshot = self.snapshot.lock();
|
let mut snapshot = self.snapshot.lock();
|
||||||
snapshot.insert_entry(
|
snapshot.insert_entry(
|
||||||
|
@ -842,6 +854,11 @@ impl BackgroundScanner {
|
||||||
let child_is_symlink = child_metadata.file_type().is_symlink();
|
let child_is_symlink = child_metadata.file_type().is_symlink();
|
||||||
let child_path = job.path.join(child_name.as_ref());
|
let child_path = job.path.join(child_name.as_ref());
|
||||||
|
|
||||||
|
// Disallow mount points outside the file system containing the root of this worktree
|
||||||
|
if self.other_mount_paths.contains(&child_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if child_metadata.is_dir() {
|
if child_metadata.is_dir() {
|
||||||
new_entries.push((
|
new_entries.push((
|
||||||
child_name,
|
child_name,
|
||||||
|
@ -874,7 +891,6 @@ impl BackgroundScanner {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(&job.path);
|
|
||||||
self.snapshot.lock().populate_dir(job.inode, new_entries);
|
self.snapshot.lock().populate_dir(job.inode, new_entries);
|
||||||
for new_job in new_jobs {
|
for new_job in new_jobs {
|
||||||
job.scan_queue.send(new_job).unwrap();
|
job.scan_queue.send(new_job).unwrap();
|
||||||
|
@ -883,7 +899,9 @@ impl BackgroundScanner {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_events(&self, mut events: Vec<fsevent::Event>) -> bool {
|
fn process_events(&mut self, mut events: Vec<fsevent::Event>) -> bool {
|
||||||
|
self.update_other_mount_paths();
|
||||||
|
|
||||||
let mut snapshot = self.snapshot();
|
let mut snapshot = self.snapshot();
|
||||||
snapshot.scan_id += 1;
|
snapshot.scan_id += 1;
|
||||||
|
|
||||||
|
@ -1210,6 +1228,34 @@ impl<'a> Iterator for FileIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mounted_volume_paths() -> Vec<PathBuf> {
|
||||||
|
use cocoa::{
|
||||||
|
base::{id, nil},
|
||||||
|
foundation::{NSArray, NSString, NSURL},
|
||||||
|
};
|
||||||
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let manager: id = msg_send![class!(NSFileManager), defaultManager];
|
||||||
|
let array = NSArray::array(nil);
|
||||||
|
let urls: id =
|
||||||
|
msg_send![manager, mountedVolumeURLsIncludingResourceValuesForKeys:array options:0];
|
||||||
|
let len = urls.count() as usize;
|
||||||
|
let mut result = Vec::with_capacity(len);
|
||||||
|
for i in 0..len {
|
||||||
|
let url = urls.objectAtIndex(i as u64);
|
||||||
|
let string = url.absoluteString();
|
||||||
|
let string = std::ffi::CStr::from_ptr(string.UTF8String())
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
if let Some(path) = string.strip_prefix("file://") {
|
||||||
|
result.push(PathBuf::from(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1273,8 +1319,6 @@ mod tests {
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
eprintln!("HI");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1383,6 +1427,12 @@ mod tests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mounted_volume_paths() {
|
||||||
|
let paths = mounted_volume_paths();
|
||||||
|
assert!(paths.contains(&"/".into()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_random() {
|
fn test_random() {
|
||||||
let iterations = env::var("ITERATIONS")
|
let iterations = env::var("ITERATIONS")
|
||||||
|
@ -1411,7 +1461,7 @@ mod tests {
|
||||||
log::info!("Generated initial tree");
|
log::info!("Generated initial tree");
|
||||||
|
|
||||||
let (notify_tx, _notify_rx) = smol::channel::unbounded();
|
let (notify_tx, _notify_rx) = smol::channel::unbounded();
|
||||||
let scanner = BackgroundScanner::new(
|
let mut scanner = BackgroundScanner::new(
|
||||||
Arc::new(Mutex::new(Snapshot {
|
Arc::new(Mutex::new(Snapshot {
|
||||||
id: 0,
|
id: 0,
|
||||||
scan_id: 0,
|
scan_id: 0,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue