bun_sys 0.1.8

A Rust-native programmable browser runtime built on Servo and SpiderMonkey
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
// Transfer all the data between two file descriptors in the most efficient way.
// The copy starts at offset 0, the initial offsets are preserved.
// No metadata is transferred over.

use core::sync::atomic::{AtomicI32, Ordering};

#[cfg(not(windows))]
use crate::E;
use crate::Fd;
#[cfg(not(any(target_os = "linux", target_os = "android")))]
use crate::Tag;

// PORT NOTE: Zig was `const debug = bun.Output.scoped(.copy_file, .hidden)`.
// `declare_scope!` uses the ident as both static name AND tag string, but
// `copy_file` would shadow `pub fn copy_file()` below. Hand-expand with the
// correct env-var tag and a non-colliding static name.
// TODO(port): `scoped_log!` stringifies its scope ident for the `[tag]`
// prefix, so log lines show `[debug]` instead of `[copy_file]`; fix when
// `scoped_log!` grows a path/expr arm.
static debug: bun_core::output::ScopedLogger =
    bun_core::output::ScopedLogger::new("copy_file", bun_core::output::Visibility::Hidden);

#[cfg(windows)]
pub type InputType<'a> = &'a bun_core::WStr; // bun.OSPathSliceZ == [:0]const u16
#[cfg(not(windows))]
pub type InputType<'a> = Fd;
// PORT NOTE: lifetime param is unused on posix (Fd is Copy); kept so callers
// can write `InputType<'_>` uniformly across platforms.

// In a `bun install` with prisma, this reduces the system call count from ~18,000 to ~12,000
//
// The intended order here is:
// 1. ioctl_ficlone
// 2. copy_file_range
// 3. sendfile()
// 4. read() write() loop
//
// copy_file_range is supposed to do all the fast ways. It might be unnecessary
// to do ioctl_ficlone.
//
// sendfile() is a good fallback to avoid the read-write loops. sendfile() improves
// performance by moving the copying step to the kernel.
//
// On Linux, sendfile() can work between any two file descriptors which can be mmap'd.
// This means that it cannot work with TTYs and some special devices
// But it can work with two ordinary files
//
// on macOS and other platforms, sendfile() only works when one of the ends is a socket
// and in general on macOS, it doesn't seem to have much performance impact.
// PORT NOTE: `packed struct(u8)` with all-bool fields → bitflags!; field reads/writes
// reshaped to `.contains()`/`.insert()` below.
bitflags::bitflags! {
    #[derive(Clone, Copy, PartialEq, Eq)]
    pub struct LinuxCopyFileState: u8 {
        /// This is the most important flag for reducing the system call count
        /// When copying files from one folder to another, if we see EXDEV once
        /// there's a very good chance we will see it for every file thereafter in that folder.
        /// So we should remember whether or not we saw it and keep the state for roughly one directory tree.
        const HAS_SEEN_EXDEV               = 1 << 0;
        const HAS_IOCTL_FICLONE_FAILED     = 1 << 1;
        const HAS_COPY_FILE_RANGE_FAILED   = 1 << 2;
        const HAS_SENDFILE_FAILED          = 1 << 3;
        // _: u4 padding
    }
}
impl Default for LinuxCopyFileState {
    fn default() -> Self {
        Self::empty()
    }
}

#[derive(Default, Clone, Copy)]
pub struct EmptyCopyFileState;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub type CopyFileState = LinuxCopyFileState;
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub type CopyFileState = EmptyCopyFileState;

type CopyFileReturnType = crate::Result<()>;

