Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
7 changes: 3 additions & 4 deletions src/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Key types and functions:
- `Fd` (`bun_core::Fd`, re-exported) — cross-platform file descriptor. `Fd::cwd()`, `Fd::stdin()/stdout()/stderr()`, `fd.close()`.
- `File::open(path: &ZStr, flags, mode)` / `File::openat(dir: Fd, path: &[u8], flags, mode)` / `File::make_open(...)` (creates parent dirs) / `File::create(dir, path, truncate)`
- `file.read(buf)` / `read_all(buf)` / `read_to_end()` / `read_to_end_small()` / `write(buf)` / `write_all(buf)`
- `File::read_from(dir, path)` and `File::open_regular_at(dir, path)` accept regular files only (a FIFO would block the open, `/dev/zero` has no end). A file bun reads whole because it found it (a config file, a lockfile, a cache entry) goes through them. `File::read_from_any_file_type` is for a path the user named that may be a device, such as `--config=/dev/null`.
- `bun_sys::open`, `read`, `write`, `pread`, `pwrite`, `stat`, `fstat`, `lstat`, `mkdir`, `unlink`, `rename`, `symlink`, `chmod` — free fns over `Fd`
- Open flags: `bun_sys::O::RDONLY`, `O::WRONLY | O::CREAT | O::TRUNC`, etc.

Expand Down Expand Up @@ -327,10 +328,8 @@ canonical example of this bug class and its fix.
## Common Patterns

