Files
bun.sh/src/sys/PosixStat.zig
robobun 46e7a3b3c5 Implement birthtime support on Linux using statx syscall (#23209)
## 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>
2025-10-04 04:57:29 -07:00

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;