// This file is part of the Essence operating system. // It is released under the terms of the MIT license -- see LICENSE.md. // Written by: nakst. #ifndef IMPLEMENTATION struct POSIXFile { uint64_t posixFlags; // The flags given in SYS_open. volatile size_t handles; char *path; // Resolved path. EsFileOffset offsetIntoFile; uint64_t openFlags; // The flags used to open the object. KMutex mutex; KNode *node; Pipe *pipe; void *directoryBuffer; size_t directoryBufferLength; #define POSIX_FILE_TERMINAL (1) #define POSIX_FILE_NORMAL (2) #define POSIX_FILE_PIPE (3) #define POSIX_FILE_ZERO (4) #define POSIX_FILE_NULL (5) #define POSIX_FILE_DIRECTORY (6) int type; }; namespace POSIX { uintptr_t DoSyscall(_EsPOSIXSyscall syscall, uintptr_t *userStackPointer); KMutex forkMutex; KMutex threadPOSIXDataMutex; } struct POSIXThread { void *forkStack; size_t forkStackSize; uintptr_t forkUSP; Process *forkProcess; }; // TODO Use SYSCALL_READ and SYSCALL_WRITE. #define SYSCALL_BUFFER_POSIX(address, length, index, write) \ MMRegion *_region ## index = MMFindAndPinRegion(currentVMM, (address), (length)); \ if (!_region ## index && !fromKernel) { KernelLog(LOG_ERROR, "POSIX", "EFAULT", "POSIX application EFAULT at %x.\n", address); return -EFAULT; } \ EsDefer(if (_region ## index) MMUnpinRegion(currentVMM, _region ## index)); \ if (write && (_region ## index->flags & MM_REGION_READ_ONLY) && (~_region ## index->flags & MM_REGION_COPY_ON_WRITE)) \ { KernelLog(LOG_ERROR, "POSIX", "EFAULT", "POSIX application EFAULT (2) at %x.\n", address); return -EFAULT; } #define SYSCALL_BUFFER_ALLOW_NULL_POSIX(address, length, index) \ MMRegion *_region ## index = MMFindAndPinRegion(currentVMM, (address), (length)); \ EsDefer(if (_region ## index) MMUnpinRegion(currentVMM, _region ## index)); #define SYSCALL_HANDLE_POSIX_2(handle, out) \ Handle _ ## out; \ uint8_t status_ ## out = handleTable->ResolveHandle(&_ ## out, ConvertStandardInputTo3(handle), KERNEL_OBJECT_POSIX_FD); \ if (status_ ## out == RESOLVE_HANDLE_FAILED) return -EBADF; \ EsDefer(if (status_ ## out == RESOLVE_HANDLE_NORMAL) CloseHandleToObject(_ ## out.object, _ ## out.type, _ ## out.flags)); \ const Handle out = _ ## out #define SYSCALL_HANDLE_POSIX(handle, out) \ Handle _ ## out; \ uint8_t status_ ## out = handleTable->ResolveHandle(&_ ## out, ConvertStandardInputTo3(handle), KERNEL_OBJECT_POSIX_FD); \ if (status_ ## out == RESOLVE_HANDLE_FAILED) return -EBADF; \ EsDefer(if (status_ ## out == RESOLVE_HANDLE_NORMAL) CloseHandleToObject(_ ## out.object, _ ## out.type, _ ## out.flags)); \ POSIXFile *const out = (POSIXFile *) _ ## out.object #endif #ifdef IMPLEMENTATION #define _POSIX_SOURCE #define _GNU_SOURCE namespace POSIX { #include #include #include #include #include #include #include #include #include EsHandle ConvertStandardInputTo3(EsHandle handle) { if (handle) { return handle; } else { return 3; } } intptr_t Read(POSIXFile *file, K_USER_BUFFER void *base, size_t length, bool baseMappedToFile) { if (!length) return 0; // EsPrint("Read %d bytes from %x into %x\n", length, file, base); if (file->type == POSIX_FILE_TERMINAL) { return 0; } else if (file->type == POSIX_FILE_PIPE) { return file->pipe->Access(base, length, false, true); } else if (file->type == POSIX_FILE_NORMAL) { // EsPrint("READ '%z' %d/%d\n", file->path, file->offsetIntoFile, length); KMutexAcquire(&file->mutex); EsDefer(KMutexRelease(&file->mutex)); size_t count = FSFileReadSync(file->node, (void *) base, file->offsetIntoFile, length, baseMappedToFile ? FS_FILE_ACCESS_USER_BUFFER_MAPPED : 0); if (ES_CHECK_ERROR(count)) return -EIO; else { file->offsetIntoFile += count; return count; } } else if (file->type == POSIX_FILE_ZERO) { EsMemoryZero(base, length); return length; } else if (file->type == POSIX_FILE_NULL) { return 0; } else if (file->type == POSIX_FILE_DIRECTORY) { return 0; } return -EACCES; } intptr_t Write(POSIXFile *file, K_USER_BUFFER void *base, size_t length, bool baseMappedToFile) { if ((file->posixFlags & O_APPEND) && file->type == POSIX_FILE_NORMAL) { // TODO Make this atomic. file->offsetIntoFile = file->node->directoryEntry->totalSize; } if (!length) return 0; if (file->type == POSIX_FILE_TERMINAL) { EsPrint("%s", length, base); return length; } else if (file->type == POSIX_FILE_PIPE) { // EsPrint("Write %d bytes to pipe %x...\n", length, file->pipe); return file->pipe->Access(base, length, true, true); } else if (file->type == POSIX_FILE_NORMAL) { // EsPrint("WRITE '%z' %d/%d\n", file->path, file->offsetIntoFile, length); KMutexAcquire(&file->mutex); EsDefer(KMutexRelease(&file->mutex)); size_t count = FSFileWriteSync(file->node, (void *) base, file->offsetIntoFile, length, (baseMappedToFile ? FS_FILE_ACCESS_USER_BUFFER_MAPPED : 0)); if (ES_CHECK_ERROR(count)) return -EIO; else { file->offsetIntoFile += count; return count; } } else if (file->type == POSIX_FILE_ZERO || file->type == POSIX_FILE_NULL) { return length; } else if (file->type == POSIX_FILE_DIRECTORY) { return length; } return -EACCES; } void Stat(int type, KNode *node, struct stat *buffer) { EsMemoryZero(buffer, sizeof(struct stat)); if (type == POSIX_FILE_NORMAL) { buffer->st_ino = node->id; buffer->st_mode = S_IFREG; buffer->st_nlink = 1; if (node->directoryEntry->type == ES_NODE_FILE) { buffer->st_size = node->directoryEntry->totalSize; buffer->st_blksize = 512; buffer->st_blocks = buffer->st_size / 512; } } else if (type == POSIX_FILE_DIRECTORY) { buffer->st_ino = node->id; buffer->st_mode = S_IFDIR; buffer->st_nlink = 1; } else if (type == POSIX_FILE_PIPE) { buffer->st_mode = S_IFIFO; } else { buffer->st_mode = S_IFCHR; } } void CloneFileDescriptorTable(Process *forkProcess, HandleTable *handleTable) { HandleTable *source = handleTable, *destination = &forkProcess->handleTable; KMutexAcquire(&source->lock); EsDefer(KMutexRelease(&source->lock)); HandleTableL1 *l1 = &source->l1r; for (uintptr_t i = 0; i < HANDLE_TABLE_L1_ENTRIES; i++) { if (!l1->u[i]) continue; HandleTableL2 *l2 = l1->t[i]; for (uintptr_t k = 0; k < HANDLE_TABLE_L2_ENTRIES; k++) { Handle *handle = l2->t + k; if (!handle->object) continue; if (handle->type != KERNEL_OBJECT_POSIX_FD) continue; if (handle->flags & FD_CLOEXEC) continue; POSIXFile *file = (POSIXFile *) handle->object; OpenHandleToObject(file, KERNEL_OBJECT_POSIX_FD, handle->flags); destination->OpenHandle(handle->object, handle->flags, handle->type, k + i * HANDLE_TABLE_L2_ENTRIES); } } } uintptr_t DoSyscall(_EsPOSIXSyscall syscall, uintptr_t *userStackPointer) { Thread *currentThread = GetCurrentThread(); Process *currentProcess = currentThread->process; MMSpace *currentVMM = currentProcess->vmm; HandleTable *handleTable = ¤tProcess->handleTable; bool fromKernel = false; if (!currentThread->posixData) { KMutexAcquire(&threadPOSIXDataMutex); if (!currentThread->posixData) currentThread->posixData = (POSIXThread *) EsHeapAllocate(sizeof(POSIXThread), true, K_FIXED); KMutexRelease(&threadPOSIXDataMutex); if (!currentThread->posixData) { return -ENOMEM; } } if (currentThread->posixData->forkProcess) { handleTable = ¤tThread->posixData->forkProcess->handleTable; } // EsPrint("%x, %x, %x, %x, %x, %x, %x, %x\n", syscall.index, syscall.arguments[0], syscall.arguments[1], syscall.arguments[2], // syscall.arguments[3], syscall.arguments[4], syscall.arguments[5], syscall.arguments[6]); switch (syscall.index) { case SYS_open: { if (syscall.arguments[6] > SYSCALL_BUFFER_LIMIT) return -ENOMEM; SYSCALL_BUFFER_POSIX(syscall.arguments[0], syscall.arguments[6], 1, false); char *path2 = (char *) syscall.arguments[0]; size_t pathLength = syscall.arguments[6]; char *path = (char *) EsHeapAllocate(pathLength, false, K_FIXED); if (!path) return -ENOMEM; EsMemoryCopy(path, path2, pathLength); EsDefer(EsHeapFree(path, 0, K_FIXED)); int flags = syscall.arguments[1]; uint64_t openFlags = ES_FLAGS_DEFAULT; POSIXFile *file = (POSIXFile *) EsHeapAllocate(sizeof(POSIXFile), true, K_FIXED); if (!file) return -ENOMEM; file->posixFlags = flags; file->handles = 1; const char *devZero = "/dev/zero"; const char *devNull = "/dev/null"; // EsPrint("Open: %s, %x\n", pathLength, path, flags); if ((EsCStringLength(devZero) == pathLength && 0 == EsMemoryCompare(path, devZero, pathLength))) { file->type = POSIX_FILE_ZERO; } else if (EsCStringLength(devNull) == pathLength && 0 == EsMemoryCompare(path, devNull, pathLength)) { file->type = POSIX_FILE_NULL; } else { if (flags & O_EXCL) openFlags |= ES_NODE_FAIL_IF_FOUND; else if (flags & O_CREAT) {} else openFlags |= ES_NODE_FAIL_IF_NOT_FOUND; if (flags & O_DIRECTORY) openFlags |= ES_NODE_DIRECTORY; if (flags & O_APPEND) openFlags |= ES_FILE_WRITE_SHARED; else if (flags & O_RDWR) openFlags |= ES_FILE_WRITE_SHARED; else if (flags & O_WRONLY) openFlags |= ES_FILE_WRITE_SHARED; else if (!(flags & O_PATH)) openFlags |= ES_FILE_READ; KNodeInformation information = FSNodeOpen(path, pathLength, openFlags, (KNode *) syscall.arguments[4]); if (!information.node) { EsHeapFree(file, sizeof(POSIXFile), K_FIXED); return (information.error == ES_ERROR_FILE_DOES_NOT_EXIST || information.error == ES_ERROR_PATH_NOT_TRAVERSABLE) ? -ENOENT : -EACCES; } if (information.node->directoryEntry->type == ES_NODE_DIRECTORY && !(flags & O_DIRECTORY) && !(flags & O_PATH)) { EsHeapFree(file, sizeof(POSIXFile), K_FIXED); CloseHandleToObject(information.node, KERNEL_OBJECT_NODE, openFlags); return -EISDIR; } file->node = information.node; file->openFlags = openFlags; file->type = file->node->directoryEntry->type == ES_NODE_DIRECTORY ? POSIX_FILE_DIRECTORY : POSIX_FILE_NORMAL; } file->path = (char *) EsHeapAllocate(pathLength + 1, true, K_FIXED); if (file->path) EsMemoryCopy(file->path, path, pathLength); if ((flags & O_TRUNC) && file->type == POSIX_FILE_NORMAL) { FSFileResize(file->node, 0); } // EsPrint("OPEN '%s' %z\n", pathLength, path, (openFlags & ES_NODE_WRITE_ACCESS) ? "Write" : ((openFlags & ES_NODE_READ_ACCESS) ? "Read" : "None")); return handleTable->OpenHandle(file, (flags & O_CLOEXEC) ? FD_CLOEXEC : 0, KERNEL_OBJECT_POSIX_FD) ?: -ENFILE; } break; case ES_POSIX_SYSCALL_GET_POSIX_FD_PATH: { if (syscall.arguments[2] > SYSCALL_BUFFER_LIMIT) return -ENOMEM; SYSCALL_HANDLE_POSIX(syscall.arguments[0], file); SYSCALL_BUFFER_POSIX(syscall.arguments[1], syscall.arguments[2], 2, true); KMutexAcquire(&file->mutex); EsDefer(KMutexRelease(&file->mutex)); int length = file->path ? EsCStringLength(file->path) : 0; if (length > syscall.arguments[2]) length = syscall.arguments[2]; EsMemoryZero((void *) syscall.arguments[1], syscall.arguments[2]); EsMemoryCopy((void *) syscall.arguments[1], file->path, length); return length; } break; case SYS_close: { if (!handleTable->CloseHandle(ConvertStandardInputTo3(syscall.arguments[0]))) { return -EBADF; } return 0; } break; case SYS_fstat: { SYSCALL_HANDLE_POSIX(syscall.arguments[0], file); SYSCALL_BUFFER_POSIX(syscall.arguments[1], sizeof(struct stat), 1, true); struct stat temp; KMutexAcquire(&file->mutex); Stat(file->type, file->node, &temp); KMutexRelease(&file->mutex); EsMemoryCopy((struct stat *) syscall.arguments[1], &temp, sizeof(struct stat)); return 0; } break; case SYS_fcntl: { SYSCALL_HANDLE_POSIX_2(syscall.arguments[0], fd); POSIXFile *file = (POSIXFile *) fd.object; if (syscall.arguments[1] == F_GETFD) { return fd.flags; } else if (syscall.arguments[1] == F_SETFD) { handleTable->ModifyFlags(syscall.arguments[0], syscall.arguments[2]); } else if (syscall.arguments[1] == F_GETFL) { return file->posixFlags; } else if (syscall.arguments[1] == F_SETFL) { if (syscall.arguments[2] == O_APPEND) { file->posixFlags |= O_APPEND; } else if (syscall.arguments[2] == 0) { file->posixFlags &= ~O_APPEND; } else { KernelPanic("POSIX::DoSyscall - Unimplemented F_SETFL %d.\n", syscall.arguments[2]); } } else if (syscall.arguments[1] == F_DUPFD) { // Duplicate with FD_CLOEXEC clear. OpenHandleToObject(file, KERNEL_OBJECT_POSIX_FD, 0); return handleTable->OpenHandle(fd.object, 0, fd.type) ?: -ENFILE; } else if (syscall.arguments[1] == F_DUPFD_CLOEXEC) { // Duplicate with FD_CLOEXEC set. OpenHandleToObject(file, KERNEL_OBJECT_POSIX_FD, FD_CLOEXEC); return handleTable->OpenHandle(fd.object, FD_CLOEXEC, fd.type) ?: -ENFILE; } else { KernelPanic("POSIX::DoSyscall - Unimplemented fcntl %d.\n", syscall.arguments[1]); } } break; case SYS_lseek: { SYSCALL_HANDLE_POSIX(syscall.arguments[0], file); KMutexAcquire(&file->mutex); EsDefer(KMutexRelease(&file->mutex)); if (syscall.arguments[2] == SEEK_SET) { file->offsetIntoFile = syscall.arguments[1]; } else if (syscall.arguments[2] == SEEK_CUR) { file->offsetIntoFile += syscall.arguments[1]; } else if (syscall.arguments[2] == SEEK_END && file->type == POSIX_FILE_NORMAL) { file->offsetIntoFile = file->node->directoryEntry->totalSize + syscall.arguments[1]; } else { return -EINVAL; } return file->offsetIntoFile; } break; case SYS_ioctl: { SYSCALL_HANDLE_POSIX(syscall.arguments[0], file); KMutexAcquire(&file->mutex); EsDefer(KMutexRelease(&file->mutex)); if (syscall.arguments[1] == TIOCGWINSZ) { if (file->type == POSIX_FILE_TERMINAL || file->type == POSIX_FILE_PIPE) { SYSCALL_BUFFER_POSIX(syscall.arguments[1], sizeof(struct winsize), 1, true); struct winsize *size = (struct winsize *) syscall.arguments[2]; size->ws_row = 80; size->ws_col = 25; size->ws_xpixel = 800; size->ws_ypixel = 800; return 0; } else { return -EINVAL; } } } break; case SYS_read: { SYSCALL_HANDLE_POSIX(syscall.arguments[0], file); SYSCALL_BUFFER_POSIX(syscall.arguments[1], syscall.arguments[2], 3, true); return Read(file, (void *) syscall.arguments[1], syscall.arguments[2], _region3->flags & MM_REGION_FILE); } break; case SYS_readv: { if (syscall.arguments[2] > 1024) return -EINVAL; SYSCALL_HANDLE_POSIX(syscall.arguments[0], file); SYSCALL_BUFFER_POSIX(syscall.arguments[1], syscall.arguments[2] * sizeof(struct iovec *), 2, false); struct iovec *vectors = (struct iovec *) EsHeapAllocate(syscall.arguments[2] * sizeof(struct iovec), false, K_FIXED); if (!vectors) return -ENOMEM; EsMemoryCopy(vectors, (void *) syscall.arguments[1], syscall.arguments[2] * sizeof(struct iovec)); EsDefer(EsHeapFree(vectors, syscall.arguments[2] * sizeof(struct iovec), K_FIXED)); size_t bytesRead = 0; for (uintptr_t i = 0; i < (uintptr_t) syscall.arguments[2]; i++) { if (!vectors[i].iov_len) continue; SYSCALL_BUFFER_POSIX((uintptr_t) vectors[i].iov_base, vectors[i].iov_len, 3, true); intptr_t result = Read(file, vectors[i].iov_base, vectors[i].iov_len, _region3->flags & MM_REGION_FILE); if (result < 0) return result; bytesRead += result; } return bytesRead; } break; case SYS_write: { SYSCALL_HANDLE_POSIX(syscall.arguments[0], file); SYSCALL_BUFFER_POSIX(syscall.arguments[1], syscall.arguments[2], 3, false); if (file->type == POSIX_FILE_NORMAL && !(file->openFlags & (ES_FILE_WRITE_SHARED | ES_FILE_WRITE))) { return -EACCES; } return Write(file, (void *) syscall.arguments[1], syscall.arguments[2], _region3->flags & MM_REGION_FILE); } break; case SYS_writev: { if (syscall.arguments[2] > 1024) return -EINVAL; SYSCALL_HANDLE_POSIX(syscall.arguments[0], file); SYSCALL_BUFFER_POSIX(syscall.arguments[1], syscall.arguments[2] * sizeof(struct iovec *), 2, false); struct iovec *vectors = (struct iovec *) EsHeapAllocate(syscall.arguments[2] * sizeof(struct iovec), false, K_FIXED); if (!vectors) return -ENOMEM; EsMemoryCopy(vectors, (void *) syscall.arguments[1], syscall.arguments[2] * sizeof(struct iovec)); EsDefer(EsHeapFree(vectors, syscall.arguments[2] * sizeof(struct iovec), K_FIXED)); size_t bytesWritten = 0; if (file->type == POSIX_FILE_NORMAL && !(file->openFlags & (ES_FILE_WRITE_SHARED | ES_FILE_WRITE))) { return -EACCES; } for (uintptr_t i = 0; i < (uintptr_t) syscall.arguments[2]; i++) { if (!vectors[i].iov_len) continue; // EsPrint("writev %d: %x/%d\n", i, vectors[i].iov_base, vectors[i].iov_len); SYSCALL_BUFFER_POSIX((uintptr_t) vectors[i].iov_base, vectors[i].iov_len, 3, false); intptr_t result = Write(file, vectors[i].iov_base, vectors[i].iov_len, _region3->flags & MM_REGION_FILE); if (result < 0) return result; bytesWritten += result; } return bytesWritten; } break; case SYS_vfork: { // To vfork: save the stack and return 0. // To exec*: create the new process, restore the state of our stack, then return the new process's ID. KMutexAcquire(&forkMutex); EsDefer(KMutexRelease(&forkMutex)); // Did we complete the last vfork? if (currentThread->posixData->forkStack) return -ENOMEM; // EsPrint("vfork->\n"); // Allocate the process. Process *forkProcess = currentThread->posixData->forkProcess = ProcessSpawn(PROCESS_NORMAL); forkProcess->pgid = currentProcess->pgid; // Clone our FDs. CloneFileDescriptorTable(forkProcess, handleTable); // Save the state of the user's stack. currentThread->posixData->forkStackSize = currentThread->userStackCommit; currentThread->posixData->forkStack = (void *) EsHeapAllocate(currentThread->posixData->forkStackSize, false, K_PAGED); #ifdef K_ARCH_STACK_GROWS_DOWN EsMemoryCopy(currentThread->posixData->forkStack, (void *) (currentThread->userStackBase + currentThread->userStackReserve - currentThread->posixData->forkStackSize), currentThread->posixData->forkStackSize); #else EsMemoryCopy(currentThread->posixData->forkStack, (void *) currentThread->userStackBase, currentThread->posixData->forkStackSize); #endif currentThread->posixData->forkUSP = *userStackPointer; // Return 0. return 0; } break; case SYS_execve: { KMutexAcquire(&forkMutex); EsDefer(KMutexRelease(&forkMutex)); // Are we vforking? if (!currentThread->posixData->forkStack) return -ENOMEM; if (syscall.arguments[1] > K_MAX_PATH) return -ENOMEM; if (syscall.arguments[3] > SYSCALL_BUFFER_LIMIT) return -ENOMEM; // EsPrint("<-execve\n"); SYSCALL_BUFFER_POSIX(syscall.arguments[0], syscall.arguments[1], 1, false); SYSCALL_BUFFER_POSIX(syscall.arguments[2], syscall.arguments[3], 2, false); // Setup the process object. char *path = (char *) EsHeapAllocate(syscall.arguments[1], false, K_FIXED); if (!path) return -ENOMEM; EsMemoryCopy(path, (void *) syscall.arguments[0], syscall.arguments[1]); Process *process = currentThread->posixData->forkProcess; process->data.subsystemID = ES_SUBSYSTEM_ID_POSIX; process->data.subsystemData = ConstantBufferCreate((void *) syscall.arguments[2], syscall.arguments[3], process); process->posixForking = true; process->permissions = currentProcess->permissions; struct { SystemStartupDataHeader header; EsMountPoint mountPoint; } systemData; EsMemoryZero(&systemData, sizeof(systemData)); OpenHandleToObject((void *) syscall.arguments[4], KERNEL_OBJECT_NODE, ES__NODE_DIRECTORY_WRITE); systemData.mountPoint.base = process->handleTable.OpenHandle((void *) syscall.arguments[4], ES__NODE_DIRECTORY_WRITE, KERNEL_OBJECT_NODE); systemData.mountPoint.prefixBytes = EsStringFormat(systemData.mountPoint.prefix, sizeof(systemData.mountPoint.prefix), "|POSIX:"); systemData.header.initialMountPointCount = 1; process->data.systemData = ConstantBufferCreate(&systemData, sizeof(systemData), process); // Start the process. if (!ProcessStart(process, path, syscall.arguments[1], (KNode *) syscall.arguments[4])) { EsHeapFree(path, 0, K_FIXED); return -ENOMEM; } KSpinlockAcquire(&scheduler.dispatchSpinlock); process->posixForking = false; bool setKilledEvent = process->allThreadsTerminated; KSpinlockRelease(&scheduler.dispatchSpinlock); if (setKilledEvent) KEventSet(&process->killedEvent, true); EsHeapFree(path, 0, K_FIXED); CloseHandleToObject(process->executableMainThread, KERNEL_OBJECT_THREAD); // Restore the state of our stack. #ifdef K_ARCH_STACK_GROWS_DOWN EsMemoryCopy((void *) (currentThread->userStackBase + currentThread->userStackReserve - currentThread->posixData->forkStackSize), currentThread->posixData->forkStack, currentThread->posixData->forkStackSize); #else EsMemoryCopy((void *) currentThread->userStackBase, currentThread->posixData->forkStack, currentThread->posixData->forkStackSize); #endif EsHeapFree(currentThread->posixData->forkStack, currentThread->posixData->forkStackSize, K_PAGED); *userStackPointer = currentThread->posixData->forkUSP; // Fork complete. currentThread->posixData->forkProcess = nullptr; currentThread->posixData->forkStack = nullptr; currentThread->posixData->forkUSP = 0; return currentProcess->handleTable.OpenHandle(process, 0, KERNEL_OBJECT_PROCESS); } break; case SYS_exit_group: { EsHandle processHandle = 0; KMutexAcquire(&forkMutex); // Are we vforking? if (currentThread->posixData->forkStack) { // Restore the state of our stack. #ifdef K_ARCH_STACK_GROWS_DOWN EsMemoryCopy((void *) (currentThread->userStackBase + currentThread->userStackReserve - currentThread->posixData->forkStackSize), currentThread->posixData->forkStack, currentThread->posixData->forkStackSize); #else EsMemoryCopy((void *) currentThread->userStackBase, currentThread->posixData->forkStack, currentThread->posixData->forkStackSize); #endif EsHeapFree(currentThread->posixData->forkStack, currentThread->posixData->forkStackSize, K_PAGED); *userStackPointer = currentThread->posixData->forkUSP; // Set the exit status. Process *process = currentThread->posixData->forkProcess; process->exitStatus = syscall.arguments[0]; process->allThreadsTerminated = true; // Set in case execve() was not attempted. KEventSet(&process->killedEvent); processHandle = currentProcess->handleTable.OpenHandle(currentThread->posixData->forkProcess, 0, KERNEL_OBJECT_PROCESS); // Close any handles the process owned. process->handleTable.Destroy(); // Cancel the vfork. currentThread->posixData->forkProcess = nullptr; currentThread->posixData->forkStack = nullptr; currentThread->posixData->forkUSP = 0; } KMutexRelease(&forkMutex); if (processHandle) { return processHandle; } else { ProcessTerminate(currentProcess, syscall.arguments[0]); } } break; case SYS_sysinfo: { SYSCALL_BUFFER_POSIX(syscall.arguments[0], sizeof(struct sysinfo), 1, true); struct sysinfo *buffer = (struct sysinfo *) syscall.arguments[0]; EsMemoryZero(buffer, sizeof(struct sysinfo)); // TODO Incomplete. buffer->totalram = K_PAGE_SIZE * pmm.commitFixedLimit; buffer->freeram = K_PAGE_SIZE * (pmm.countZeroedPages + pmm.countFreePages); buffer->procs = scheduler.allProcesses.count; buffer->mem_unit = 1; return 0; } break; case SYS_dup2: { SYSCALL_HANDLE_POSIX_2(syscall.arguments[0], fd); POSIXFile *file = (POSIXFile *) fd.object; // Try to close the newfd. handleTable->CloseHandle(ConvertStandardInputTo3(syscall.arguments[1])); // Clone the oldfd as newfd. OpenHandleToObject(file, KERNEL_OBJECT_POSIX_FD, fd.flags); return handleTable->OpenHandle(fd.object, fd.flags, fd.type, ConvertStandardInputTo3(syscall.arguments[1])) ? syscall.arguments[1] : -EBUSY; } break; case SYS_pipe2: { if (syscall.arguments[1] & ~O_CLOEXEC) { return -EINVAL; } SYSCALL_BUFFER_POSIX(syscall.arguments[0], sizeof(int) * 2, 1, true); int *fildes = (int *) syscall.arguments[0]; Pipe *pipe = (Pipe *) EsHeapAllocate(sizeof(Pipe), true, K_PAGED); POSIXFile *reader = (POSIXFile *) EsHeapAllocate(sizeof(POSIXFile), true, K_FIXED); POSIXFile *writer = (POSIXFile *) EsHeapAllocate(sizeof(POSIXFile), true, K_FIXED); if (!reader || !writer || !pipe) { EsHeapFree(pipe, 0, K_PAGED); EsHeapFree(reader, 0, K_FIXED); EsHeapFree(writer, 0, K_FIXED); return -ENOMEM; } KEventSet(&pipe->canWrite); reader->type = POSIX_FILE_PIPE; reader->openFlags = PIPE_READER; reader->handles = 1; reader->pipe = pipe; writer->type = POSIX_FILE_PIPE; writer->openFlags = PIPE_WRITER; writer->handles = 1; writer->pipe = pipe; pipe->readers = 1; pipe->writers = 1; fildes[0] = handleTable->OpenHandle(reader, (syscall.arguments[1] & O_CLOEXEC) ? FD_CLOEXEC : 0, KERNEL_OBJECT_POSIX_FD); fildes[1] = handleTable->OpenHandle(writer, (syscall.arguments[1] & O_CLOEXEC) ? FD_CLOEXEC : 0, KERNEL_OBJECT_POSIX_FD); return 0; } break; case SYS_getdents64: { // TODO This is a bit of a hack. // Especially allocating the entire directory list in the kernel's memory space. if (syscall.arguments[2] > SYSCALL_BUFFER_LIMIT) return -ENOMEM; SYSCALL_HANDLE_POSIX(syscall.arguments[0], file); SYSCALL_BUFFER_POSIX(syscall.arguments[1], syscall.arguments[2], 3, true); KMutexAcquire(&file->mutex); EsDefer(KMutexRelease(&file->mutex)); if (file->type != POSIX_FILE_DIRECTORY) { return -EACCES; } if (!file->offsetIntoFile || !file->directoryBuffer) { EsHeapFree(file->directoryBuffer, file->directoryBufferLength, K_PAGED); file->directoryBuffer = nullptr; file->offsetIntoFile = 0; file->directoryBufferLength = 0; uintptr_t bufferSize = file->node->directoryEntry->directoryChildren; EsDirectoryChild *buffer = (EsDirectoryChild *) EsHeapAllocate(sizeof(EsDirectoryChild) * bufferSize, false, K_FIXED); if (!buffer) { return -ENOMEM; } size_t count = FSDirectoryEnumerate(file->node, buffer, bufferSize); if (ES_CHECK_ERROR(count)) { EsHeapFree(buffer, sizeof(EsDirectoryChild) * bufferSize, K_FIXED); return -EIO; } size_t neededSize = 0; for (uintptr_t i = 0; i < count; i++) { neededSize += RoundUp(19 + buffer[i].nameBytes + 1, (size_t) 8); } file->directoryBuffer = EsHeapAllocate(neededSize, true, K_PAGED); if (!file->directoryBuffer) { EsHeapFree(buffer, bufferSize * sizeof(EsDirectoryChild), K_FIXED); return -ENOMEM; } file->directoryBufferLength = neededSize; uint8_t *position = (uint8_t *) file->directoryBuffer; for (uintptr_t i = 0; i < count; i++) { // EsPrint("%d - %d\n", i, position - (uint8_t *) file->directoryBuffer); size_t size = RoundUp(19 + buffer[i].nameBytes + 1, (size_t) 8); EsDirectoryChild *entry = buffer + i; ((uint64_t *) position)[0] = EsRandomU64(); // We don't have a concept of inodes. ((uint64_t *) position)[1] = position - (uint8_t *) file->directoryBuffer + size; ((uint16_t *) position)[8] = size; ((uint8_t *) position)[18] = entry->type == ES_NODE_DIRECTORY ? 4 /* DT_DIR */ : entry->type == ES_NODE_FILE ? 8 /* DT_REG */ : 0 /* DT_UNKNOWN */; EsMemoryCopy(position + 19, entry->name, entry->nameBytes); *(position + 19 + entry->nameBytes) = 0; position += size; } EsHeapFree(buffer, bufferSize * sizeof(EsDirectoryChild), K_FIXED); } uint64_t offset = file->offsetIntoFile; size_t bufferSize = syscall.arguments[2]; while (offset + 19 < file->directoryBufferLength) { uint8_t *position = (uint8_t *) file->directoryBuffer + offset; uint64_t nextOffset = ((uint64_t *) position)[1]; if (nextOffset > file->directoryBufferLength || nextOffset < offset + 19 || nextOffset - file->offsetIntoFile >= bufferSize) { break; } offset = nextOffset; } size_t bytesToReturn = offset - file->offsetIntoFile; if (bytesToReturn > bufferSize) { bytesToReturn = bufferSize; } EsMemoryCopy((void *) syscall.arguments[1], (uint8_t *) file->directoryBuffer + file->offsetIntoFile, bytesToReturn); file->offsetIntoFile += bytesToReturn; return bytesToReturn; } break; case SYS_setpgid: { Process *process = (Process *) syscall.arguments[0]; process->pgid = syscall.arguments[1]; return 0; } break; } return -1; } }; #endif