Files
nt-lite/code/user/personalities/posix/posix_dir.c
T

255 lines
9.0 KiB
C

// POSIX compatibility support for posix dir.
#include <string.h>
#include <ntlite/syscall.h>
#include <ntlite/path.h>
#include <stdlib.h>
#include "posix_internal.h"
#include "posix_fd.h"
#include "posix_path.h"
typedef SYS_DIR_ENTRY KDIR_ENTRY;
#define KATTR_DIR 0x10
// Publishes the process-local native CWD into the authoritative posixsrv process record.
static KSTATUS PosixPublishCurrentDirectory(const char* nativeDirectory) {
if (!nativeDirectory) return NTLSTATUS_INVALID_PARAMETER;
size_t length = strlen(nativeDirectory);
if (length == 0 || length > SYS_PATH_MAX) return NTLSTATUS_INVALID_PARAMETER;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_SET_CURRENT_DIRECTORY;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.PathLength = (uint32_t)length;
memcpy(request.Path, nativeDirectory, length + 1u);
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeCallServer(&request, &reply);
if (!KSUCCESS(status)) return status;
return reply.Status;
}
// Implements chdir while keeping the local path resolver and posixsrv authority synchronized.
__declspec(dllexport) int chdir(const char* path) {
if (!path) { errno = EINVAL; return -1; }
char oldNative[SYS_PATH_MAX + 1u];
size_t oldRequired = 0;
KSTATUS status = NtlGetCurrentDirectory(oldNative, sizeof(oldNative), &oldRequired);
if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; }
char native[SYS_PATH_MAX + 1u];
const char* translated = PosixTranslatePath(path, native, sizeof(native));
if (translated != native || native[0] == '\0') { errno = EINVAL; return -1; }
status = NtlSetCurrentDirectory(SysSvFromCstr(native));
if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; }
char canonical[SYS_PATH_MAX + 1u];
size_t required = 0;
status = NtlGetCurrentDirectory(canonical, sizeof(canonical), &required);
if (KSUCCESS(status)) status = PosixPublishCurrentDirectory(canonical);
if (!KSUCCESS(status)) {
(void)NtlSetCurrentDirectory(SysSvFromCstr(oldNative));
errno = PosixStatusToErrno(status);
return -1;
}
return 0;
}
// Implements getcwd from the server-owned POSIX process attribute.
__declspec(dllexport) char* getcwd(char* buf, size_t size) {
if (!buf || size == 0) { errno = EINVAL; return (char*)0; }
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeGetProcessAttributes(&reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return (char*)0;
}
if (reply.CurrentDirectoryLength == 0 || reply.CurrentDirectoryLength > SYS_PATH_MAX ||
reply.CurrentDirectory[reply.CurrentDirectoryLength] != '\0') {
errno = EIO;
return (char*)0;
}
char posix[SYS_PATH_MAX + 1u];
NativeToPosix(reply.CurrentDirectory, posix, sizeof(posix));
size_t needed = strlen(posix) + 1u;
if (needed > size) {
errno = ERANGE;
return (char*)0;
}
memcpy(buf, posix, needed);
return buf;
}
// Copies one path string into either caller storage or a new allocation.
static char* PosixCopyResolvedPath(const char* value, char* resolvedPath) {
if (!value) { errno = EINVAL; return (char*)0; }
size_t needed = strlen(value) + 1;
char* out = resolvedPath;
if (!out) {
out = (char*)malloc(needed);
if (!out) { errno = ENOMEM; return (char*)0; }
}
memcpy(out, value, needed);
return out;
}
// Implements the realpath operation over the kernel lexical resolver.
__declspec(dllexport) char* realpath(const char* path, char* resolved_path) {
if (!path) { errno = EINVAL; return (char*)0; }
char native[SYS_PATH_MAX + 1];
const char* translated = PosixTranslatePath(path, native, sizeof(native));
if (strcmp(translated, SYNTH_DEVNULL) == 0 || strcmp(translated, SYNTH_DEVZERO) == 0)
return PosixCopyResolvedPath(path, resolved_path);
size_t required = 0;
KSTATUS status = NtlRealPath(SysSvFromCstr(translated), (char*)0, 0, &required);
if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return (char*)0; }
if (required == 0 || required > SYS_PATH_MAX + 1u) { errno = ERANGE; return (char*)0; }
char nativeResolved[SYS_PATH_MAX + 1u];
status = NtlRealPath(SysSvFromCstr(translated), nativeResolved, sizeof(nativeResolved), &required);
if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return (char*)0; }
char posix[SYS_PATH_MAX];
NativeToPosix(nativeResolved, posix, sizeof(posix));
return PosixCopyResolvedPath(posix, resolved_path);
}
__declspec(dllexport) DIR* opendir(const char* path) {
char native[512];
PosixTranslatePath(path, native, sizeof(native));
uint32_t kh;
KSTATUS s = NtlOpenDir(SysSvFromCstr(native),
SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN,
SYS_DIR_FLAG_NONE, &kh);
if (s != 0) { errno = ENOENT; return (DIR*)0; }
// Allocate DIR on the kernel heap (malloc) so it outlives the stack.
DIR* d = (DIR*)malloc(sizeof(DIR));
if (!d) { SysCloseHandle(kh); errno = ENOMEM; return (DIR*)0; }
d->KHandle = kh;
d->Valid = false;
return d;
}
__declspec(dllexport) struct dirent* readdir(DIR* d) {
if (!d) { errno = EBADF; return (struct dirent*)0; }
KDIR_ENTRY ke;
KSTATUS s = SysReadDir(d->KHandle, &ke);
if (s != 0) return (struct dirent*)0; // end of directory or error
size_t len = strlen(ke.Name);
if (len > NAME_MAX) len = NAME_MAX;
memcpy(d->Current.d_name, ke.Name, len);
d->Current.d_name[len] = '\0';
d->Current.d_type = (ke.Attr & KATTR_DIR) ? DT_DIR : DT_REG;
d->Valid = true;
return &d->Current;
}
__declspec(dllexport) int closedir(DIR* d) {
if (!d) { errno = EBADF; return -1; }
SysCloseHandle(d->KHandle);
free(d);
return 0;
}
// Converts native file metadata to a POSIX stat structure.
static int PosixFillStat(const SYS_FILE_STAT* native, struct stat* out) {
if (!native || !out) { errno = EINVAL; return -1; }
if (native->FileSize > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
memset(out, 0, sizeof(*out));
out->st_dev = native->VolumeId;
out->st_ino = native->FileId;
out->st_nlink = native->LinkCount ? native->LinkCount : 1;
out->st_size = (off_t)native->FileSize;
out->st_atime = (int64_t)native->LastAccessTime.EpochSeconds;
out->st_mtime = (int64_t)native->LastWriteTime.EpochSeconds;
out->st_ctime = (int64_t)native->ChangeTime.EpochSeconds;
if (native->Type == SYS_FILE_TYPE_DIRECTORY) {
out->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR |
S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
} else {
out->st_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
}
if (native->Attributes & SYS_FILE_ATTR_READ_ONLY)
out->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
return 0;
}
// Fills stat for one synthetic descriptor.
static int PosixFillSyntheticStat(mode_t type, struct stat* out) {
if (!out) { errno = EINVAL; return -1; }
memset(out, 0, sizeof(*out));
out->st_mode = type | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
out->st_nlink = 1;
return 0;
}
__declspec(dllexport) int stat(const char* path, struct stat* out) {
if (!path || !out) { errno = EINVAL; return -1; }
char native[512];
const char* p = PosixTranslatePath(path, native, sizeof(native));
if (strcmp(p, SYNTH_DEVNULL) == 0 || strcmp(p, SYNTH_DEVZERO) == 0)
return PosixFillSyntheticStat(S_IFCHR, out);
SYS_FILE_STAT nativeStat;
KSTATUS s = NtlStatPath(SysSvFromCstr(p), &nativeStat);
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
return PosixFillStat(&nativeStat, out);
}
__declspec(dllexport) int fstat(int fd, struct stat* out) {
if (!out) { errno = EINVAL; return -1; }
POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(fd);
if (!description) return -1;
int result = -1;
switch (description->Type) {
case PosixFdConsoleRead:
case PosixFdConsoleWrite:
case PosixFdDevNull:
case PosixFdDevZero:
result = PosixFillSyntheticStat(S_IFCHR, out);
break;
case PosixFdSocket:
result = PosixFillSyntheticStat(S_IFSOCK, out);
break;
case PosixFdPipeRead:
case PosixFdPipeWrite:
result = PosixFillSyntheticStat(S_IFIFO, out);
break;
case PosixFdFile: {
SYS_FILE_STAT nativeStat;
KSTATUS status = SysGetFileStat(description->KHandle, &nativeStat);
if (status != NTLSTATUS_SUCCESS)
errno = PosixStatusToErrno(status);
else
result = PosixFillStat(&nativeStat, out);
break;
}
default:
errno = EBADF;
break;
}
PosixFdRelease(description);
return result;
}