Skip to content
Merged
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
6 changes: 4 additions & 2 deletions src/jsc/bindings/BunProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2385,10 +2385,12 @@ __attribute__((minsize)) static JSValue constructReportObjectComplete(VM& vm, Zi
};

for (size_t i = 0; i < std::size(resourceLimits); i++) {
// Node leaves out a limit it cannot read.
struct rlimit limit;
if (getrlimit(resourceLimits[i], &limit) != 0)
continue;
JSC::JSObject* limitObject = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 2);
RETURN_IF_EXCEPTION(scope, {});
struct rlimit limit;
getrlimit(resourceLimits[i], &limit);

JSValue soft = limit.rlim_cur == RLIM_INFINITY ? JSC::jsString(vm, String("unlimited"_s)) : JSC::jsNumber(limit.rlim_cur);

Expand Down
18 changes: 12 additions & 6 deletions src/resolver/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@ pub mod fs {
/// record the resulting fd budget so `need_to_close_files` can decide
/// whether to cache directory fds.
pub(crate) fn init(cwd: &'static [u8]) -> RealFS {
let file_limit = Self::adjust_ulimit().expect("unreachable");
let file_limit = Self::adjust_ulimit();
#[cfg(windows)]
let _ = file_limit;
RealFS {
Expand All @@ -1076,16 +1076,22 @@ pub mod fs {
}

/// Port of `RealFS.adjustUlimit` — always try to max out how many
/// files we can keep open.
pub(crate) fn adjust_ulimit() -> crate::CrateResult<usize> {
/// files we can keep open. Returns 0 when the limit cannot be read:
/// `need_to_close_files` is then always true and no fd stays cached.
/// `--watch`/`--hot` register an imported module through its cached
/// fd, so they then only see the entry point.
pub(crate) fn adjust_ulimit() -> usize {
#[cfg(not(unix))]
{
Ok(usize::MAX)
usize::MAX
}
#[cfg(unix)]
{
let resource = bun_sys::posix::RlimitResource::NOFILE;
let mut lim = bun_sys::posix::getrlimit(resource)?;
// Node ignores a failed getrlimit(RLIMIT_NOFILE) too (`PlatformInit`).
let Ok(mut lim) = bun_sys::posix::getrlimit(resource) else {
return 0;
};

// Cap at 1<<20 to match Node.js. On macOS the hard limit defaults to
// RLIM_INFINITY; raising soft anywhere near INT_MAX breaks child processes
Expand All @@ -1108,7 +1114,7 @@ pub mod fs {
lim.cur = raised.cur;
}
}
Ok(usize::try_from(lim.cur).expect("int cast"))
usize::try_from(lim.cur).expect("int cast")
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/sys/PosixStat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@ pub fn stat_ctime(s: &Stat) -> Timespec {
}
#[inline]
pub fn stat_birthtime(s: &Stat) -> Timespec {
// Linux gets the epoch arm; everything else reads the real birthtime.
// Windows `Stat` is `uv_stat_t` and libuv fills `birthtim` from NTFS
// CreationTime, so it must NOT fall into the epoch arm.
// Linux `struct stat` has no birthtime (only `statx` does), so it gets the
// ctime arm like libuv's `uv__to_stat`; everything else reads the real
// birthtime. Windows `Stat` is `uv_stat_t` and libuv fills `birthtim` from
// NTFS CreationTime, so it must NOT fall into the ctime arm.
#[cfg(windows)]
{
Timespec {
Expand Down Expand Up @@ -160,8 +161,7 @@ pub fn stat_birthtime(s: &Stat) -> Timespec {
target_os = "dragonfly"
)))]
{
let _ = s;
Timespec::EPOCH
stat_ctime(s)
}
}

Expand Down
19 changes: 11 additions & 8 deletions src/sys/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2871,11 +2871,7 @@ mod posix_impl {
}
/// Never errors; any non-zero rc → `Ok(false)`.
pub fn faccessat(dir: impl AsFd, sub: &ZStr) -> Maybe<bool> {
let dir = dir.as_fd();
// SAFETY: `dir` is a live fd (or AT_FDCWD); `ZStr::as_ptr()` is a
// valid NUL-terminated C string.
let rc = unsafe { libc::faccessat(dir.native(), sub.as_ptr(), libc::F_OK, 0) };
Ok(rc == 0)
Ok(exists_at(dir, sub))
}
pub fn futimens(fd: Fd, atime: TimeLike, mtime: TimeLike) -> Maybe<()> {
let ts = [atime.to_timespec(), mtime.to_timespec()];
Expand Down Expand Up @@ -2923,9 +2919,16 @@ mod posix_impl {
}
pub fn exists_at(dir: impl AsFd, sub: &ZStr) -> bool {
let dir = dir.as_fd();
// SAFETY: `dir` is a live fd (or AT_FDCWD); `ZStr::as_ptr()` is a
// valid NUL-terminated C string.
unsafe { libc::faccessat(dir.native(), sub.as_ptr(), libc::F_OK, 0) == 0 }
#[cfg(any(target_os = "linux", target_os = "android"))]
{
super::linux_syscall::faccessat(dir, sub, libc::F_OK).is_ok()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
{
// SAFETY: `dir` is a live fd (or AT_FDCWD); `ZStr::as_ptr()` is a
// valid NUL-terminated C string.
unsafe { libc::faccessat(dir.native(), sub.as_ptr(), libc::F_OK, 0) == 0 }
}
}
/// Calls extern C `is_executable_file` (c-bindings.cpp:72-89) via FFI.
pub fn is_executable_file_path(path: &ZStr) -> bool {
Expand Down
15 changes: 15 additions & 0 deletions src/sys/linux_syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,21 @@ pub(crate) fn fstatat(dir: i32, path: &ZStr, flags: i32) -> Result<libc::stat, i
retry(|| rustix::fs::statat(dir, path.as_cstr(), at)).map(stat_to_libc)
}

/// `faccessat(2)`, the syscall without a `flags` argument. Do not use libc's
/// `faccessat()` for this: glibc 2.33+ issues `faccessat2` first even for
/// `flags == 0` and falls back on ENOSYS only, so a seccomp filter that
/// answers the newer call with any other errno (EPERM from a profile written
/// before Linux 5.8) fails every check. With empty flags rustix goes straight
/// to `faccessat`.
#[inline]
pub(crate) fn faccessat(dir: Fd, path: &ZStr, mode: i32) -> Result<(), i32> {
// `Access` is `c_uint` bits on rustix's linux_raw backend, `c_int` on its
// libc backend (Android, where bionic's `faccessat` is already flag-less).
let access = rustix::fs::Access::from_bits_retain(mode as _);
let dir = dir.as_borrowed_fd();
retry(|| rustix::fs::accessat(dir, path.as_cstr(), access, rustix::fs::AtFlags::empty()))
}

/// Map rustix's kernel `struct stat` → `libc::stat`.
///
/// On Bun's tier-1 Linux targets (x86_64, aarch64 — gnu/musl/bionic alike),
Expand Down
Loading
Loading