pub fn copy_file_with_state(
    in_: InputType<'_>,
    out: InputType<'_>,
    copy_file_state: &mut CopyFileState,
) -> CopyFileReturnType {
    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    let _ = copy_file_state;
    #[cfg(target_os = "macos")]
    {
        unsafe extern "C" {
            // safe: by-value `c_int` fds + `u32` flags; bad fd → `EBADF`/
            // `EOPNOTSUPP`, never UB. `state` is `Option<NonNull<c_void>>`
            // (FFI-safe via the null-pointer niche → ABI-identical to a
            // nullable `copyfile_state_t`); we never allocate a state.
            safe fn fcopyfile(
                from: libc::c_int,
                to: libc::c_int,
                state: Option<core::ptr::NonNull<core::ffi::c_void>>,
                flags: u32,
            ) -> libc::c_int;
        }
        let rc = fcopyfile(in_.native(), out.native(), None, libc::COPYFILE_DATA);

        match crate::get_errno(rc) {
            E::SUCCESS => return Ok(()),
            // The source file is not a directory, symbolic link, or regular file.
            // Try with the fallback path before giving up.
            E::EOPNOTSUPP => {}
            e => return Err(crate::Error::from_code(e, Tag::copyfile)),
        }
    }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    {
        if can_use_ioctl_ficlone()
            && !copy_file_state.contains(LinuxCopyFileState::HAS_SEEN_EXDEV)
            && !copy_file_state.contains(LinuxCopyFileState::HAS_IOCTL_FICLONE_FAILED)
        {
            // We only check once if the ioctl is supported, and cache the result.
            // EXT4 does not support FICLONE.
            let rc = crate::linux::ioctl_ficlone(out, in_);
            // the ordering is flipped but it is consistent with other system calls.
            crate::syslog!("ioctl_ficlone({}, {}) = {}", in_, out, rc);
            match crate::get_errno(rc) {
                E::SUCCESS => return Ok(()),
                E::EXDEV => {
                    copy_file_state.insert(LinuxCopyFileState::HAS_SEEN_EXDEV);
                }

                // Don't worry about EINTR here.
                E::EINTR => {}

                // PORT NOTE: Zig matched .OPNOTSUPP; on Linux EOPNOTSUPP == ENOTSUP.
                E::EACCES | E::EBADF | E::EINVAL | E::ENOTSUP | E::ENOSYS | E::EPERM => {
                    bun_core::scoped_log!(debug, "ioctl_ficlonerange is NOT supported");
                    CAN_USE_IOCTL_FICLONE_.store(-1, Ordering::Relaxed);
                    copy_file_state.insert(LinuxCopyFileState::HAS_IOCTL_FICLONE_FAILED);
                }
                _ => {
                    // Failed for some other reason
                    copy_file_state.insert(LinuxCopyFileState::HAS_IOCTL_FICLONE_FAILED);
                }
            }
        }

        // Try copy_file_range first as that works at the FS level and is the
        // most efficient method (if available).
        let mut offset: u64 = 0;
        'cfr_loop: loop {
            // The kernel checks the u64 value `offset+count` for overflow, use
            // a 32 bit value so that the syscall won't return EINVAL except for
            // impossibly large files (> 2^64-1 - 2^32-1).
            let amt = copy_file_range(
                in_.native(),
                out.native(),
                (i32::MAX - 1) as usize,
                0,
                copy_file_state,
            )?;
            // Terminate when no data was copied
            if amt == 0 {
                break 'cfr_loop;
            }
            offset += amt as u64;
        }
        let _ = offset;
        return Ok(());
    }

    #[cfg(target_os = "freebsd")]
    {
        // FreeBSD 13+ has copy_file_range(2). Unlike Linux, we don't need
        // kernel-version probing — our minimum is 14.0.
        loop {
            // SAFETY: FFI call; fds are valid, offset ptrs are null (kernel uses file position)
            let rc = unsafe {
                libc::copy_file_range(
                    in_.native(),
                    core::ptr::null_mut(),
                    out.native(),
                    core::ptr::null_mut(),
                    (i32::MAX - 1) as usize,
                    0,
                )
            };
            crate::syslog!(
                "copy_file_range({}, {}) = {}",
                in_.native(),
                out.native(),
                rc
            );
            match crate::get_errno(rc) {
                E::SUCCESS => {
                    if rc == 0 {
                        return Ok(());
                    }
                }
                // Cross-filesystem or unsupported fd type — fall back to r/w loop.
                E::EXDEV | E::EINVAL | E::EOPNOTSUPP | E::EBADF => break,
                E::EINTR => continue,
                e => return Err(crate::Error::from_code(e, Tag::copy_file_range)),
            }
        }
    }

    #[cfg(windows)]
    {
        // SAFETY: FFI call; in_/out are NUL-terminated WStr, pointers valid for duration of call
        let rc = unsafe { crate::windows::CopyFileW(in_.as_ptr(), out.as_ptr(), 0) };
        if rc == 0 {
            return Err(crate::Error::from_code(
                crate::windows::get_last_errno(),
                Tag::copyfile,
            ));
        }
        return Ok(());
    }

    #[cfg(not(any(target_os = "linux", target_os = "android", windows)))]
    {
        loop {
            {
                let amt =
                    copy_file_read_write_loop(in_.native(), out.native(), (i32::MAX - 1) as usize)?;
                if amt == 0 {
                    break;
                }
            }
        }

        return Ok(());
    }
}

pub fn copy_file(in_: InputType<'_>, out: InputType<'_>) -> CopyFileReturnType {
    let mut state: CopyFileState = CopyFileState::default();
    copy_file_with_state(in_, out, &mut state)
}

