Skip to content

Commit d136355

Browse files
Linux: Reimplement *stat via statx
Modern Linux ports do not implement the classic `stat` family, instead relying on libc to implement it via `statx`. Since the minimum kernel we support is 4.19 we can rely on the presence of `statx`, since it was released in version 4.11. The logic has been ported from musl, along with marking parameters as `noalias`. Also, the `makedev`, `major` and `minor` functions have been added.
1 parent 38cd0a6 commit d136355

File tree

1 file changed

+70
-42
lines changed

1 file changed

+70
-42
lines changed

lib/std/os/linux.zig

Lines changed: 70 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1944,62 +1944,90 @@ pub fn accept4(fd: i32, noalias addr: ?*sockaddr, noalias len: ?*socklen_t, flag
19441944
return syscall4(.accept4, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len), flags);
19451945
}
19461946

1947-
pub fn fstat(fd: i32, stat_buf: *Stat) usize {
1948-
if (native_arch == .riscv32) {
1949-
// riscv32 has made the interesting decision to not implement some of
1950-
// the older stat syscalls, including this one.
1951-
@compileError("No fstat syscall on this architecture.");
1952-
} else if (@hasField(SYS, "fstat64")) {
1953-
return syscall2(.fstat64, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(stat_buf));
1954-
} else {
1955-
return syscall2(.fstat, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(stat_buf));
1956-
}
1947+
pub fn fstat(fd: fd_t, statbuf: *Stat) usize {
1948+
if (fd < 0)
1949+
return @bitCast(@as(isize, -@intFromEnum(E.BADF)));
1950+
return fstatat(fd, "", statbuf, AT.EMPTY_PATH);
19571951
}
19581952

1959-
pub fn stat(pathname: [*:0]const u8, statbuf: *Stat) usize {
1960-
if (native_arch == .riscv32) {
1961-
// riscv32 has made the interesting decision to not implement some of
1962-
// the older stat syscalls, including this one.
1963-
@compileError("No stat syscall on this architecture.");
1964-
} else if (@hasField(SYS, "stat64")) {
1965-
return syscall2(.stat64, @intFromPtr(pathname), @intFromPtr(statbuf));
1966-
} else {
1967-
return syscall2(.stat, @intFromPtr(pathname), @intFromPtr(statbuf));
1968-
}
1953+
pub fn stat(noalias pathname: [*:0]const u8, noalias statbuf: *Stat) usize {
1954+
return fstatat(AT.FDCWD, pathname, statbuf, 0);
19691955
}
19701956

1971-
pub fn lstat(pathname: [*:0]const u8, statbuf: *Stat) usize {
1972-
if (native_arch == .riscv32) {
1973-
// riscv32 has made the interesting decision to not implement some of
1974-
// the older stat syscalls, including this one.
1975-
@compileError("No lstat syscall on this architecture.");
1976-
} else if (@hasField(SYS, "lstat64")) {
1977-
return syscall2(.lstat64, @intFromPtr(pathname), @intFromPtr(statbuf));
1978-
} else {
1979-
return syscall2(.lstat, @intFromPtr(pathname), @intFromPtr(statbuf));
1980-
}
1957+
pub fn lstat(noalias pathname: [*:0]const u8, noalias statbuf: *Stat) usize {
1958+
return fstatat(AT.FDCWD, pathname, statbuf, AT.SYMLINK_NOFOLLOW);
19811959
}
19821960

1983-
pub fn fstatat(dirfd: i32, path: [*:0]const u8, stat_buf: *Stat, flags: u32) usize {
1984-
if (native_arch == .riscv32) {
1985-
// riscv32 has made the interesting decision to not implement some of
1986-
// the older stat syscalls, including this one.
1987-
@compileError("No fstatat syscall on this architecture.");
1988-
} else if (@hasField(SYS, "fstatat64")) {
1989-
return syscall4(.fstatat64, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags);
1990-
} else {
1991-
return syscall4(.fstatat, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags);
1992-
}
1961+
pub fn fstatat(
1962+
dirfd: i32,
1963+
noalias pathname: [*:0]const u8,
1964+
noalias statbuf: *Stat,
1965+
flags: u32,
1966+
) usize {
1967+
var stx: Statx = undefined;
1968+
const rc = statx(dirfd, pathname, flags | AT.NO_AUTOMOUNT, 0x7ff, &stx);
1969+
if (rc != 0) return rc;
1970+
statbuf.* = .{
1971+
.dev = makedev(stx.dev_major, stx.dev_minor),
1972+
.ino = stx.ino,
1973+
.mode = stx.mode,
1974+
.nlink = stx.nlink,
1975+
.uid = stx.uid,
1976+
.gid = stx.gid,
1977+
.rdev = makedev(stx.rdev_major, stx.rdev_minor),
1978+
.size = stx.size,
1979+
.blksize = stx.blksize,
1980+
.blocks = stx.blocks,
1981+
.atim = .{
1982+
.sec = stx.atime.sec,
1983+
.nsec = stx.atime.nsec,
1984+
},
1985+
.mtim = .{
1986+
.sec = stx.atime.sec,
1987+
.nsec = stx.atime.nsec,
1988+
},
1989+
.ctim = .{
1990+
.sec = stx.atime.sec,
1991+
.nsec = stx.atime.nsec,
1992+
},
1993+
};
1994+
return 0;
1995+
}
1996+
1997+
/// Returns the major number from a device number.
1998+
pub fn major(dev: dev_t) u32 {
1999+
return ((dev >> 8) & 0xfff) | ((dev >> 32) & ~0xfff);
19932000
}
19942001

1995-
pub fn statx(dirfd: i32, path: [*:0]const u8, flags: u32, mask: u32, statx_buf: *Statx) usize {
2002+
/// Returns the minor number from a device number
2003+
pub fn minor(dev: dev_t) u32 {
2004+
return (dev & 0xff) | ((dev >> 12) & ~0xff);
2005+
}
2006+
2007+
/// Create a device number from a major/minor pair.
2008+
fn makedev(x: u32, y: u32) dev_t {
2009+
const xx: u64 = x;
2010+
const yy: u64 = y;
2011+
return ((yy & 0xff) << 0) |
2012+
((xx & 0xfff) << 8) |
2013+
((yy & ~0xff) << 12) |
2014+
((xx & ~0xfff) << 32);
2015+
}
2016+
2017+
pub fn statx(
2018+
dirfd: i32,
2019+
noalias path: [*:0]const u8,
2020+
flags: u32,
2021+
mask: u32,
2022+
noalias buf: *Statx,
2023+
) usize {
19962024
return syscall5(
19972025
.statx,
19982026
@as(usize, @bitCast(@as(isize, dirfd))),
19992027
@intFromPtr(path),
20002028
flags,
20012029
mask,
2002-
@intFromPtr(statx_buf),
2030+
@intFromPtr(buf),
20032031
);
20042032
}
20052033

0 commit comments

Comments
 (0)