mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 03:48:56 +00:00
## Summary - Adds birthtime (file creation time) support on Linux using the `statx` syscall - Stores birthtime in architecture-specific unused fields of the kernel Stat struct (x86_64 and aarch64) - Falls back to traditional `stat` on kernels < 4.11 that don't support `statx` - Includes comprehensive tests validating birthtime behavior Fixes #6585 ## Implementation Details **src/sys.zig:** - Added `StatxField` enum for field selection - Implemented `statxImpl()`, `fstatx()`, `statx()`, and `lstatx()` functions - Stores birthtime in unused padding fields (architecture-specific for x86_64 and aarch64) - Graceful fallback to traditional stat if statx is not supported **src/bun.js/node/node_fs.zig:** - Updated `stat()`, `fstat()`, and `lstat()` to use statx functions on Linux **src/bun.js/node/Stat.zig:** - Added `getBirthtime()` helper to extract birthtime from architecture-specific storage **test/js/node/fs/fs-birthtime-linux.test.ts:** - Tests non-zero birthtime values - Verifies birthtime immutability across file modifications - Validates consistency across stat/lstat/fstat - Tests BigInt stats with nanosecond precision - Verifies birthtime ordering relative to other timestamps ## Test Plan - [x] Run `bun bd test test/js/node/fs/fs-birthtime-linux.test.ts` - all 5 tests pass - [x] Compare behavior with Node.js - identical behavior - [x] Compare with system Bun - system Bun returns epoch, new implementation returns real birthtime 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
97 lines
3.3 KiB
Zig
97 lines
3.3 KiB
Zig
/// POSIX-like stat structure with birthtime support for node:fs
|
|
/// This extends the standard POSIX stat with birthtime (creation time)
|
|
pub const PosixStat = extern struct {
|
|
dev: u64,
|
|
ino: u64,
|
|
mode: u32,
|
|
nlink: u64,
|
|
uid: u32,
|
|
gid: u32,
|
|
rdev: u64,
|
|
size: i64,
|
|
blksize: i64,
|
|
blocks: i64,
|
|
|
|
/// Access time
|
|
atim: bun.timespec,
|
|
/// Modification time
|
|
mtim: bun.timespec,
|
|
/// Change time (metadata)
|
|
ctim: bun.timespec,
|
|
/// Birth time (creation time) - may be zero if not supported
|
|
birthtim: bun.timespec,
|
|
|
|
/// Convert platform-specific bun.Stat to PosixStat
|
|
pub fn init(stat_: *const bun.Stat) PosixStat {
|
|
if (Environment.isWindows) {
|
|
// Windows: all fields need casting
|
|
const atime_val = stat_.atime();
|
|
const mtime_val = stat_.mtime();
|
|
const ctime_val = stat_.ctime();
|
|
const birthtime_val = stat_.birthtime();
|
|
|
|
return PosixStat{
|
|
.dev = @intCast(stat_.dev),
|
|
.ino = @intCast(stat_.ino),
|
|
.mode = @intCast(stat_.mode),
|
|
.nlink = @intCast(stat_.nlink),
|
|
.uid = @intCast(stat_.uid),
|
|
.gid = @intCast(stat_.gid),
|
|
.rdev = @intCast(stat_.rdev),
|
|
.size = @intCast(stat_.size),
|
|
.blksize = @intCast(stat_.blksize),
|
|
.blocks = @intCast(stat_.blocks),
|
|
.atim = .{ .sec = atime_val.sec, .nsec = atime_val.nsec },
|
|
.mtim = .{ .sec = mtime_val.sec, .nsec = mtime_val.nsec },
|
|
.ctim = .{ .sec = ctime_val.sec, .nsec = ctime_val.nsec },
|
|
.birthtim = .{ .sec = birthtime_val.sec, .nsec = birthtime_val.nsec },
|
|
};
|
|
} else {
|
|
// POSIX (Linux/macOS): use accessor methods and cast types
|
|
const atime_val = stat_.atime();
|
|
const mtime_val = stat_.mtime();
|
|
const ctime_val = stat_.ctime();
|
|
const birthtime_val = if (Environment.isLinux)
|
|
bun.timespec.epoch
|
|
else
|
|
stat_.birthtime();
|
|
|
|
return PosixStat{
|
|
.dev = @intCast(stat_.dev),
|
|
.ino = @intCast(stat_.ino),
|
|
.mode = @intCast(stat_.mode),
|
|
.nlink = @intCast(stat_.nlink),
|
|
.uid = @intCast(stat_.uid),
|
|
.gid = @intCast(stat_.gid),
|
|
.rdev = @intCast(stat_.rdev),
|
|
.size = @intCast(stat_.size),
|
|
.blksize = @intCast(stat_.blksize),
|
|
.blocks = @intCast(stat_.blocks),
|
|
.atim = .{ .sec = atime_val.sec, .nsec = atime_val.nsec },
|
|
.mtim = .{ .sec = mtime_val.sec, .nsec = mtime_val.nsec },
|
|
.ctim = .{ .sec = ctime_val.sec, .nsec = ctime_val.nsec },
|
|
.birthtim = .{ .sec = birthtime_val.sec, .nsec = birthtime_val.nsec },
|
|
};
|
|
}
|
|
}
|
|
|
|
pub fn atime(self: *const PosixStat) bun.timespec {
|
|
return self.atim;
|
|
}
|
|
|
|
pub fn mtime(self: *const PosixStat) bun.timespec {
|
|
return self.mtim;
|
|
}
|
|
|
|
pub fn ctime(self: *const PosixStat) bun.timespec {
|
|
return self.ctim;
|
|
}
|
|
|
|
pub fn birthtime(self: *const PosixStat) bun.timespec {
|
|
return self.birthtim;
|
|
}
|
|
};
|
|
|
|
const bun = @import("bun");
|
|
const Environment = bun.Environment;
|