static CAN_USE_COPY_FILE_RANGE: AtomicI32 = AtomicI32::new(0);

#[inline]
pub fn disable_copy_file_range_syscall() {
    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    {
        return;
    }
    #[cfg(any(target_os = "linux", target_os = "android"))]
    CAN_USE_COPY_FILE_RANGE.store(-1, Ordering::Relaxed);
}

pub fn can_use_copy_file_range_syscall() -> bool {
    let result = CAN_USE_COPY_FILE_RANGE.load(Ordering::Relaxed);
    if result == 0 {
        // This flag mostly exists to make other code more easily testable.
        if bun_core::env_var::BUN_CONFIG_DISABLE_COPY_FILE_RANGE
            .get()
            .unwrap_or(false)
        {
            bun_core::scoped_log!(
                debug,
                "copy_file_range is disabled by BUN_CONFIG_DISABLE_COPY_FILE_RANGE"
            );
            CAN_USE_COPY_FILE_RANGE.store(-1, Ordering::Relaxed);
            return false;
        }

        // Zig: `kernel.orderWithoutTag(.{ .major = 4, .minor = 5 }).compare(.gte)`
        if kernel_at_least(4, 5) {
            bun_core::scoped_log!(debug, "copy_file_range is supported");
            CAN_USE_COPY_FILE_RANGE.store(1, Ordering::Relaxed);
            return true;
        } else {
            bun_core::scoped_log!(debug, "copy_file_range is NOT supported");
            CAN_USE_COPY_FILE_RANGE.store(-1, Ordering::Relaxed);
            return false;
        }
    }

    result == 1
}

pub static CAN_USE_IOCTL_FICLONE_: AtomicI32 = AtomicI32::new(0);

#[inline]
pub fn disable_ioctl_ficlone() {
    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    {
        return;
    }
    #[cfg(any(target_os = "linux", target_os = "android"))]
    CAN_USE_IOCTL_FICLONE_.store(-1, Ordering::Relaxed);
}

pub fn can_use_ioctl_ficlone() -> bool {
    let result = CAN_USE_IOCTL_FICLONE_.load(Ordering::Relaxed);
    if result == 0 {
        // This flag mostly exists to make other code more easily testable.
        if bun_core::env_var::BUN_CONFIG_DISABLE_ioctl_ficlonerange
            .get()
            .unwrap_or(false)
        {
            bun_core::scoped_log!(
                debug,
                "ioctl_ficlonerange is disabled by BUN_CONFIG_DISABLE_ioctl_ficlonerange"
            );
            CAN_USE_IOCTL_FICLONE_.store(-1, Ordering::Relaxed);
            return false;
        }

        // Zig: `kernel.orderWithoutTag(.{ .major = 4, .minor = 5 }).compare(.gte)`
        if kernel_at_least(4, 5) {
            bun_core::scoped_log!(debug, "ioctl_ficlonerange is supported");
            CAN_USE_IOCTL_FICLONE_.store(1, Ordering::Relaxed);
            return true;
        } else {
            bun_core::scoped_log!(debug, "ioctl_ficlonerange is NOT supported");
            CAN_USE_IOCTL_FICLONE_.store(-1, Ordering::Relaxed);
            return false;
        }
    }

    result == 1
}