```rust
// Read a file, return JS error on failure
let contents = match bun_sys::File::openat(Fd::cwd(), path, O::RDONLY, 0)
.and_then(|f| f.read_to_end())
{
// Read a file (regular files only, see bun_sys above), return JS error on failure
let contents = match bun_sys::File::read_from(Fd::cwd(), path) {
Ok(bytes) => bytes,
Err(err) => return Ok(err.to_js(global)?),
};
Expand Down
8 changes: 7 additions & 1 deletion src/ast/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2796,6 +2796,8 @@ fn range_data_text(source: Option<&Source>, r: Range, text: Cow<'static, [u8]>)
#[derive(Default, Clone, Copy)]
pub struct ToSourceOptions {
pub convert_bom: bool,
/// Read a device too ([`bun_sys::File::read_from_any_file_type`]): only for a path the user named.
pub any_file_type: bool,
}

/// Read `path` (rooted at cwd) into memory and wrap it in a `Source`.
Expand All @@ -2813,7 +2815,11 @@ fn source_from_file_at(
path: &bun_core::ZStr,
opts: ToSourceOptions,
) -> bun_sys::Maybe<Source> {
let mut bytes = bun_sys::file::File::read_from(dir_fd, path)?;
let mut bytes = if opts.any_file_type {
bun_sys::file::File::read_from_any_file_type(dir_fd, path)?
} else {
bun_sys::file::File::read_from(dir_fd, path)?
};
if opts.convert_bom {
if let Some(bom) = bun_core::strings::BOM::detect(&bytes) {
bytes = bom.remove_and_convert_to_utf8_and_free(bytes);
Expand Down
32 changes: 18 additions & 14 deletions src/bunfig/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,25 @@ fn load_bunfig(
config_path: &ZStr,
ctx: Context<'_>,
) -> Result<(), crate::Error> {
let source =
match bun_ast::to_source(config_path, bun_ast::ToSourceOptions { convert_bom: true }) {
Ok(s) => s,
Err(err) => {
if auto_loaded {
return Ok(());
}
bun_core::pretty_errorln!(
"{}\nwhile reading config \"{}\"",
err,
BStr::new(config_path.as_bytes()),
);
Global::exit(1);
let options = bun_ast::ToSourceOptions {
convert_bom: true,
// `--config=/dev/null` is a way to run without a config.
any_file_type: !auto_loaded,
};
let source = match bun_ast::to_source(config_path, options) {
Ok(s) => s,
Err(err) => {
if auto_loaded {
return Ok(());
}
};
bun_core::pretty_errorln!(
"{}\nwhile reading config \"{}\"",
err,
BStr::new(config_path.as_bytes()),
);
Global::exit(1);
}
};

bun_ast::stmt::data::Store::create();
bun_ast::expr::data::Store::create();
Expand Down
47 changes: 23 additions & 24 deletions src/dotenv/env_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,33 +798,32 @@ impl Loader {

// `bun_sys` is errno-based; the match arms below group the recoverable
// errnos. Any errno not listed propagates.
let file =
match bun_sys::File::openat(dir, base, bun_sys::O::RDONLY | bun_sys::O::CLOEXEC, 0) {
Ok(file) => file,
Err(err) => {
use bun_sys::E;
match err.get_errno() {
E::EISDIR | E::ENOENT => {
// prevent retrying
self.default_files_loaded.insert(env_file);
return Ok(());
}
E::EBUSY | E::EACCES => {
if !self.quiet {
bun_core::pretty_errorln!(
"<r><red>{}<r> error loading {} file",
bstr::BStr::new(err.name()),
bstr::BStr::new(base)
);
}
// prevent retrying
self.default_files_loaded.insert(env_file);
return Ok(());
let file = match bun_sys::File::open_regular_at(dir, base) {
Ok((file, _)) => file,
Err(err) => {
use bun_sys::E;
match err.get_errno() {
E::EISDIR | E::ENODEV | E::ENOENT => {
// prevent retrying
self.default_files_loaded.insert(env_file);
return Ok(());
}
E::EBUSY | E::EACCES => {
if !self.quiet {
bun_core::pretty_errorln!(
"<r><red>{}<r> error loading {} file",
bstr::BStr::new(err.name()),
bstr::BStr::new(base)
);
}
_ => return Err(err.into()),
// prevent retrying
self.default_files_loaded.insert(env_file);
return Ok(());
}
_ => return Err(err.into()),
}
};
}
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

match read_env_file_contents(&file)? {
ReadEnvFile::Empty => {}
Expand Down
5 changes: 4 additions & 1 deletion src/ini/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1261,7 +1261,10 @@ mod draft {
for &npmrc_path in npmrc_paths {
let source = match bun_ast::source_from_file(
npmrc_path,
bun_ast::ToSourceOptions { convert_bom: true },
bun_ast::ToSourceOptions {
convert_bom: true,
..Default::default()
},
) {
Ok(s) => s,
Err(err) => {
Expand Down
11 changes: 6 additions & 5 deletions src/install/PackageInstaller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,11 @@ impl NodeModulesFolder {
) -> bun_sys::Result<bun_sys::File> {
let mut path_buf = PathBuffer::uninit();
let parts: [&[u8]; 2] = [self.path.as_slice(), file_path.as_bytes()];
root_node_modules_dir.open_file(
bun_sys::File::open_regular_at(
root_node_modules_dir,
join_z_buf::<platform::Auto>(path_buf.as_mut_slice(), &parts),
bun_sys::O::RDONLY,
0,
)
.map(|(file, _)| file)
}

pub(crate) fn read_small_file(
Expand Down Expand Up @@ -208,8 +208,9 @@ impl NodeModulesFolder {
}

let dir = self.open_dir(root_node_modules_dir)?;
let res = dir.open_file(file_path, bun_sys::O::RDONLY, 0);
res.map_err(|e| e.to_zig_err().into())
bun_sys::File::open_regular_at(&dir, file_path)
.map(|(file, _)| file)
.map_err(|e| e.to_zig_err().into())
}

pub(crate) fn open_dir(&self, root: &Dir) -> crate::Result<Dir> {
Expand Down
7 changes: 6 additions & 1 deletion src/install/PackageManager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1587,14 +1587,19 @@ pub fn init(
this_cwd.len() + b"/package.json".len(),
);

// Only locates the project: like its readers (`File::open_regular_at`), never block on a FIFO.
#[cfg(unix)]
let flags = bun_sys::O::CLOEXEC | bun_sys::O::NONBLOCK;
#[cfg(not(unix))]
let flags = bun_sys::O::CLOEXEC;
match bun_sys::File::openat(
bun_sys::Fd::cwd(),
package_json_path.as_bytes(),
if need_write {
bun_sys::O::RDWR
} else {
bun_sys::O::RDONLY
} | bun_sys::O::CLOEXEC,
} | flags,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
0,
) {
Ok(f) => break 'child f,
Expand Down
24 changes: 8 additions & 16 deletions src/install/lockfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,9 @@ impl Lockfile {
debug_assert!(Fs::INSTANCE_LOADED.load(core::sync::atomic::Ordering::Relaxed));

let mut lockfile_format = LockfileFormat::Text;
let file: File = 'file: {
match File::openat(dir, zstr!("bun.lock"), sys::O::RDONLY, 0) {
sys::Result::Ok(f) => break 'file f,
let (file, size): (File, u64) = 'file: {
match File::open_regular_at(dir, zstr!("bun.lock")) {
sys::Result::Ok(opened) => break 'file opened,
sys::Result::Err(text_open_err) => {
if text_open_err.errno != sys::SystemErrno::ENOENT as u16 {
return LoadResult::Err(LoadResultErr {
Expand All @@ -521,8 +521,8 @@ impl Lockfile {

lockfile_format = LockfileFormat::Binary;

match File::openat(dir, zstr!("bun.lockb"), sys::O::RDONLY, 0) {
sys::Result::Ok(f) => break 'file f,
match File::open_regular_at(dir, zstr!("bun.lockb")) {
sys::Result::Ok(opened) => break 'file opened,
sys::Result::Err(binary_open_err) => {
if binary_open_err.errno != sys::SystemErrno::ENOENT as u16 {
return LoadResult::Err(LoadResultErr {
Expand All @@ -549,9 +549,7 @@ impl Lockfile {
}
};

// `bun_sys::File::read_to_end` returns `Maybe<Vec<u8>>`
// (fstat-presized, pread-from-0); map the error arm to `.read_file`.
let buf = match file.read_to_end() {
let buf = match file.read_to_end_sized(size) {
Ok(bytes) => bytes,
Err(e) => {
return LoadResult::Err(LoadResultErr {
Expand Down Expand Up @@ -1833,14 +1831,8 @@ impl Lockfile {
}
break 'bytes bytes;
};
if File::openat(
Fd::cwd(),
save_format.filename().as_bytes(),
sys::O::RDONLY,
0,
)
.and_then(|existing| existing.read_to_end())
.is_ok_and(|existing| existing == bytes)
if File::read_from(Fd::cwd(), save_format.filename())
.is_ok_and(|existing| existing == bytes)
{
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions src/install/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use bun_core::{Output, zstr};
use bun_paths::PathBuffer;
use bun_semver::query::token::Wildcard;
use bun_semver::{self as Semver, SlicedString};
use bun_sys::{self, Fd, File, O};
use bun_sys::{self, Fd, File};

use crate::install::{self as Install, PackageManager, Subcommand};
use crate::lockfile::{
Expand Down Expand Up @@ -38,7 +38,7 @@ pub fn detect_and_load_other_lockfile<'a>(

'npm: {
let timer = std::time::Instant::now();
let Ok(lockfile) = File::openat(dir, b"package-lock.json", O::RDONLY, 0) else {
let Ok((lockfile, size)) = File::open_regular_at(dir, b"package-lock.json") else {
break 'npm;
};
// file closes on Drop
Expand All @@ -48,7 +48,7 @@ pub fn detect_and_load_other_lockfile<'a>(
break 'npm;
};
let lockfile_path: &[u8] = &*lockfile_path;
let Ok(data) = lockfile.read_to_end() else {
let Ok(data) = lockfile.read_to_end_sized(size) else {
break 'npm;
};
let migrate_result =
Expand Down
8 changes: 2 additions & 6 deletions src/install/migration/npm_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use bun_install_types::DependencyGroup;
use bun_paths::resolve_path;
use bun_semver::query::token::Wildcard;
use bun_semver::{self as Semver, SlicedString, String as SemverString};
use bun_sys::{Fd, File, O};
use bun_sys::{Fd, File};

use super::{package_name_from_path, pkg_flag_is_true, string_hash};
use crate::Error;
Expand Down Expand Up @@ -1017,13 +1017,9 @@ pub(super) fn apply_root_overrides(
workspace_map: Option<&WorkspaceMap>,
abs_lockfile_path: &[u8],
) -> Result<(), Error> {
let Ok(file) = File::openat(dir, b"package.json", O::RDONLY, 0) else {
let Ok(contents) = File::read_from(dir, b"package.json") else {
return Ok(());
};
let Ok(contents) = file.read_to_end() else {
return Ok(());
};
drop(file);

let mut package_json_path_buf = bun_paths::path_buffer_pool::get();
let package_json_path = resolve_path::join_string_buf::<bun_paths::platform::Auto>(
Expand Down
3 changes: 2 additions & 1 deletion src/install/npm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1384,7 +1384,8 @@ pub mod package_manifest {
) -> Result<Option<PackageManifest>, Error> {
let mut file_path_buf = [0u8; 512 + 64];
let file_name = Self::manifest_file_name(&mut file_path_buf, file_id, scope)?;
let Ok(cache_file) = File::openat(cache_dir, file_name, bun_sys::O::RDONLY, 0) else {
// A non-regular entry counts as missing too: the save after the fetch replaces it.
let Ok((cache_file, _)) = File::open_regular_at(cache_dir, file_name) else {
return Ok(None);
};

Expand Down
5 changes: 4 additions & 1 deletion src/install/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ impl SloppyGlobalGitConfig {
// MOVE_DOWN: `File::toSource` lives in `bun_logger` (T1→T2 cyclebreak).
let Ok(source) = bun_ast::to_source(
config_file_path,
bun_ast::ToSourceOptions { convert_bom: true },
bun_ast::ToSourceOptions {
convert_bom: true,
..Default::default()
},
) else {
return SloppyGlobalGitConfig::default();
};
Expand Down
4 changes: 2 additions & 2 deletions src/install/resolvers/folder_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use bun_core::{ZStr, strings};
use bun_paths::{self, MAX_PATH_BYTES, PathBuffer, SEP, SEP_STR};
use bun_resolver::fs::FileSystem;
use bun_semver::{self as semver, String as SemverString};
use bun_sys::{self, Fd, File, O};
use bun_sys::{self, Fd, File};

use crate::bun_json::Expr;
use crate::dependency::{self};
Expand Down Expand Up @@ -320,7 +320,7 @@ fn read_package_json_from_disk<R: FolderResolverImpl>(
bun_perf::trace(bun_perf::PerfEvent::FolderResolverReadPackageJSONFromDiskFolder);

let source = {
let file = File::openat(Fd::cwd(), abs.as_bytes(), O::RDONLY, 0)?;
let (file, _) = File::open_regular_at(Fd::cwd(), abs.as_bytes())?;
// defer file.close()
body.reset();
let read_result = file
Expand Down
5 changes: 2 additions & 3 deletions src/install/yarn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,11 +631,10 @@ pub(crate) fn migrate_yarn_lockfile<'a>(
let mut root_dependencies: Vec<RootDep> = Vec::new();

// read package.json to get specified dependencies
let Ok(package_json_fd) = bun_sys::File::openat(dir, b"package.json", bun_sys::O::RDONLY, 0)
else {
let Ok((package_json_fd, size)) = bun_sys::File::open_regular_at(dir, b"package.json") else {
return Err(crate::Error::InvalidPackageJSON);
};
let Ok(package_json_contents) = package_json_fd.read_to_end() else {
let Ok(package_json_contents) = package_json_fd.read_to_end_sized(size) else {
return Err(crate::Error::InvalidPackageJSON);
};

Expand Down
13 changes: 4 additions & 9 deletions src/runtime/cli/bunx_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,7 @@ impl BunxCommand {
dir_fd: Fd,
subpath_z: &ZStr,
) -> crate::Result<Box<[u8]>> {
let target_package_json_fd = bun_sys::openat(dir_fd, subpath_z, O::RDONLY, 0)?;
let target_package_json = bun_sys::File::from_fd(target_package_json_fd);

// TODO: make this better
let package_json_bytes = target_package_json.read_to_end()?;
let package_json_bytes = bun_sys::File::read_from(dir_fd, subpath_z)?;
let package_json_contents = package_json_bytes.as_slice();
let source = bun_ast::Source::init_path_string(subpath_z.as_bytes(), package_json_contents);

Expand Down Expand Up @@ -417,11 +413,10 @@ impl BunxCommand {
subpath[len] = 0;
// SAFETY: subpath[len] == 0 written above
let subpath_z = ZStr::from_buf(&subpath[..], len);
let target_package_json_fd = match bun_sys::openat(Fd::cwd(), subpath_z, O::RDONLY, 0) {
Ok(fd) => fd,
let target_package_json = match bun_sys::File::open_regular_at(Fd::cwd(), subpath_z) {
Ok((file, _)) => file,
Err(_) => return Err(crate::Error::NeedToInstall),
};
let target_package_json = bun_sys::File::from_fd(target_package_json_fd);

let is_stale: bool = 'is_stale: {
#[cfg(windows)]
Expand All @@ -432,7 +427,7 @@ impl BunxCommand {
// SAFETY: FFI call with valid out-params
let rc = unsafe {
win::ntdll::NtQueryInformationFile(
target_package_json_fd.native(),
target_package_json.handle.native(),
&mut io_status_block,
(&mut info as *mut win::FILE_BASIC_INFORMATION).cast(),
u32::try_from(size_of::<win::FILE_BASIC_INFORMATION>())
Expand Down
Loading
Loading