Skip to content
Draft
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
10 changes: 10 additions & 0 deletions src/resolver/dir_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ impl Default for DirInfo {
}
}

/// What `Resolver::real_path_of` found for an entry of a directory.
pub enum RealPath {
/// No symlink is on the path.
Same,
/// The real path: the entry is a symlink, or the resolver cached the joined path on it.
Whole(&'static [u8]),
/// Only a parent directory is a symlink: the real path is this directory plus the entry's base.
Dir(&'static [u8]),
}

/// Dereference an arena-interned `NonNull<T>` to `&'static T`.
///
/// Single deref site for the three `Option<NonNull<_>>` read accessors on
Expand Down
12 changes: 1 addition & 11 deletions src/resolver/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ pub struct Entry {
pub mutex: Mutex,
pub need_stat: AtomicBool,

/// The lexical path, in the spelling of the first lookup that filled it. `Resolver::real_path_of` gives the real one.
pub abs_path: Interned,
}

Expand Down Expand Up @@ -183,17 +184,6 @@ impl Entry {
self.dir
}

/// `Interned` is `Copy`.
#[inline]
pub fn abs_path(&self) -> Interned {
self.abs_path
}

#[inline]
pub fn set_abs_path(&mut self, p: Interned) {
self.abs_path = p;
}

/// Stat-on-first-use.
///
/// # Safety
Expand Down
1 change: 1 addition & 0 deletions src/resolver/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub use data_url::DataURL;
/// Re-export real `DirInfo`.
pub use dir_info::DirInfo;
pub use dir_info::DirInfoRef;
pub use dir_info::RealPath;
/// Re-export real filesystem `Path`.
pub use fs::Path;
/// Re-export real `PackageJSON`.
Expand Down
36 changes: 23 additions & 13 deletions src/resolver/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ use crate::tsconfig_json::TSConfigJSON;
pub use crate::data_url::DataURL;
pub use crate::dir_info as DirInfo;
pub use crate::dir_info::DirInfoRef;
use crate::dir_info::RealPath;
pub use ::bun_options_types::global_cache::GlobalCache;

// Sibling resolver modules. They retain the same item names so cross-references
Expand Down Expand Up @@ -1574,6 +1575,19 @@ impl<'a> Resolver<'a> {
self.resolve(source_dir, import_path, kind)
}

/// The real path of `entry`, which `dir` lists. The one rule, also for the route scan of `Bun.FileSystemRouter`.
pub fn real_path_of(&self, dir: &DirInfo::DirInfo, entry: &Fs::file_system::Entry) -> RealPath {
// SAFETY: `rfs_ptr` is the process-global RealFS. `symlink` locks the entry to stat it.
let symlink = unsafe { entry.symlink(self.rfs_ptr(), self.store_fd) };
if !symlink.is_empty() {
RealPath::Whole(symlink)
} else if !dir.abs_real_path.is_empty() {
RealPath::Dir(dir.abs_real_path)
} else {
RealPath::Same
}
}

pub(crate) fn finalize_result(
&mut self,
result: &mut Result,
Expand Down Expand Up @@ -1648,10 +1662,8 @@ impl<'a> Resolver<'a> {
// map in place under that lock. The entry pointer stays valid after
// unlock (EntryStore-owned).
if let Some(query) = dir.get_entry(self.generation, name.filename) {
// SAFETY: rfs points at the process-global RealFS; the lazy-stat
// rewrite inside `symlink()` is serialized on the per-entry mutex.
let symlink_path = unsafe { query.entry().symlink(self.rfs_ptr(), self.store_fd) };
if !symlink_path.is_empty() {
let real_path = self.real_path_of(&dir, query.entry());
if let RealPath::Whole(symlink_path) = real_path {
path.set_realpath(symlink_path);
if !result.file_fd.is_valid() {
result.file_fd = query.entry().cache().fd;
Expand All @@ -1664,9 +1676,9 @@ impl<'a> Resolver<'a> {
bstr::BStr::new(symlink_path)
));
}
} else if !dir.abs_real_path.is_empty() {
} else if let RealPath::Dir(abs_real_path) = real_path {
// When the directory is a symlink, we don't need to call getFdPath.
let parts = [dir.abs_real_path, query.entry().base()];
let parts = [abs_real_path, query.entry().base()];
let mut buf = bun_paths::path_buffer_pool::get();

// NOTE: `abs_buf` returns a borrow of `buf`; capture only the
Expand Down Expand Up @@ -6327,10 +6339,8 @@ impl<'a> Resolver<'a> {
// dies (NLL) before any later `&mut` to this slot.
let entry = lookup.entry();

// SAFETY: `rfs_ptr` points at the process-global RealFS; the lazy-stat
// rewrite inside `symlink()` is serialized on `Entry.mutex`.
let mut symlink = unsafe { entry.symlink(rfs_ptr, self.store_fd) };
if !symlink.is_empty() {
let real_path = self.real_path_of(&parent_, entry);
if let RealPath::Whole(symlink) = real_path {
if let Some(logs) = self.debug_logs.as_mut() {
let mut buf = Vec::new();
let _ = write!(
Expand All @@ -6342,15 +6352,15 @@ impl<'a> Resolver<'a> {
logs.add_note(buf);
}
info.abs_real_path = symlink;
} else if !parent_.abs_real_path.is_empty() {
} else if let RealPath::Dir(parent_real_path) = real_path {
// this might leak a little i'm not sure
let parts = [parent_.abs_real_path, base];
let parts = [parent_real_path, base];
// NOTE: split into two statements so the two `&mut FileSystem`
// borrows from `unsafe { &mut *self.fs() }` don't overlap (Stacked Borrows).
let joined = self
.fs_ref()
.abs_buf(&parts, bufs!(dir_info_uncached_filename));
symlink = self
let symlink = self
.fs_ref()
.dirname_store
.append_slice(joined)
Expand Down
Loading
Loading