code wiki / (root) / nx_dirent.nx

nx_dirent.nx source

↩ module page · 149 lines · 4559 B

1// nx_dirent.nx -- getdents64(2) directory iteration. 2// 3// Used by: 4// - Build tools that walk source directories (find *.nx files) 5// - Test harnesses that enumerate test cases 6// - File-based config loaders 7// - Sovereign `ls` (companion to objdump / readelf / nm) 8// 9// Linux RV64 syscall: 61 (getdents64). 10// Returns a buffer of variable-sized struct linux_dirent64 records: 11// ino_t d_ino u64 12// off_t d_off u64 13// ushort d_reclen u16 (size of THIS record) 14// uchar d_type u8 15// char d_name[] NUL-terminated, padded to alignment 16// 17// Total per record = d_reclen. Caller scans by adding d_reclen. 18 19// nx_safety_envelope: 20// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 21// sil_target: SIL1 22// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 23// verdict: NOT_YET_EVALUATED 24 25import "syscalls.nx" 26import "nx_fcntl.nx" 27const NX_MAGIC_8192: i64 = 8192 28 29// Per-target syscall number. Cross-target via @ifdef TARGET_X86_64 30// (same shape as nx_syscalls.nx + nx_fcntl.nx; bit-level no-proliferation 31// extends to substrate syscall ABI). 32@ifdef TARGET_X86_64 33const NX_SYS_GETDENTS64: i64 = 217 34@endif 35@ifndef TARGET_X86_64 36const NX_SYS_GETDENTS64: i64 = 61 37@endif 38 39// d_type values. 40const NX_DT_UNKNOWN: i64 = 0 41const NX_DT_FIFO: i64 = 1 42const NX_DT_CHR: i64 = 2 43const NX_DT_DIR: i64 = 4 44const NX_DT_BLK: i64 = 6 45const NX_DT_REG: i64 = 8 46const NX_DT_LNK: i64 = 10 47const NX_DT_SOCK: i64 = 12 48 49struct NxDirent { 50 ino: i64, 51 off: i64, 52 reclen: i64, // we store the unsigned u16 as i64 for ergonomics 53 dtype: i64, // u8 widened 54 name: *u8, // points into the calling buffer; NUL-terminated 55} 56 57const NX_DIRENT_BYTES: i64 = 40 58 59// Read a batch of directory entries into a caller buffer. Returns 60// the number of bytes filled (0 = end of directory, negative = 61// errno). Use the buffer with nx_dirent_iter to walk records. 62func nx_dirent_read(fd: i64, buf: *u8, cap: i64) -> i64 { 63 return __syscall(NX_SYS_GETDENTS64, fd, buf as i64, cap, 0, 0, 0) 64} 65 66// Decode the record at buf[off..] into `out` and return the offset 67// of the next record (off + d_reclen). Returns -1 if buf is 68// exhausted (caller should check before reading `out`). 69// 70// The `name` field points into the caller's buffer and remains 71// valid only while that buffer is held. Copy if you need to keep 72// it past the next nx_dirent_read. 73func nx_dirent_iter(buf: *u8, off: i64, end: i64, out: *NxDirent) -> i64 { 74 if off >= end { return -1 } 75 let p: i64 = (buf as i64) + off 76 77 // d_ino at +0 (u64 LE) 78 let ino_p: *i64 = p as *i64 79 out.ino = *ino_p 80 81 // d_off at +8 (u64 LE) 82 let off_p: *i64 = (p + 8) as *i64 83 out.off = *off_p 84 85 // d_reclen at +16 (u16 LE) 86 let rb: *u8 = (p + 16) as *u8 87 out.reclen = (rb[0] as i64) | ((rb[1] as i64) << 8) 88 89 // d_type at +18 (u8) 90 let tb: *u8 = (p + 18) as *u8 91 out.dtype = tb[0] 92 93 // d_name at +19, NUL-terminated. 94 out.name = ((p + 19) as *u8) 95 96 return off + out.reclen 97} 98 99// strlen-on-name helper. 100func nx_dirent_name_len(d: *NxDirent) -> i64 { 101 var n: i64 = 0 102 while d.name[n] != 0 { n = n + 1 } 103 return n 104} 105 106// ---- self-test --------------------------------------------------- 107 108func main() -> i64 { 109 // Open "." and read at least one entry. 110 let dot: *u8 = sys_mmap(8) 111 dot[0] = 0x2E; dot[1] = 0 112 let fd: i64 = nx_openat(NX_AT_FDCWD, dot, NX_O_RDONLY | NX_O_DIRECTORY, 0) 113 if fd < 0 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 114 115 let buf: *u8 = sys_mmap(NX_MAGIC_8192) 116 let n: i64 = nx_dirent_read(fd, buf, NX_MAGIC_8192) 117 if n <= 0 { 118 sys_close(fd) 119 return __syscall(93, 2, 0, 0, 0, 0, 0) 120 } 121 122 let dr_raw: *u8 = sys_mmap(NX_DIRENT_BYTES) 123 let dr: *NxDirent = dr_raw as *NxDirent 124 125 // First entry should be "." (always present in any directory). 126 let next_off: i64 = nx_dirent_iter(buf, 0, n, dr) 127 if next_off <= 0 { 128 sys_close(fd) 129 return __syscall(93, 3, 0, 0, 0, 0, 0) 130 } 131 if dr.name[0] != 0x2E { 132 sys_close(fd) 133 return __syscall(93, 4, 0, 0, 0, 0, 0) 134 } 135 // Either "." (single-char) or ".." (two-char). 136 if dr.name[1] != 0 { 137 if dr.name[1] != 0x2E { 138 sys_close(fd) 139 return __syscall(93, 5, 0, 0, 0, 0, 0) 140 } 141 } 142 if dr.reclen <= 0 { 143 sys_close(fd) 144 return __syscall(93, 6, 0, 0, 0, 0, 0) 145 } 146 147 sys_close(fd) 148 return 0 149}