feat: real stat metadata across all filesystems, plus mkdir and rmdir
This commit is contained in:
+51
-19
@@ -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
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user