Skip to content
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
4 changes: 1 addition & 3 deletions src/install/PackageInstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,6 @@ impl<'a> PackageInstall<'a> {
Ok(w) => w,
Err(err) => return Ok(InstallResult::fail(err.into(), Step::OpeningCacheDir, None)),
};
walker_.resolve_unknown_entry_types = true;

fn copy(destination_dir_: &Dir, walker: &mut Walker) -> crate::Result<()> {
let mut stackpath = [0u8; path::MAX_PATH_BYTES];
Expand Down Expand Up @@ -1181,12 +1180,11 @@ impl<'a> PackageInstall<'a> {
&[]
};

let mut walker = bun_core::handle_oom(walker_skippable::walk_owned(
let walker = bun_core::handle_oom(walker_skippable::walk_owned(
cached_package_dir,
&[] as &[&OSPathSlice],
skip_dirs,
));
walker.resolve_unknown_entry_types = true;

#[cfg(not(windows))]
{
Expand Down
1 change: 1 addition & 0 deletions src/install/PackageManager/PackageManagerResolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ impl PackageManager {
Err(e) => return Err(e),
};
let mut iter = bun_sys::iterate_dir(dir.fd);
iter.resolve_unknown_entry_types = true;

loop {
let entry = match iter.next() {
Expand Down
1 change: 1 addition & 0 deletions src/install/PackageManager/updatePackageJSONAndInstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ pub(super) fn remove_leftover_node_modules(
match bun_sys::open_dir_for_iteration(cwd.fd(), manager.options.bin_path.as_bytes()) {
Ok(node_modules_bin) => {
let mut iter = bun_sys::iterate_dir(node_modules_bin);
iter.resolve_unknown_entry_types = true;
'iterator: loop {
let Ok(Some(entry)) = iter.next() else { break };
match entry.kind {
Expand Down
4 changes: 3 additions & 1 deletion src/install/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ fn normalized_bin_name(name: &[u8]) -> &[u8] {
/// verbatim from package.json, so without this check a malicious package could
/// point a bin link at (and chmod) an arbitrary file on disk (the bug class
/// npm fixed as CVE-2019-16775).
pub(crate) fn bin_target_escapes_package_dir(target: &[u8]) -> bool {
pub fn bin_target_escapes_package_dir(target: &[u8]) -> bool {
if path::is_absolute(target) {
return true;
}
Expand Down Expand Up @@ -1796,6 +1796,7 @@ impl<'a> Linker<'a> {
let abs_dest_dir_end = dest_off;

let mut iter = sys::iterate_dir(target_dir);
iter.resolve_unknown_entry_types = true;
while let Some(entry) = iter.next().unwrap_or(None) {
match entry.kind {
sys::EntryKind::SymLink | sys::EntryKind::File => {
Expand Down Expand Up @@ -1953,6 +1954,7 @@ impl<'a> Linker<'a> {
let abs_dest_dir_end = dest_off;

let mut iter = sys::iterate_dir(target_dir);
iter.resolve_unknown_entry_types = true;
while let Some(entry) = iter.next().unwrap_or(None) {
match entry.kind {
sys::EntryKind::SymLink | sys::EntryKind::File => {
Expand Down
11 changes: 1 addition & 10 deletions src/install/isolated_install/FileCopier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,7 @@ impl FileCopier {
Ok(FileCopier {
src_path,
dest_subpath,
walker: {
let mut w = walker_skippable::walk(
src_dir,
// bun.default_allocator → deleted (global mimalloc)
&[],
skip_dirnames,
)?;
w.resolve_unknown_entry_types = true;
w
},
walker: walker_skippable::walk(src_dir, &[], skip_dirnames)?,
})
}

Expand Down
11 changes: 1 addition & 10 deletions src/install/isolated_install/Hardlinker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,7 @@ impl Hardlinker {
Ok(Hardlinker {
src,
dest,
walker: {
let mut w = bun_sys::walker_skippable::walk(
folder_dir,
// bun.default_allocator dropped — global mimalloc
&[],
skip_dirnames,
)?;
w.resolve_unknown_entry_types = true;
w
},
walker: bun_sys::walker_skippable::walk(folder_dir, &[], skip_dirnames)?,
})
}

Expand Down
16 changes: 5 additions & 11 deletions src/install/prune.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1343,24 +1343,17 @@ fn lstat_kind(dir: &Dir, name: &[u8]) -> EntryKind {
}
}

fn entry_kind(dir: &Dir, name: &[u8], kind: EntryKind) -> EntryKind {
if kind != EntryKind::Unknown {
return kind;
}
lstat_kind(dir, name)
}

fn read_entries(dir: &Dir) -> Vec<(Box<[u8]>, EntryKind)> {
let mut out = Vec::new();
let mut iter = sys::iterate_dir(dir.fd());
iter.resolve_unknown_entry_types = true;
while let Ok(Some(entry)) = iter.next() {
let name = entry.name.slice_u8();
if name.first() == Some(&b'.') {
continue;
}
let kind = entry_kind(dir, name, entry.kind);
if kind == EntryKind::Directory || kind == EntryKind::SymLink {
out.push((name.into(), kind));
if entry.kind == EntryKind::Directory || entry.kind == EntryKind::SymLink {
out.push((name.into(), entry.kind));
}
}
out
Expand Down Expand Up @@ -1780,9 +1773,10 @@ fn prune_bins(dir: &Dir) {
};
let mut dangling: Vec<Box<[u8]>> = Vec::new();
let mut iter = sys::iterate_dir(bin.fd());
iter.resolve_unknown_entry_types = true;
while let Ok(Some(entry)) = iter.next() {
let name = entry.name.slice_u8();
if entry_kind(&bin, name, entry.kind) == EntryKind::SymLink && is_dangling(&bin, name) {
if entry.kind == EntryKind::SymLink && is_dangling(&bin, name) {
dangling.push(name.into());
}
}
Expand Down
1 change: 0 additions & 1 deletion src/runtime/cli/build_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1355,7 +1355,6 @@ pub(crate) fn collect_compile_assets(
Ok(w) => w,
Err(_) => bun_core::out_of_memory(),
};
walker.resolve_unknown_entry_types = true;
loop {
let entry = match walker.next() {
Ok(Some(e)) => e,
Expand Down
15 changes: 13 additions & 2 deletions src/runtime/cli/bunx_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,11 +335,22 @@ impl BunxCommand {

if let Some(dirs) = expr.as_property(b"directories") {
if let Some(bin_prop) = dirs.expr.as_property(b"bin") {
if let Some(dir_name) = bin_prop.expr.as_utf8_string_literal() {
let bin_dir = bun_sys::openat_a(dir_fd, dir_name, O::RDONLY | O::DIRECTORY, 0)?;
// Same values the bin linker refuses to link from (`bin.rs`, `Tag::Dir`).
if let Some(dir_name) = bin_prop.expr.as_utf8_string_literal().filter(|dir| {
!dir.is_empty() && !bun_install::bin::bin_target_escapes_package_dir(dir)
}) {
// `directories.bin` is relative to the package, not to `dir_fd`.
use bun_paths::platform::Auto;
let package_dir =
bun_paths::resolve_path::dirname::<Auto>(subpath_z.as_bytes());
let bin_dir_path =
bun_paths::resolve_path::join_z::<Auto>(&[package_dir, dir_name]);
let bin_dir =
bun_sys::openat(dir_fd, bin_dir_path, O::RDONLY | O::DIRECTORY, 0)?;
// Fd is non-owning Copy; guard it.
let _close_bin_dir = bun_sys::CloseOnDrop::new(bin_dir);
let mut iterator = bun_sys::dir_iterator::iterate(bin_dir);
iterator.resolve_unknown_entry_types = true;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
let mut entry = iterator.next();
loop {
let current = match entry {
Expand Down
1 change: 1 addition & 0 deletions src/runtime/cli/create_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,7 @@ impl Example {
for folder in &folders {
if folder.fd() != bun_sys::Fd::invalid() {
let mut iter = bun_sys::dir_iterator::iterate(folder.fd());
iter.resolve_unknown_entry_types = true;

'loop_: while let Some(entry) = iter.next().ok().flatten() {
let entry_name = entry.name.slice_u8();
Expand Down
1 change: 1 addition & 0 deletions src/runtime/cli/init_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ impl InitCommand {
let _ = bun_sys::close(d);
});
let mut it = bun_sys::iterate_dir(dir);
it.resolve_unknown_entry_types = true;
while let Some(file) = it.next().map_err(crate::Error::from)? {
if file.kind != bun_sys::FileKind::File {
continue;
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/cli/pack_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ fn iterate_included_project_tree(
});

let mut dir_iter = DirIterator::iterate(Fd::from_std_dir(&dir));
dir_iter.resolve_unknown_entry_types = true;
'next_entry: while let Some(entry) = dir_iter.next().ok().flatten() {
// On iterator error, treat as end of iteration.
if entry.kind != bun_sys::FileKind::File && entry.kind != bun_sys::FileKind::Directory {
Expand Down Expand Up @@ -713,6 +714,7 @@ fn add_entire_tree(
}

let mut iter = DirIterator::iterate(Fd::from_std_dir(&dir));
iter.resolve_unknown_entry_types = true;
'next_entry: while let Some(entry) = iter.next().ok().flatten() {
if entry.kind != bun_sys::FileKind::File && entry.kind != bun_sys::FileKind::Directory {
continue;
Expand Down Expand Up @@ -884,6 +886,7 @@ fn iterate_bundled_deps(
let mut additional_bundled_deps: Vec<DirInfo> = Vec::new();

let mut iter = DirIterator::iterate(Fd::from_std_dir(&dir));
iter.resolve_unknown_entry_types = true;
while let Some(entry) = iter.next().ok().flatten() {
if entry.kind != bun_sys::FileKind::Directory {
continue;
Expand Down Expand Up @@ -1022,6 +1025,7 @@ fn add_bundled_dep(
let DirInfo(dir, dir_subpath, dir_depth) = dir_info;

let mut iter = DirIterator::iterate(Fd::from_std_dir(&dir));
iter.resolve_unknown_entry_types = true;
while let Some(entry) = iter.next().ok().flatten() {
if entry.kind != bun_sys::FileKind::File && entry.kind != bun_sys::FileKind::Directory {
continue;
Expand Down Expand Up @@ -1284,6 +1288,7 @@ fn iterate_project_tree(
}

let mut dir_iter = DirIterator::iterate(Fd::from_std_dir(&dir));
dir_iter.resolve_unknown_entry_types = true;
'next_entry: while let Some(entry) = dir_iter.next().ok().flatten() {
if entry.kind != bun_sys::FileKind::File && entry.kind != bun_sys::FileKind::Directory {
continue;
Expand Down
1 change: 1 addition & 0 deletions src/runtime/cli/pm_licenses_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ fn list_dir(path: &[u8]) -> Vec<(Box<[u8]>, FileKind)> {
return out;
};
let mut iter = bun_sys::iterate_dir(dir.fd());
iter.resolve_unknown_entry_types = true;
while let Ok(Some(entry)) = iter.next() {
out.push((entry.name.slice_u8().into(), entry.kind));
}
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/cli/publish_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,7 @@ impl PublishCommand {
});

let mut iter = DirIterator::iterate(workspace_dir);
iter.resolve_unknown_entry_types = true;
while let Some(entry) = iter.next().ok().flatten() {
if entry.kind == bun_sys::EntryKind::Directory {
continue;
Expand Down Expand Up @@ -1811,6 +1812,7 @@ impl PublishCommand {
});

let mut iter = DirIterator::iterate(dir);
iter.resolve_unknown_entry_types = true;
while let Some(entry) = iter.next().ok().flatten() {
let (name, subpath): (&'static ZStr, &'static ZStr) = {
// Entry names are UTF-8 on every platform.
Expand Down
40 changes: 30 additions & 10 deletions src/runtime/node/dir_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,30 @@ pub struct IteratorResult {
/// `RawSlice` invariant: borrows the iterator's `getdents` buffer
/// (streaming-iterator contract — invalidated on next `next()` call).
/// The kernel writes `d_name` NUL-terminated, so the backing has a NUL at
/// `[name.len()]` (see `name_assume_z`).
/// `[name.len()]` (see `resolve_unknown_kind`).
pub name: RawSlice<u8>,
pub(crate) kind: EntryKind,
}

impl IteratorResult {
/// The entry name as a NUL-terminated `&ZStr` — the POSIX `d_name` is always
/// NUL-terminated in the `getdents` buffer.
#[inline]
pub(crate) fn name_assume_z(&self) -> &bun_core::ZStr {
/// See `NewWrappedIterator::resolve_unknown_entry_types`.
#[cfg(not(windows))]
fn resolve_unknown_kind(&mut self, dir: Fd) {
if self.kind != EntryKind::Unknown {
return;
}
let s = self.name.slice();
// SAFETY: `d_name` is NUL-terminated by the kernel; `name` points at it
// with len excluding the NUL, so `[len] == 0`.
unsafe { bun_core::ZStr::from_raw(s.as_ptr(), s.len()) }
let name = unsafe { bun_core::ZStr::from_raw(s.as_ptr(), s.len()) };
if let Ok(st) = sys::lstatat(dir, name) {
self.kind = sys::kind_from_mode(st.st_mode as sys::Mode);
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/// The Windows iterator always knows the kind.
#[cfg(windows)]
fn resolve_unknown_kind(&mut self, _dir: Fd) {}
}
pub type Result = sys::Result<Option<IteratorResult>>;

Expand Down Expand Up @@ -412,9 +421,7 @@ mod platform {
libc::DT_LNK => EntryKind::SymLink,
libc::DT_REG => EntryKind::File,
libc::DT_SOCK => EntryKind::UnixDomainSocket,
// DT_UNKNOWN: Some filesystems (e.g., bind mounts, FUSE, NFS)
// don't provide d_type. Callers should use lstatat() to determine
// the type when needed (lazy stat pattern for performance).
// DT_UNKNOWN: see `NewWrappedIterator::resolve_unknown_entry_types`.
_ => EntryKind::Unknown,
};
return Ok(Some(IteratorResult {
Expand Down Expand Up @@ -858,12 +865,20 @@ where
(): WrappedSelect<IS_U16>,
{
pub(crate) iter: NewIterator<IS_U16>,
/// As `bun_sys::dir_iterator::WrappedIterator::resolve_unknown_entry_types`; ignored by the `IS_U16` (Windows) iterator.
pub(crate) resolve_unknown_entry_types: bool,
}

impl NewWrappedIterator<false> {
#[inline]
pub(crate) fn next(&mut self) -> Result {
self.iter.next()
let mut entry = self.iter.next()?;
if self.resolve_unknown_entry_types {
if let Some(entry) = entry.as_mut() {
entry.resolve_unknown_kind(self.iter.dir);
}
}
Ok(entry)
}
}

Expand Down Expand Up @@ -892,6 +907,7 @@ where
buf: platform::DirentBuf([0u8; 8192]),
received_eof: false,
},
resolve_unknown_entry_types: false,
};
}
#[cfg(any(target_os = "linux", target_os = "android"))]
Expand All @@ -904,6 +920,7 @@ where
// zero-init avoids the invalid_value lint on [u8; N]
buf: platform::DirentBuf([0u8; 8192]),
},
resolve_unknown_entry_types: false,
};
}
#[cfg(target_os = "freebsd")]
Expand All @@ -916,6 +933,7 @@ where
// zero-init avoids the invalid_value lint on [u8; N]
buf: platform::DirentBuf([0u8; 8192]),
},
resolve_unknown_entry_types: false,
};
}
#[cfg(windows)]
Expand All @@ -932,6 +950,7 @@ where
name_data: unsafe { bun_core::ffi::zeroed_unchecked() },
name_filter: None,
},
resolve_unknown_entry_types: false,
};
}
#[cfg(target_os = "wasi")]
Expand All @@ -945,6 +964,7 @@ where
// zero-init avoids the invalid_value lint on [u8; N]
buf: [0u8; 8192],
},
resolve_unknown_entry_types: false,
};
}
}
Expand Down
Loading
Loading