feat: real stat metadata across all filesystems, plus mkdir and rmdir

This commit is contained in:
2026-08-04 13:03:08 +02:00
parent ca5331c9db
commit edc3452d61
14 changed files with 557 additions and 25 deletions
+51 -19
View File
@@ -116,6 +116,7 @@ static inline long _zos_syscall4(long nr, long a1, long a2, long a3, long a4) {
#define SYS_GETPID 3
#define SYS_DUPHANDLE 98
#define SYS_KILL 62
#define SYS_STAT 152
/* ========================================================================
errno
@@ -136,6 +137,17 @@ struct _mtk_datetime {
uint8_t second;
};
/* Mirrors montauk::abi::FileStat, the SYS_STAT output layout. Kept in step
with Api/Syscall.hpp by hand, like the syscall numbers above. */
struct _mtk_filestat {
uint64_t size;
int64_t mtime;
int64_t ctime;
int64_t atime;
uint32_t mode; /* POSIX i_mode bits: type + permissions */
uint32_t isDir;
};
struct _DIR {
int count;
int index;
@@ -2311,6 +2323,19 @@ int mkdir(const char *path, unsigned int mode) {
return (int)_zos_syscall1(SYS_FMKDIR, (long)path);
}
/* The VFS exposes no inode numbers, so derive a stable id from the path and
let distinct paths compare as distinct files. GCC's include-path setup
deduplicates directories by (st_dev, st_ino); leaving this 0 made every
directory "the same" and all but one include dir was silently dropped.
SYS_STAT supplies no inode either, so this stays in use regardless. */
static ino_t _path_fake_ino(const char *path) {
unsigned long ino = 5381;
for (const char *p = path; *p; p++) {
ino = ino * 33 + (unsigned char)*p;
}
return (ino_t)(ino | 1);
}
int stat(const char *path, struct stat *buf) {
if (path == NULL || buf == NULL) {
errno = EINVAL;
@@ -2318,21 +2343,35 @@ int stat(const char *path, struct stat *buf) {
}
memset(buf, 0, sizeof(*buf));
buf->st_ino = _path_fake_ino(path);
buf->st_nlink = 1;
buf->st_blksize = 4096;
/* Preferred path: real metadata from the filesystem driver. */
struct _mtk_filestat st;
if (_zos_syscall2(SYS_STAT, (long)path, (long)&st) == 0) {
buf->st_mode = (mode_t)st.mode;
/* A driver that reports no type bits still tells us whether the
entry is a directory; without this S_ISREG/S_ISDIR both fail. */
if ((buf->st_mode & S_IFMT) == 0) {
buf->st_mode |= st.isDir ? S_IFDIR : S_IFREG;
}
buf->st_size = (off_t)st.size;
buf->st_atime = (time_t)st.atime;
buf->st_mtime = (time_t)st.mtime;
buf->st_ctime = (time_t)st.ctime;
buf->st_blocks = (blkcnt_t)((buf->st_size + 511) / 512);
return 0;
}
/* Fallback for a filesystem whose driver has no Stat entry point: probe
with open/readdir as before. Size is recoverable this way, timestamps
and permissions are not. */
int h = (int)_zos_syscall1(SYS_OPEN, (long)path);
if (h >= 0) {
buf->st_mode = S_IFREG;
buf->st_size = (off_t)_zos_syscall1(SYS_GETSIZE, (long)h);
_zos_syscall1(SYS_CLOSE, (long)h);
/* No inodes in the VFS API: derive a stable id from the path
so that distinct paths compare as distinct files. */
unsigned long ino = 5381;
for (const char *p = path; *p; p++) {
ino = ino * 33 + (unsigned char)*p;
}
buf->st_ino = ino | 1;
buf->st_nlink = 1;
buf->st_blksize = 4096;
buf->st_blocks = (blkcnt_t)((buf->st_size + 511) / 512);
return 0;
}
@@ -2340,16 +2379,6 @@ int stat(const char *path, struct stat *buf) {
if (_path_is_directory(path)) {
buf->st_mode = S_IFDIR;
buf->st_size = 0;
/* Directories need distinct fake inodes too: GCC's include
path setup deduplicates directories by (st_dev, st_ino), so
leaving 0 here made every directory "the same" and all but
one include dir was silently dropped. */
unsigned long ino = 5381;
for (const char *p = path; *p; p++) {
ino = ino * 33 + (unsigned char)*p;
}
buf->st_ino = ino | 1;
buf->st_nlink = 1;
return 0;
}
@@ -2363,6 +2392,9 @@ int fstat(int fd, struct stat *buf) {
return -1;
}
memset(buf, 0, sizeof(*buf));
/* SYS_STAT is path-based and handles do not map back to paths, so this
cannot report real timestamps or permissions the way stat() does.
Callers needing those must stat the path instead. */
buf->st_mode = S_IFREG;
buf->st_size = (off_t)_zos_syscall1(SYS_GETSIZE, (long)fd);
/* Handles do not map back to paths, so fake a per-handle inode in a