// TODO(port): `fd_t` is `std.posix.fd_t` (c_int on posix, HANDLE on windows). Only the
// posix paths call the fns below, so c_int is sufficient here.
#[allow(non_camel_case_types)]
type fd_t = core::ffi::c_int;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn copy_file_range(
    in_: fd_t,
    out: fd_t,
    len: usize,
    flags: u32,
    copy_file_state: &mut CopyFileState,
) -> crate::Result<usize> {
    if can_use_copy_file_range_syscall()
        && !copy_file_state.contains(LinuxCopyFileState::HAS_SEEN_EXDEV)
        && !copy_file_state.contains(LinuxCopyFileState::HAS_COPY_FILE_RANGE_FAILED)
    {
        loop {
            // TODO(port): raw syscall binding `std.os.linux.copy_file_range`
            // SAFETY: raw syscall; fds valid, offset ptrs null
            let rc = unsafe {
                crate::linux::copy_file_range(
                    in_,
                    core::ptr::null_mut(),
                    out,
                    core::ptr::null_mut(),
                    len,
                    flags,
                )
            };
            crate::syslog!("copy_file_range({}, {}, {}) = {}", in_, out, len, rc);
            match crate::get_errno(rc) {
                E::SUCCESS => return Ok(rc as usize),
                // these may not be regular files, try fallback
                E::EINVAL => {
                    copy_file_state.insert(LinuxCopyFileState::HAS_COPY_FILE_RANGE_FAILED);
                }
                // support for cross-filesystem copy added in Linux 5.3
                // and even then, it is frequently not supported.
                E::EXDEV => {
                    copy_file_state.insert(LinuxCopyFileState::HAS_SEEN_EXDEV);
                    copy_file_state.insert(LinuxCopyFileState::HAS_COPY_FILE_RANGE_FAILED);
                }
                // syscall added in Linux 4.5, use fallback
                // PORT NOTE: Zig matched .OPNOTSUPP; on Linux EOPNOTSUPP == ENOTSUP.
                E::ENOTSUP | E::ENOSYS => {
                    copy_file_state.insert(LinuxCopyFileState::HAS_COPY_FILE_RANGE_FAILED);
                    bun_core::scoped_log!(debug, "copy_file_range is NOT supported");
                    CAN_USE_COPY_FILE_RANGE.store(-1, Ordering::Relaxed);
                }
                E::EINTR => continue,
                _ => {
                    // failed for some other reason
                    copy_file_state.insert(LinuxCopyFileState::HAS_COPY_FILE_RANGE_FAILED);
                }
            }
            break;
        }
    }

    while !copy_file_state.contains(LinuxCopyFileState::HAS_SENDFILE_FAILED) {
        // TODO(port): raw syscall binding `std.os.linux.sendfile`
        // SAFETY: raw syscall; fds valid, offset ptr null
        let rc = unsafe { crate::linux::sendfile(out, in_, core::ptr::null_mut(), len) };
        crate::syslog!("sendfile({}, {}, {}) = {}", in_, out, len, rc);
        match crate::get_errno(rc) {
            E::SUCCESS => return Ok(rc as usize),
            E::EINTR => continue,
            // these may not be regular files, try fallback
            E::EINVAL => {
                copy_file_state.insert(LinuxCopyFileState::HAS_SENDFILE_FAILED);
            }
            // This shouldn't happen?
            E::EXDEV => {
                copy_file_state.insert(LinuxCopyFileState::HAS_SEEN_EXDEV);
                copy_file_state.insert(LinuxCopyFileState::HAS_SENDFILE_FAILED);
            }
            // they might not support it
            // PORT NOTE: Zig matched .OPNOTSUPP; on Linux EOPNOTSUPP == ENOTSUP.
            E::ENOTSUP | E::ENOSYS => {
                copy_file_state.insert(LinuxCopyFileState::HAS_SENDFILE_FAILED);
            }
            _ => {
                // failed for some other reason, fallback to read-write loop
                copy_file_state.insert(LinuxCopyFileState::HAS_SENDFILE_FAILED);
            }
        }
        break;
    }

    copy_file_read_write_loop(in_, out, len)
}

pub fn copy_file_read_write_loop(in_: fd_t, out: fd_t, len: usize) -> crate::Result<usize> {
    let mut stack_buf = bun_core::vec::UninitBuf::<{ 8 * 4096 }>::uninit();
    // SAFETY: `read` below is the only writer of `buf`; only `buf[..amt_read]` is read back.
    let buf = unsafe { stack_buf.as_bytes_mut() };
    let adjusted_count = buf.len().min(len);
    match crate::read(Fd::from_native(in_ as _), &mut buf[0..adjusted_count]) {
        Ok(amt_read) => {
            let mut amt_written: usize = 0;
            if amt_read == 0 {
                return Ok(0);
            }

            while amt_written < amt_read {
                {
                    let wrote =
                        crate::write(Fd::from_native(out as _), &buf[amt_written..amt_read])?;
                    if wrote == 0 {
                        return Ok(amt_written);
                    }

                    amt_written += wrote;
                }
            }
            if amt_read == 0 {
                return Ok(0);
            }
            Ok(amt_read)
        }
        Err(err) => Err(err),
    }
}

/// `Platform.kernelVersion().orderWithoutTag(.{ major, minor }).compare(.gte)`.
/// PORT NOTE: `bun_analytics::generate_header::Platform` (T6) is the canonical
/// source; T1 routes through `bun_core::linux_kernel_version()` (TYPE_ONLY
/// move-down) so this crate stays leaf. Compare matches Zig
/// `std.SemanticVersion.orderWithoutTag` (lexicographic on major→minor→patch,
/// patch defaults to 0 in the comparand).
#[inline]
fn kernel_at_least(major: u32, minor: u32) -> bool {
    let v = bun_core::linux_kernel_version();
    (v.major, v.minor, v.patch) >= (major, minor, 0)
}

// ported from: src/sys/copy_file.zig