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
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