diff --git a/Makefile b/Makefile index c1f25c69..0aa0f16f 100644 --- a/Makefile +++ b/Makefile @@ -162,8 +162,17 @@ release-all-archs: $(INITRD): sysroot mkdir -p `dirname $(INITRD)` echo -n > $(INITRD).filter + echo "exclude /boot" >> $(INITRD).filter + echo "exclude /dev" >> $(INITRD).filter + echo "exclude /next" >> $(INITRD).filter + echo "exclude /tmp" >> $(INITRD).filter + for OTHER_PLATFORM in $(OTHER_PLATFORMS); do \ + echo "exclude /$$OTHER_PLATFORM" >> $(INITRD).filter; \ + echo "exclude /etc/$$OTHER_PLATFORM" >> $(INITRD).filter; \ + echo "exclude /include/$$OTHER_PLATFORM" >> $(INITRD).filter; \ + done; if ! which mkinitrd; then echo You need to install mkinitrd; fi - mkinitrd --filter $(INITRD).filter "$(SYSROOT)/$(HOST)/bin" -o $(INITRD) + mkinitrd --filter $(INITRD).filter "$(SYSROOT)" -o $(INITRD) rm -f $(INITRD).filter .PHONY: initrd diff --git a/libc/Makefile b/libc/Makefile index a07ee066..5a0df496 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -144,6 +144,7 @@ fdio.o \ fileno.o \ fork.o \ fpipe.o \ +fstatat.o \ fstat.o \ ftruncate.o \ getc.o \ @@ -165,8 +166,8 @@ memstat.o \ mkdir.o \ mktemp.o \ on_exit.o \ -open.o \ openat.o \ +open.o \ pipe.o \ print.o \ putc.o \ diff --git a/libc/execvpe.cpp b/libc/execvpe.cpp index 9ceab427..01c5709a 100644 --- a/libc/execvpe.cpp +++ b/libc/execvpe.cpp @@ -26,6 +26,14 @@ #include #include +#if defined(PLATFORM_X86) + #define CPUTYPE_STR "i486-sortix" +#elif defined(PLATFORM_X64) + #define CPUTYPE_STR "x86_64-sortix" +#else + #error No cputype environmental variable provided here. +#endif + // Note that the only PATH variable in Sortix is the one used here. extern "C" int execvpe(const char* filename, char* const* argv, char* const* envp) @@ -33,7 +41,7 @@ extern "C" int execvpe(const char* filename, char* const* argv, if ( strchr(filename, '/') ) return execve(filename, argv, envp); size_t filenamelen = strlen(filename); - const char* PATH = "/bin"; + const char* PATH = "/" CPUTYPE_STR "/bin"; size_t pathlen = strlen(PATH); char* pathname = (char*) malloc(filenamelen + 1 + pathlen + 1); if ( !pathname ) { return -1; } diff --git a/libc/fddir-sortix.c b/libc/fddir-sortix.c index 7e5f34f7..f06daa48 100644 --- a/libc/fddir-sortix.c +++ b/libc/fddir-sortix.c @@ -22,6 +22,7 @@ *******************************************************************************/ +#include #include #include #include @@ -34,8 +35,8 @@ typedef struct fddir_sortix_struct { - struct sortix_dirent* dirent; - struct sortix_dirent* current; + struct kernel_dirent* dirent; + struct kernel_dirent* current; size_t direntsize; int fd; } fddir_sortix_t; @@ -44,18 +45,23 @@ int fddir_sortix_readents(fddir_sortix_t* info) { if ( !info->dirent ) { - // Allocate a buffer of at least sizeof(sortix_dirent). - info->direntsize = sizeof(struct sortix_dirent) + 4UL; + // Allocate a buffer of at least sizeof(kernel_dirent). + info->direntsize = sizeof(struct kernel_dirent) + 4UL; info->dirent = malloc(info->direntsize); - if ( !info->dirent ) { return -1; } + if ( !info->dirent ) + return -1; } - if ( readdirents(info->fd, info->dirent, info->direntsize) ) + if ( readdirents(info->fd, info->dirent, info->direntsize) < 0 ) { - if ( errno != ERANGE ) { return -1; } - size_t newdirentsize = info->dirent->d_namelen; - struct sortix_dirent* newdirent = malloc(newdirentsize); - if ( !newdirent ) { return -1; } + if ( errno != ERANGE ) + return -1; + size_t newdirentsize = sizeof(struct kernel_dirent) + info->dirent->d_namelen + 1; + if ( newdirentsize < info->direntsize ) + newdirentsize *= 2; + struct kernel_dirent* newdirent = malloc(newdirentsize); + if ( !newdirent ) + return -1; free(info->dirent); info->dirent = newdirent; info->direntsize = newdirentsize; @@ -70,7 +76,8 @@ int fddir_sortix_read(void* user, struct dirent* dirent, size_t* size) fddir_sortix_t* info = (fddir_sortix_t*) user; if ( !info->current ) { - if ( fddir_sortix_readents(info) ) { return -1; } + if ( fddir_sortix_readents(info) ) + return -1; info->current = info->dirent; } @@ -82,7 +89,7 @@ int fddir_sortix_read(void* user, struct dirent* dirent, size_t* size) dirent->d_reclen = needed; strcpy(dirent->d_name, info->current->d_name); - info->current = info->current->d_next; + info->current = kernel_dirent_next(info->current); return 0; } @@ -90,7 +97,7 @@ int fddir_sortix_read(void* user, struct dirent* dirent, size_t* size) int fddir_sortix_rewind(void* user) { fddir_sortix_t* info = (fddir_sortix_t*) user; - return lseek(info->fd, SEEK_SET, 0); + return lseek(info->fd, 0, SEEK_SET); } int fddir_sortix_fd(void* user) @@ -116,7 +123,9 @@ DIR* fdopendir(int fd) DIR* dir = dnewdir(); if ( !dir ) { free(info); return NULL; } - // TODO: Possibly set O_CLOEXEC on fd, as that's what GNU/Linux does. + int old_dflags = fcntl(fd, F_GETFD); + if ( 0 <= old_dflags ) + fcntl(fd, F_SETFD, old_dflags | O_CLOEXEC); info->fd = fd; @@ -131,7 +140,7 @@ DIR* fdopendir(int fd) DIR* opendir(const char* path) { - int fd = open(path, O_SEARCH | O_DIRECTORY | O_CLOEXEC); + int fd = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC); if ( fd < 0 ) { return NULL; } DIR* dir = fdopendir(fd); if ( !dir ) { close(fd); return NULL; } diff --git a/libc/fstatat.cpp b/libc/fstatat.cpp new file mode 100644 index 00000000..699a62f3 --- /dev/null +++ b/libc/fstatat.cpp @@ -0,0 +1,33 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + fstatat.cpp + Retrieves status of an open file. + +*******************************************************************************/ + +#include +#include + +DEFN_SYSCALL4(int, sys_fstatat, SYSCALL_FSTATAT, int, const char*, struct stat*, int); + +extern "C" int fstatat(int dirfd, const char* path, struct stat* buf, int flags) +{ + return sys_fstatat(dirfd, path, buf, flags); +} diff --git a/libc/getcwd.cpp b/libc/getcwd.cpp index b6684d81..0670bd90 100644 --- a/libc/getcwd.cpp +++ b/libc/getcwd.cpp @@ -22,12 +22,116 @@ *******************************************************************************/ -#include +#include +#include +#include +#include +#include +#include +#include #include -DEFN_SYSCALL2(char*, SysGetCWD, SYSCALL_GETCWD, char*, size_t); +static int dup_handles_cwd(int fd) +{ + if ( fd == AT_FDCWD ) + return open(".", O_RDONLY | O_DIRECTORY); + return dup(fd); +} + +static char* FindDirectoryEntryAt(int dirfd, ino_t inode, dev_t dev) +{ + int dupdirfd = dup_handles_cwd(dirfd); + if ( dupdirfd < 0 ) + return NULL; + DIR* dir = fdopendir(dupdirfd); + if ( !dir ) { close(dupdirfd); return NULL; } + struct dirent* entry; + while ( (entry = readdir(dir)) ) + { + if ( !strcmp(entry->d_name, "..") ) + continue; + struct stat st; + if ( fstatat(dupdirfd, entry->d_name, &st, 0) ) + continue; // Not ideal, but missing permissions, broken symlinks.. + if ( st.st_ino == inode && st.st_dev == dev ) + { + char* result = strdup(entry->d_name); + closedir(dir); + return result; + } + } + closedir(dir); + errno = ENOENT; + return NULL; +} + +extern "C" char* get_current_dir_name(void) +{ + int fd; + int parent; + struct stat fdst; + struct stat parentst; + size_t retlen = 0; + size_t newretlen; + char* ret = NULL; + char* newret; + char* elem; + + fd = open(".", O_RDONLY | O_DIRECTORY); + if ( fd < 0 ) + goto cleanup_done; + if ( fstat(fd, &fdst) ) + goto cleanup_fd; +next_parent: + parent = openat(fd, "..", O_RDONLY | O_DIRECTORY); + if ( parent < 0 ) + goto cleanup_fd; + if ( fstat(parent, &parentst) ) + goto cleanup_parent; + if ( fdst.st_ino == parentst.st_ino && + fdst.st_dev == parentst.st_dev ) + { + close(fd); + close(parent); + return ret ? ret : strdup("/"); + } + elem = FindDirectoryEntryAt(parent, fdst.st_ino, fdst.st_dev); + if ( !elem ) + goto cleanup_parent; + newretlen = 1 + strlen(elem) + retlen; + newret = (char*) malloc(sizeof(char) * (newretlen + 1)); + if ( !newret ) + goto cleanup_elem; + stpcpy(stpcpy(stpcpy(newret, "/"), elem), ret ? ret : ""); + free(elem); + free(ret); + ret = newret; + retlen = newretlen; + close(fd); + fd = parent; + fdst = parentst; + goto next_parent; + +cleanup_elem: + free(elem); +cleanup_parent: + close(parent); +cleanup_fd: + close(fd); +cleanup_done: + free(ret); + return NULL; +} extern "C" char* getcwd(char* buf, size_t size) { - return SysGetCWD(buf, size); + char* cwd = get_current_dir_name(); + if ( !buf ) + return cwd; + if ( !cwd ) + return NULL; + if ( size < strlen(cwd)+1 ) { free(cwd); errno = ERANGE; return NULL; } + strcpy(buf, cwd); + free(cwd); + return buf; } diff --git a/libc/include/fcntl.h b/libc/include/fcntl.h index 728ab09c..2b0ea83e 100644 --- a/libc/include/fcntl.h +++ b/libc/include/fcntl.h @@ -59,9 +59,9 @@ struct flock int fcntl(int fd, int cmd, ...); int open(const char* path, int oflag, ...); +int openat(int fd, const char* path, int oflag, ...); #if defined(__SORTIX_SHOW_UNIMPLEMENTED) int creat(const char* path, mode_t mode); -int openat(int fd, const char* path, int oflag, ...); #endif __END_DECLS diff --git a/libc/include/sys/readdirents.h b/libc/include/sys/readdirents.h index fd69d5e0..0027b9e4 100644 --- a/libc/include/sys/readdirents.h +++ b/libc/include/sys/readdirents.h @@ -26,21 +26,16 @@ #define _SYS_READDIRENTS_H 1 #include +#include +#include +#include +#include __BEGIN_DECLS @include(size_t.h) -// Keep this up to date with -struct sortix_dirent -{ - struct sortix_dirent* d_next; - unsigned char d_type; - size_t d_namelen; - char d_name[]; -}; - -int readdirents(int fd, struct sortix_dirent* dirent, size_t size); +ssize_t readdirents(int fd, struct kernel_dirent* dirent, size_t size); __END_DECLS diff --git a/libc/include/sys/stat.h b/libc/include/sys/stat.h index f37f60cc..064fa345 100644 --- a/libc/include/sys/stat.h +++ b/libc/include/sys/stat.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of the Sortix C Library. @@ -45,13 +45,16 @@ __END_DECLS #include __BEGIN_DECLS + int chmod(const char* path, mode_t mode); int fchmod(int fd, mode_t mode); int fstat(int fd, struct stat* st); +int fstatat(int dirfd, const char* path, struct stat* buf, int flags); int lstat(const char* restrict path, struct stat* restrict st); -int mkdir(const char *path, mode_t mode); +int mkdir(const char* path, mode_t mode); int stat(const char* restrict path, struct stat* restrict st); mode_t umask(mode_t mask); + __END_DECLS #endif diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 23a5b695..62f9c55e 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -165,6 +165,7 @@ int execvp(const char*, char* const []); pid_t fork(void); int ftruncate(int, off_t); char* getcwd(char*, size_t); +char* get_current_dir_name(void); pid_t getpid(void); pid_t getppid(void); int isatty(int); diff --git a/libc/lseek.cpp b/libc/lseek.cpp index 9cf888b1..7db6fda2 100644 --- a/libc/lseek.cpp +++ b/libc/lseek.cpp @@ -25,10 +25,9 @@ #include #include -DEFN_SYSCALL3(int, SysSeek, SYSCALL_SEEK, int, off_t*, int); +DEFN_SYSCALL3(off_t, sys_seek, SYSCALL_SEEK, int, off_t, int); extern "C" off_t lseek(int fd, off_t offset, int whence) { - SysSeek(fd, &offset, whence); - return offset; + return sys_seek(fd, offset, whence); } diff --git a/libc/read.cpp b/libc/read.cpp index 9c5069b0..38c9ba6e 100644 --- a/libc/read.cpp +++ b/libc/read.cpp @@ -26,14 +26,11 @@ #include #include -DEFN_SYSCALL3(ssize_t, SysRead, SYSCALL_READ, int, void*, size_t); +DEFN_SYSCALL3(ssize_t, sys_read, SYSCALL_READ, int, void*, size_t); extern "C" ssize_t read(int fd, void* buf, size_t count) { -retry: - ssize_t result = SysRead(fd, buf, count); - if ( result < 0 && errno == EAGAIN ) { goto retry; } - return result; + return sys_read(fd, buf, count); } DEFN_SYSCALL4(ssize_t, sys_pread, SYSCALL_PREAD, int, void*, size_t, off_t); diff --git a/libc/readdirents.cpp b/libc/readdirents.cpp index 3eaea262..a7b2da26 100644 --- a/libc/readdirents.cpp +++ b/libc/readdirents.cpp @@ -25,9 +25,9 @@ #include #include -DEFN_SYSCALL3(int, SysReadDirEnts, SYSCALL_READDIRENTS, int, struct sortix_dirent*, size_t); +DEFN_SYSCALL4(ssize_t, SysReadDirEnts, SYSCALL_READDIRENTS, int, struct kernel_dirent*, size_t, size_t); -extern "C" int readdirents(int fd, struct sortix_dirent* dirent, size_t size) +extern "C" ssize_t readdirents(int fd, struct kernel_dirent* dirent, size_t size) { - return SysReadDirEnts(fd, dirent, size); + return SysReadDirEnts(fd, dirent, size, 1); } diff --git a/libc/write.cpp b/libc/write.cpp index 92899a33..dd126423 100644 --- a/libc/write.cpp +++ b/libc/write.cpp @@ -26,14 +26,11 @@ #include #include -DEFN_SYSCALL3(ssize_t, SysWrite, SYSCALL_WRITE, int, const void*, size_t); +DEFN_SYSCALL3(ssize_t, sys_write, SYSCALL_WRITE, int, const void*, size_t); extern "C" ssize_t write(int fd, const void* buf, size_t count) { -retry: - ssize_t result = SysWrite(fd, buf, count); - if ( result < 0 && errno == EAGAIN ) { goto retry; } - return result; + return sys_write(fd, buf, count); } DEFN_SYSCALL4(ssize_t, sys_pwrite, SYSCALL_PWRITE, int, const void*, size_t, off_t); diff --git a/sortix/Makefile b/sortix/Makefile index aa79b206..8132f66f 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -74,36 +74,34 @@ ata.o \ bga.o \ calltrace.o \ com.o \ +copy.o \ $(CPU)/calltrace.o \ $(CPU)/kthread.o \ crc32.o \ -descriptors.o \ -device.o \ -directory.o \ +descriptor.o \ dispmsg.o \ +dtable.o \ elf.o \ -filesystem.o \ -fs/devfs.o \ -fs/initfs.o \ -fs/ramfs.o \ +fsfunc.o \ +fs/kram.o \ fs/util.o \ -fs/videofs.o \ initrd.o \ +inode.o \ interlock.o \ interrupt.o \ +ioctx.o \ io.o \ kb/layout/us.o \ kb/ps2.o \ kernelinfo.o \ kernel.o \ -keyboard.o \ kthread.o \ lfbtextbuffer.o \ linebuffer.o \ log.o \ logterminal.o \ memorymanagement.o \ -mount.o \ +mtable.o \ panic.o \ pci.o \ pipe.o \ @@ -115,7 +113,6 @@ signal.o \ sound.o \ string.o \ syscall.o \ -terminal.o \ textbuffer.o \ textterminal.o \ thread.o \ @@ -125,6 +122,7 @@ utf8.o \ vga.o \ vgatextbuffer.o \ video.o \ +vnode.o \ worker.o \ ALLOBJS=\ diff --git a/sortix/ata.cpp b/sortix/ata.cpp index c1a5152b..0d201002 100644 --- a/sortix/ata.cpp +++ b/sortix/ata.cpp @@ -23,12 +23,18 @@ *******************************************************************************/ #include +#include #include -#include "cpu.h" +#include +#include +#include +#include +#include +#include #include #include #include "ata.h" -#include "fs/devfs.h" +#include "cpu.h" // TODO: Use the PCI to detect ATA devices instead of relying on them being on // standard locations. @@ -62,27 +68,126 @@ const uint8_t CTL_RESET = (1<<2); namespace ATA { -void DetectDrive(unsigned busid, ATABus* bus, unsigned driveid) +class ATANode : public AbstractInode +{ +public: + ATANode(ATADrive* drive, uid_t owner, gid_t group, mode_t mode, dev_t dev, + ino_t ino); + virtual ~ATANode(); + virtual int sync(ioctx_t* ctx); + virtual int truncate(ioctx_t* ctx, off_t length); + virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); + virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, + off_t off); + virtual ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, + off_t off); + +private: + kthread_mutex_t filelock; + ATADrive* drive; + +}; + +ATANode::ATANode(ATADrive* drive, uid_t owner, gid_t group, mode_t mode, + dev_t dev, ino_t /*ino*/) +{ + inode_type = INODE_TYPE_FILE; + filelock = KTHREAD_MUTEX_INITIALIZER; + this->dev = dev; + this->ino = (ino_t) this; + this->stat_uid = owner; + this->stat_gid = group; + this->type = S_IFBLK; + this->stat_size = (off_t) drive->GetSize(); + this->stat_mode = (mode & S_SETABLE) | this->type; + this->stat_blksize = (blksize_t) drive->GetSectorSize(); + this->stat_blocks = (blkcnt_t) drive->GetNumSectors(); + this->drive = drive; +} + +ATANode::~ATANode() +{ + delete drive; +} + +int ATANode::sync(ioctx_t* /*ctx*/) +{ + // TODO: Actually sync the device here! + return 0; +} + +int ATANode::truncate(ioctx_t* /*ctx*/, off_t length) +{ + ScopedLock lock(&filelock); + if ( length == drive->GetSize() ) { return 0; } + errno = EPERM; + return -1; +} + +off_t ATANode::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) +{ + ScopedLock lock(&filelock); + if ( whence == SEEK_SET ) + return offset; + if ( whence == SEEK_END ) + return (off_t) drive->GetSize() + offset; + errno = EINVAL; + return -1; +} + +ssize_t ATANode::pread(ioctx_t* /*ctx*/, uint8_t* buf, size_t count, off_t off) +{ + // TODO: SECURITY: Use ioctx copy functions to copy or we have a serious + // security hole if invoked from user-space! + size_t numbytes = drive->Read(off, buf, count); + if ( numbytes < count ) + return -1; + return (ssize_t) numbytes; +} + +ssize_t ATANode::pwrite(ioctx_t* /*ctx*/, const uint8_t* buf, size_t count, + off_t off) +{ + // TODO: SECURITY: Use ioctx copy functions to copy or we have a serious + // security hole if invoked from user-space! + size_t numbytes = drive->Write(off, buf, count); + if ( numbytes < count ) + return -1; + return (ssize_t) numbytes; +} + +void DetectDrive(const char* devpath, Ref slashdev, unsigned busid, + ATABus* bus, unsigned driveid) { unsigned ataid = busid*2 + driveid; ATADrive* drive = bus->Instatiate(driveid); - if ( !drive ) { return; } - DeviceFS::RegisterATADrive(ataid, drive); + if ( !drive ) + return; + Ref node(new ATANode(drive, 0, 0, 0660, slashdev->dev, 0)); + if ( !node ) + Panic("Unable to allocate memory for ATA drive inode."); + const size_t NAMELEN = 64; + char name[NAMELEN]; + snprintf(name, NAMELEN, "ata%u", ataid); + ioctx_t ctx; SetupKernelIOCtx(&ctx); + if ( LinkInodeInDir(&ctx, slashdev, name, node) != 0 ) + PanicF("Unable to link %s/%s to ATA driver inode.", devpath, name); } -void DetectBus(unsigned busid, uint16_t ioport, uint16_t altio) +void DetectBus(const char* devpath, Ref slashdev, unsigned busid, + uint16_t ioport, uint16_t altio) { ATABus* bus = ATA::CreateBus(ioport, altio); if ( !bus ) return; - DetectDrive(busid, bus, 0); - DetectDrive(busid, bus, 1); + DetectDrive(devpath, slashdev, busid, bus, 0); + DetectDrive(devpath, slashdev, busid, bus, 1); } -void Init() +void Init(const char* devpath, Ref slashdev) { - DetectBus(0, 0x1F0, 0x3F6); - DetectBus(1, 0x170, 0x366); + DetectBus(devpath, slashdev, 0, 0x1F0, 0x3F6); + DetectBus(devpath, slashdev, 1, 0x170, 0x366); } ATABus* CreateBus(uint16_t portoffset, uint16_t altport) diff --git a/sortix/ata.h b/sortix/ata.h index 08866bed..2363030e 100644 --- a/sortix/ata.h +++ b/sortix/ata.h @@ -26,9 +26,11 @@ #define SORTIX_ATA_H #include +#include namespace Sortix { +class Descriptor; class ATABus; class ATADrive; @@ -83,7 +85,7 @@ private: namespace ATA { -void Init(); +void Init(const char* devpath, Ref slashdev); ATABus* CreateBus(uint16_t portoffset, uint16_t altport); } // namespace ATA diff --git a/sortix/com.cpp b/sortix/com.cpp index 6e925de3..e142234a 100644 --- a/sortix/com.cpp +++ b/sortix/com.cpp @@ -24,13 +24,16 @@ #include #include +#include +#include +#include +#include +#include +#include #include #include "interrupt.h" -#include "stream.h" -#include "syscall.h" #include "thread.h" #include "signal.h" -#include "fs/devfs.h" #include "com.h" namespace Sortix { @@ -51,13 +54,9 @@ namespace COM { // Yet another alternative is to use POLL_HACK, but return EGAIN and let user- // space call retry, rather than relying on the broken syscall interstructure. -#ifndef GOT_ACTUAL_KTHREAD #define POLL_EAGAIN 1 -#else -#define POLL_EAGAIN 0 -#endif -#if !POLL_EAGAIN && !POLL_HACK && defined(GOT_ACTUAL_KTHREAD) +#if !POLL_EAGAIN && !POLL_HACK #error The interrupt-based code was broken in the kthread branch. #error You need to port this to the new thread/interrupt API. #warning Oh, and fix the above mentioned bugs too. @@ -211,133 +210,97 @@ void EarlyInit() } } -class DevCOMPort : public DevStream +class DevCOMPort : public AbstractInode { public: - typedef DevStream BaseClass; - -public: - DevCOMPort(uint16_t port); + DevCOMPort(dev_t dev, uid_t owner, gid_t group, mode_t mode, uint16_t port); virtual ~DevCOMPort(); - -public: - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); + virtual int sync(ioctx_t* ctx); + virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); + virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); public: void OnInterrupt(); private: - uint16_t port; kthread_mutex_t portlock; -#ifdef GOT_FAKE_KTHREAD - Event dataevent; - Event sentevent; -#endif + uint16_t port; }; -DevCOMPort::DevCOMPort(uint16_t port) +DevCOMPort::DevCOMPort(dev_t dev, uid_t owner, gid_t group, mode_t mode, + uint16_t port) { + inode_type = INODE_TYPE_STREAM; this->port = port; this->portlock = KTHREAD_MUTEX_INITIALIZER; + this->stat_uid = owner; + this->stat_gid = group; + this->type = S_IFCHR; + this->stat_mode = (mode & S_SETABLE) | this->type; + this->dev = dev; + this->ino = (ino_t) this; } DevCOMPort::~DevCOMPort() { } -bool DevCOMPort::IsReadable() { return true; } -bool DevCOMPort::IsWritable() { return true; } +int DevCOMPort::sync(ioctx_t* /*ctx*/) +{ + // TODO: Not implemented yet, please wait for all outstanding requests. + return 0; +} #if POLL_HACK -const unsigned TRIES = 1000; - -ssize_t DevCOMPort::Read(uint8_t* dest, size_t count) +ssize_t DevCOMPort::read(ioctx_t* ctx, uint8_t* dest, size_t count) { if ( !count ) { return 0; } if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } ScopedLock lock(&portlock); -#ifdef GOT_ACTUAL_KTHREAD while ( !(CPU::InPortB(port + LSR) & LSR_READY) ) if ( Signal::IsPending() ) { errno = EINTR; return -1; } -#else - uint8_t lsr; - for ( unsigned i = 0; i < TRIES; i++ ) - { - lsr = CPU::InPortB(port + LSR); - if ( lsr & LSR_READY ) { break; } - } - - if ( !(lsr & LSR_READY) ) - { -#if POLL_EAGAIN - errno = EAGAIN; -#else - errno = EBLOCKING; - Syscall::Yield(); -#endif - return -1; - } -#endif size_t sofar = 0; do { if ( count <= sofar ) { break; } - dest[sofar++] = CPU::InPortB(port + RXR); + uint8_t val = CPU::InPortB(port + RXR); + if ( !ctx->copy_to_dest(dest + sofar++, &val, sizeof(val)) ) + return -1; } while ( CPU::InPortB(port + LSR) & LSR_READY); return sofar; } -ssize_t DevCOMPort::Write(const uint8_t* src, size_t count) +ssize_t DevCOMPort::write(ioctx_t* ctx, const uint8_t* src, size_t count) { if ( !count ) { return 0; } if ( SSIZE_MAX < count ) { count = SSIZE_MAX; }; ScopedLock lock(&portlock); -#ifdef GOT_ACTUAL_KTHREAD while ( !(CPU::InPortB(port + LSR) & LSR_THRE) ) if ( Signal::IsPending() ) { errno = EINTR; return -1; } -#else - uint8_t lsr; - for ( unsigned i = 0; i < TRIES; i++ ) - { - lsr = CPU::InPortB(port + LSR); - if ( lsr & LSR_THRE ) { break; } - } - - if ( !(lsr & LSR_THRE) ) - { -#if POLL_EAGAIN - errno = EAGAIN; -#else - errno = EBLOCKING; - Syscall::Yield(); -#endif - return -1; - } -#endif size_t sofar = 0; do { if ( count <= sofar ) { break; } - CPU::OutPortB(port + TXR, src[sofar++]); + uint8_t val; + if ( !ctx->copy_from_src(&val, src + sofar++, sizeof(val)) ) + return -1; + CPU::OutPortB(port + TXR, val); } while ( CPU::InPortB(port + LSR) & LSR_THRE ); return sofar; @@ -345,7 +308,9 @@ ssize_t DevCOMPort::Write(const uint8_t* src, size_t count) #else -ssize_t DevCOMPort::Read(uint8_t* dest, size_t count) +#error Yeah, please port these to the new IO interface. + +ssize_t DevCOMPort::Read(byte* dest, size_t count) { if ( !count ) { return 0; } if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } @@ -355,12 +320,8 @@ ssize_t DevCOMPort::Read(uint8_t* dest, size_t count) uint8_t lsr = CPU::InPortB(port + LSR); if ( !(lsr & LSR_READY) ) { -#ifdef GOT_ACTUAL_KTHREAD Panic("Can't wait for com data receive event"); -#else - dataevent.Register(); -#endif - errno = EBLOCKING; + Error::Set(EBLOCKING); return -1; } @@ -384,12 +345,8 @@ ssize_t DevCOMPort::Write(const uint8_t* src, size_t count) uint8_t lsr = CPU::InPortB(port + LSR); if ( !(lsr & LSR_THRE) ) { -#ifdef GOT_ACTUAL_KTHREAD Panic("Can't wait for com data sent event"); -#else - sentevent.Register(); -#endif - errno = EBLOCKING; + Error::Set(EBLOCKING); return -1; } @@ -424,18 +381,10 @@ void DevCOMPort::OnInterrupt() CPU::InPortB(port + LSR); break; case IIR_RECV_DATA: -#ifdef GOT_ACTUAL_KTHREAD Panic("Can't wait for com data sent event"); -#else - dataevent.Signal(); -#endif break; case IIR_SENT_DATA: -#ifdef GOT_ACTUAL_KTHREAD Panic("Can't wait for com data sent event"); -#else - sentevent.Signal(); -#endif CPU::InPortB(port + IIR); break; case IIR_MODEM_STATUS: @@ -444,7 +393,7 @@ void DevCOMPort::OnInterrupt() } } -DevCOMPort* comdevices[1+NUMCOMPORTS]; +Ref comdevices[1+NUMCOMPORTS]; static void UARTIRQHandler(CPU::InterruptRegisters* /*regs*/, void* /*user*/) { @@ -455,12 +404,14 @@ static void UARTIRQHandler(CPU::InterruptRegisters* /*regs*/, void* /*user*/) } } -void Init() +void Init(const char* devpath, Ref slashdev) { + ioctx_t ctx; SetupKernelIOCtx(&ctx); for ( size_t i = 1; i <= NUMCOMPORTS; i++ ) { - if ( !comports[i] ) { comdevices[i] = NULL; continue; } - comdevices[i] = new DevCOMPort(comports[i]); + if ( !comports[i] ) { comdevices[i] = Ref(); continue; } + comdevices[i] = Ref + (new DevCOMPort(slashdev->dev, 0, 0, 0660, comports[i])); if ( !comdevices[i] ) { PanicF("Unable to allocate device for COM port %zu at 0x%x", i, @@ -468,10 +419,8 @@ void Init() } char name[5] = "comN"; name[3] = '0' + i; - if ( !DeviceFS::RegisterDevice(name, comdevices[i]) ) - { - PanicF("Unable to register device /dev/%s", name); - } + if ( LinkInodeInDir(&ctx, slashdev, name, comdevices[i]) != 0 ) + PanicF("Unable to link %s/%s to COM port driver.", devpath, name); } Interrupt::RegisterHandler(Interrupt::IRQ3, UARTIRQHandler, NULL); diff --git a/sortix/com.h b/sortix/com.h index 2d8a8c6a..7400b1bf 100644 --- a/sortix/com.h +++ b/sortix/com.h @@ -26,12 +26,16 @@ #define SORTIX_COM_H namespace Sortix { + +class Descriptor; + namespace COM { void EarlyInit(); -void Init(); +void Init(const char* devpath, Ref slashdev); } // namespace COM + } // namespace Sortix #endif diff --git a/sortix/copy.cpp b/sortix/copy.cpp new file mode 100644 index 00000000..70c8603d --- /dev/null +++ b/sortix/copy.cpp @@ -0,0 +1,65 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + copy.h + The context for io operations: who made it, how should data be copied, etc. + +*******************************************************************************/ + +#include +#include +#include +#include + +namespace Sortix { + +// TODO: These are currently insecure, please check userspace tables before +// moving data to avoid security problems. + +bool CopyToUser(void* userdst, const void* ksrc, size_t count) +{ + memcpy(userdst, ksrc, count); + return true; +} + +bool CopyFromUser(void* kdst, const void* usersrc, size_t count) +{ + //Log::PrintF("[copy.cpp] Copying %zu bytes from 0x%zx to 0x%zx\n", count, usersrc, kdst); + memcpy(kdst, usersrc, count); + return true; +} + +bool CopyToKernel(void* kdst, const void* ksrc, size_t count) +{ + memcpy(kdst, ksrc, count); + return true; +} + +bool CopyFromKernel(void* kdst, const void* ksrc, size_t count) +{ + memcpy(kdst, ksrc, count); + return true; +} + +char* GetStringFromUser(const char* str) +{ + return String::Clone(str); +} + +} // namespace Sortix diff --git a/sortix/descriptor.cpp b/sortix/descriptor.cpp new file mode 100644 index 00000000..ce2b7078 --- /dev/null +++ b/sortix/descriptor.cpp @@ -0,0 +1,419 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + descriptor.cpp + A file descriptor. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // DEBUG +#include +#include +#include +#include +#include +#include +#include +#include "process.h" + +namespace Sortix { + +bool LinkInodeInDir(ioctx_t* ctx, Ref dir, const char* name, + Ref inode) +{ + Ref vnode(new Vnode(inode, Ref(), 0, 0)); + if ( !vnode ) return false; + Ref desc(new Descriptor(Ref(vnode), 0)); + if ( !desc ) return false; + return dir->link(ctx, name, desc) != 0; +} + +Ref OpenDirContainingPath(ioctx_t* ctx, Ref from, + const char* path, char** finalp) +{ + if ( !path[0] ) { errno = EINVAL; return Ref(); } + char* dirpath; + char* final; + if ( !SplitFinalElem(path, &dirpath, &final) ) + return Ref(); + // TODO: Removing trailing slashes in final may not the right thing. + size_t finallen = strlen(final); + while ( finallen && final[finallen-1] == '/' ) + final[--finallen] = 0; + // Safe against buffer overflow because final contains at least one + // character because we reject the empty string above. + if ( !finallen ) + final[0] = '.', + final[1] = '\0'; + if ( !dirpath[0] ) + { + delete[] dirpath; + *finalp = final; + return from; + } + Ref ret = from->open(ctx, dirpath, O_RDONLY | O_DIRECTORY, 0); + delete[] dirpath; + if ( !ret ) { delete[] final; return Ref(); } + *finalp = final; + return ret; +} + +// TODO: Add security checks. + +Descriptor::Descriptor(Ref vnode, int dflags) +{ + curofflock = KTHREAD_MUTEX_INITIALIZER; + this->vnode = vnode; + this->ino = vnode->ino; + this->dev = vnode->dev; + this->type = vnode->type; + this->dflags = dflags; + checked_seekable = false; + curoff = 0; +} + +Descriptor::~Descriptor() +{ +} + +Ref Descriptor::Fork() +{ + Ref ret(new Descriptor(vnode, dflags)); + if ( !ret ) + return Ref(); + ret->curoff = curoff; + ret->checked_seekable = checked_seekable; + ret->seekable = seekable; + return ret; +} + +bool Descriptor::IsSeekable() +{ + if ( !checked_seekable ) + { + // TODO: Is this enough? Check that errno happens to be ESPIPE? + ioctx_t ctx; SetupKernelIOCtx(&ctx); + seekable = 0 <= vnode->lseek(&ctx, SEEK_SET, 0) || S_ISDIR(vnode->type); + checked_seekable = true; + } + return seekable; +} + +int Descriptor::sync(ioctx_t* ctx) +{ + return vnode->sync(ctx); +} + +int Descriptor::stat(ioctx_t* ctx, struct stat* st) +{ + return vnode->stat(ctx, st); +} + +int Descriptor::chmod(ioctx_t* ctx, mode_t mode) +{ + return vnode->chmod(ctx, mode); +} + +int Descriptor::chown(ioctx_t* ctx, uid_t owner, gid_t group) +{ + if ( owner < 0 || group < 0 ) { errno = EINVAL; return -1; } + return vnode->chown(ctx, owner, group); +} + +int Descriptor::truncate(ioctx_t* ctx, off_t length) +{ + if ( length < 0 ) { errno = EINVAL; return -1; } + return vnode->truncate(ctx, length); +} + +off_t Descriptor::lseek(ioctx_t* ctx, off_t offset, int whence) +{ + if ( !IsSeekable() ) + return vnode->lseek(ctx, offset, whence); + ScopedLock lock(&curofflock); + off_t reloff; + if ( whence == SEEK_SET ) + reloff = 0; + else if ( whence == SEEK_CUR ) + reloff = curoff; + else if ( whence == SEEK_END ) + { + if ( (reloff = vnode->lseek(ctx, offset, SEEK_END)) < 0 ) + return -1; + } + else + return errno = EINVAL, -1; + + if ( offset < 0 && reloff + offset < 0 ) + return errno = EOVERFLOW, -1; + if ( OFF_MAX - curoff < offset ) + return errno = EOVERFLOW, -1; + + return curoff = reloff + offset; +} + +ssize_t Descriptor::read(ioctx_t* ctx, uint8_t* buf, size_t count) +{ + if ( !count ) { return 0; } + if ( (size_t) SSIZE_MAX < count ) { count = SSIZE_MAX; } + if ( !IsSeekable() ) + return vnode->read(ctx, buf, count); + // TODO: Locking here only allows one task to read/write at once. + ScopedLock lock(&curofflock); + ssize_t ret = vnode->pread(ctx, buf, count, curoff); + if ( 0 <= ret ) + curoff += ret; + return ret; +} + +ssize_t Descriptor::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off) +{ + if ( off < 0 ) { errno = EINVAL; return -1; } + if ( !count ) { return 0; } + if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } + return vnode->pread(ctx, buf, count, off); +} + +ssize_t Descriptor::write(ioctx_t* ctx, const uint8_t* buf, size_t count) +{ + if ( !count ) { return 0; } + if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } + if ( !IsSeekable() ) + return vnode->write(ctx, buf, count); + // TODO: Locking here only allows one task to read/write at once. + ScopedLock lock(&curofflock); + // TODO: What if lseek fails? Sets curoff = -1, which we forward to vnodes + // and we are not allowed to do that! + if ( dflags & O_APPEND ) + curoff = vnode->lseek(ctx, 0, SEEK_END); + ssize_t ret = vnode->pwrite(ctx, buf, count, curoff); + if ( 0 <= ret ) + curoff += ret; + return ret; +} + +ssize_t Descriptor::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off) +{ + if ( off < 0 ) { errno = EINVAL; return -1; } + if ( !count ) { return 0; } + if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } + return vnode->pwrite(ctx, buf, count, off); +} + +int Descriptor::utimes(ioctx_t* ctx, const struct timeval times[2]) +{ + return vnode->utimes(ctx, times); +} + +int Descriptor::isatty(ioctx_t* ctx) +{ + return vnode->isatty(ctx); +} + +ssize_t Descriptor::readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, + size_t size, size_t maxcount) +{ + if ( !maxcount ) { return 0; } + if ( SSIZE_MAX < size ) { size = SSIZE_MAX; } + if ( size < sizeof(*dirent) ) { errno = EINVAL; return -1; } + // TODO: Locking here only allows one task to read/write at once. + ScopedLock lock(&curofflock); + ssize_t ret = vnode->readdirents(ctx, dirent, size, curoff, maxcount); + if ( ret == 0 ) + { + const char* name = ""; + size_t namelen = strlen(name); + size_t needed = sizeof(*dirent) + namelen + 1; + struct kernel_dirent retdirent; + memset(&retdirent, 0, sizeof(retdirent)); + retdirent.d_reclen = needed; + retdirent.d_off = 0; + retdirent.d_namelen = namelen; + if ( !ctx->copy_to_dest(dirent, &retdirent, sizeof(retdirent)) ) + return -1; + if ( size < needed ) + return errno = ERANGE, -1; + if ( !ctx->copy_to_dest(dirent->d_name, name, namelen+1) ) + return -1; + return needed; + } + // TODO: Accessing data here is dangerous if it is userspace: + if ( 0 < ret ) + for ( ; dirent; curoff++, dirent = kernel_dirent_next(dirent) ); + return ret; +} + +Ref Descriptor::open(ioctx_t* ctx, const char* filename, int flags, + mode_t mode) +{ + if ( !filename[0] ) + return errno = ENOENT, Ref(); + // O_DIRECTORY makes us only open directories. It is therefore a logical + // error to provide flags that only makes sense for non-directory. We also + // filter reject them early to prevent O_TRUNC | O_DIRECTORY from opening a + // file, truncating it, and then aborting the open with an error. + if ( (flags & (O_CREAT | O_TRUNC)) && (flags & (O_DIRECTORY)) ) + return errno = EINVAL, Ref(); + Ref desc(this); + while ( filename[0] ) + { + if ( filename[0] == '/' ) + { + if ( !S_ISDIR(desc->type) ) + return errno = ENOTDIR, Ref(); + filename++; + continue; + } + size_t slashpos = strcspn(filename, "/"); + bool lastelem = filename[slashpos] == '\0'; + char* elem = String::Substring(filename, 0, slashpos); + if ( !elem ) + return Ref(); + int open_flags = lastelem ? flags : O_RDONLY; + // Forward O_DIRECTORY so the operation can fail earlier. If it doesn't + // fail and we get a non-directory in return, we'll handle it on exit + // with no consequences (since opening an existing file is harmless). + open_flags &= ~(0 /*| O_DIRECTORY*/); + mode_t open_mode = lastelem ? mode : 0; + // TODO: O_NOFOLLOW. + Ref next = desc->open_elem(ctx, elem, open_flags, open_mode); + delete[] elem; + if ( !next ) + return Ref(); + desc = next; + filename += slashpos; + } + if ( flags & O_DIRECTORY && !S_ISDIR(desc->type) ) + return errno = ENOTDIR, Ref(); + + // TODO: The new file descriptor may not be opened with the correct + // permissions in the below case! + // If the path only contains slashes, we'll get outselves back, be sure to + // get ourselves back. + return desc == this ? Fork() : desc; +} + +Ref Descriptor::open_elem(ioctx_t* ctx, const char* filename, + int flags, mode_t mode) +{ + assert(!strchr(filename, '/')); + int next_flags = flags & ~(O_APPEND | O_CLOEXEC); + Ref retvnode = vnode->open(ctx, filename, next_flags, mode); + if ( !retvnode ) + return Ref(); + Ref ret(new Descriptor(retvnode, flags & O_APPEND)); + if ( !ret ) + return Ref(); + if ( (flags & O_TRUNC) && S_ISREG(ret->type) ) + if ( (flags & O_ACCMODE) == O_WRONLY || + (flags & O_ACCMODE) == O_RDWR ) + ret->truncate(ctx, 0); + return ret; +} + +int Descriptor::mkdir(ioctx_t* ctx, const char* filename, mode_t mode) +{ + char* final; + Ref dir = OpenDirContainingPath(ctx, Ref(this), + filename, &final); + if ( !dir ) + return -1; + int ret = dir->vnode->mkdir(ctx, final, mode); + delete[] final; + return ret; +} + +int Descriptor::link(ioctx_t* ctx, const char* filename, Ref node) +{ + char* final; + Ref dir = OpenDirContainingPath(ctx, Ref(this), + filename, &final); + if ( !dir ) + return -1; + int ret = dir->vnode->link(ctx, final, node->vnode); + delete[] final; + return ret; +} + +int Descriptor::unlink(ioctx_t* ctx, const char* filename) +{ + char* final; + Ref dir = OpenDirContainingPath(ctx, Ref(this), + filename, &final); + if ( !dir ) + return -1; + int ret = dir->vnode->unlink(ctx, final); + delete[] final; + return ret; +} + +int Descriptor::rmdir(ioctx_t* ctx, const char* filename) +{ + char* final; + Ref dir = OpenDirContainingPath(ctx, Ref(this), + filename, &final); + if ( !dir ) + return -1; + int ret = dir->vnode->rmdir(ctx, final); + delete[] final; + return ret; +} + +int Descriptor::symlink(ioctx_t* ctx, const char* oldname, const char* filename) +{ + char* final; + Ref dir = OpenDirContainingPath(ctx, Ref(this), + filename, &final); + if ( !dir ) + return -1; + int ret = dir->vnode->symlink(ctx, oldname, final); + delete[] final; + return ret; +} + +ssize_t Descriptor::readlink(ioctx_t* ctx, char* buf, size_t bufsize) +{ + return vnode->readlink(ctx, buf, bufsize); +} + +int Descriptor::tcgetwinsize(ioctx_t* ctx, struct winsize* ws) +{ + return vnode->tcgetwinsize(ctx, ws); +} + +int Descriptor::settermmode(ioctx_t* ctx, unsigned mode) +{ + return vnode->settermmode(ctx, mode); +} + +int Descriptor::gettermmode(ioctx_t* ctx, unsigned* mode) +{ + return vnode->gettermmode(ctx, mode); +} + +} // namespace Sortix diff --git a/sortix/descriptors.cpp b/sortix/descriptors.cpp deleted file mode 100644 index ab96c8b0..00000000 --- a/sortix/descriptors.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2012. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - descriptors.cpp - Handles file descriptors, socket descriptors, and whatnot for each process. - -********************************************************************************/ - -#include -#include -#include -#include -#include "descriptors.h" -#include "device.h" -#include - -namespace Sortix -{ - // When in doubt use brute-force. This class could easily be optimized. - - Device* const RESERVED_DEVICE = (Device*) 0x1UL; - - DescriptorTable::DescriptorTable() - { - numdevices = 0; - devices = NULL; - } - - DescriptorTable::~DescriptorTable() - { - Reset(); - } - - void DescriptorTable::Reset() - { - for ( int i = 0; i < numdevices; i++ ) - { - Device* dev = devices[i].dev; - if ( !dev || dev == RESERVED_DEVICE ) { continue; } - - dev->Unref(); - delete[] devices[i].path; - } - - delete[] devices; - devices = NULL; - numdevices = 0; - } - - int DescriptorTable::Allocate(Device* object, char* pathcopy) - { - for ( int i = 0; i < numdevices; i++ ) - { - if ( !devices[i].dev ) - { - object->Refer(); - devices[i].dev = object; - devices[i].flags = 0; - devices[i].path = pathcopy; - return i; - } - } - - int newlistlength = numdevices*2; - if ( newlistlength == 0 ) { newlistlength = 8; } - - DescriptorEntry* newlist = new DescriptorEntry[newlistlength]; - if ( newlist == NULL ) { return -1; } - - if ( devices != NULL ) - { - memcpy(newlist, devices, sizeof(*devices) * numdevices); - } - - for ( int i = numdevices; i < newlistlength; i++ ) - newlist[i].dev = NULL, - newlist[i].path = NULL, - newlist[i].flags = 0; - - delete[] devices; - - devices = newlist; - numdevices = newlistlength; - - return Allocate(object, pathcopy); - } - - void DescriptorTable::Free(int index) - { - assert(index < numdevices); - assert(devices[index].dev); - - if ( devices[index].dev != RESERVED_DEVICE ) - { - devices[index].dev->Unref(); - delete[] devices[index].path; - } - - devices[index].dev = NULL; - devices[index].path = NULL; - devices[index].flags = 0; - } - - int DescriptorTable::Reserve() - { - return Allocate(RESERVED_DEVICE, NULL); - } - - void DescriptorTable::UseReservation(int index, Device* object, char* pathcopy) - { - assert(index < index); - assert(devices[index].dev != NULL); - assert(devices[index].dev == RESERVED_DEVICE); - assert(devices[index].path == NULL); - - object->Refer(); - devices[index].dev = object; - devices[index].flags = 0; - devices[index].path = pathcopy; - } - - bool DescriptorTable::Fork(DescriptorTable* forkinto) - { - DescriptorEntry* newlist = new DescriptorEntry[numdevices]; - if ( !newlist ) { return false; } - - for ( int i = 0; i < numdevices; i++ ) - { - Device* dev = devices[i].dev; - int flags = devices[i].flags; - char* path = devices[i].path; - if ( !dev || dev == RESERVED_DEVICE ) - path = NULL; - path = path ? String::Clone(path) : NULL; - if ( flags & FD_CLOFORK ) { dev = NULL; flags = 0; } - newlist[i].dev = dev; - newlist[i].flags = flags; - if ( !dev || dev == RESERVED_DEVICE ) { continue; } - newlist[i].dev->Refer(); - newlist[i].path = path; - } - - assert(!forkinto->devices); - - forkinto->devices = newlist; - forkinto->numdevices = numdevices; - - return true; - } - - void DescriptorTable::OnExecute() - { - for ( int i = 0; i < numdevices; i++ ) - { - if ( devices[i].flags & FD_CLOEXEC ) { Free(i); } - } - } - - void DescriptorTable::SetFlags(int index, int flags) - { - assert(0 <= index && index < numdevices); - assert(devices[index].dev); - devices[index].flags = flags; - } - - int DescriptorTable::GetFlags(int index) - { - assert(0 <= index && index < numdevices); - assert(devices[index].dev); - return devices[index].flags; - } - - const char* DescriptorTable::GetPath(int index) - { - assert(0 <= index && index < numdevices); - assert(devices[index].dev); - return devices[index].path; - } -} diff --git a/sortix/descriptors.h b/sortix/descriptors.h deleted file mode 100644 index 51575be7..00000000 --- a/sortix/descriptors.h +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2012. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - descriptors.h - Handles file descriptors, socket descriptors, and whatnot for each process. - -*******************************************************************************/ - -#ifndef SORTIX_DESCRIPTORS_H -#define SORTIX_DESCRIPTORS_H - -namespace Sortix -{ - class Device; - - struct DescriptorEntry - { - Device* dev; - int flags; - char* path; - }; - - class DescriptorTable - { - public: - DescriptorTable(); - ~DescriptorTable(); - - private: - int numdevices; - DescriptorEntry* devices; - - public: - int Allocate(Device* object, char* pathcopy); - int Reserve(); - void Free(int index); - void UseReservation(int index, Device* object, char* pathcopy); - bool Fork(DescriptorTable* forkinto); - void OnExecute(); - void Reset(); - void SetFlags(int index, int flags); - int GetFlags(int index); - const char* GetPath(int index); - - public: - inline Device* Get(int index) - { - if ( !devices ) { return NULL; } - if ( index < 0 || numdevices <= index ) { return NULL; } - return devices[index].dev; - } - - }; -} - -#endif diff --git a/sortix/directory.cpp b/sortix/directory.cpp deleted file mode 100644 index 555fd7e2..00000000 --- a/sortix/directory.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - directory.cpp - Allows access to stored sequences of bytes in an orderly fashion. - -*******************************************************************************/ - -#include -#include -#include -#include -#include -#include "syscall.h" -#include "process.h" -#include "device.h" -#include "directory.h" -#include "filesystem.h" -#include "mount.h" - -namespace Sortix -{ - namespace Directory - { - int SysReadDirEnts(int fd, sortix_dirent* dirent, size_t size) - { - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - if ( !dev->IsType(Device::DIRECTORY) ) { errno = EBADF; return -1; } - DevDirectory* dir = (DevDirectory*) dev; - - sortix_dirent* prev = NULL; - - while ( true ) - { - // Check if we got enough bytes left to tell how many bytes we - // actually needed. - if ( size < sizeof(sortix_dirent) ) - { - if ( prev ) { return 0; } // We did some work. - errno = EINVAL; // Nope, userspace was cheap. - return -1; - } - - // Attempt to read into the space left, and if we managed to - // read at least one record, just say we succeded. The user - // space buffer is empty on the next call, so that'll probably - // succeed. The directory read function will store the number of - // bytes needed in the d_namelen variable and set errno to - // ERANGE such that userspace knows we need a larger buffer. - if ( dir->Read(dirent, size) ) { return (prev) ? 0 : -1; } - - // Insert the current dirent into the single-linked list for - // easy iteration by userspace. - if ( prev ) { prev->d_next = dirent; } - dirent->d_next = NULL; - - // Check for end-of-directory conditions. Signal to userspace - // that we are done by giving them the empty filename. - if ( dirent->d_namelen == 0 ) { return 0; } - - // Alright, we managed to read a dirent. Now let's try reading - // another one (provide as many as we can). - prev = dirent; - size_t bytesused = sizeof(sortix_dirent) + dirent->d_namelen + 1; - assert(bytesused <= size); - size -= bytesused; - dirent = (sortix_dirent*) ( ((uint8_t*) dirent) + bytesused ); - } - } - - int SysChDir(const char* path) - { - // Calculate the absolute new path. - Process* process = CurrentProcess(); - const char* wd = process->workingdir; - char* abs = MakeAbsolute(wd, path); - if ( !abs ) { errno = ENOMEM; return -1; } - size_t abslen = strlen(abs); - if ( 1 < abslen && abs[abslen-1] == '/' ) - { - abs[abslen-1] = '\0'; - } - - // Lookup the path and see if it is a directory. - size_t pathoffset = 0; - DevFileSystem* fs = Mount::WhichFileSystem(abs, &pathoffset); - if ( !fs ) { delete[] abs; errno = EINVAL; return -1; } - Device* dev = fs->Open(abs + pathoffset, O_SEARCH | O_DIRECTORY, 0); - if ( !dev ) { errno = ENOTDIR; return -1; } - dev->Unref(); - - // Alright, the path passed. - delete[] process->workingdir; // Works if it was NULL. - process->workingdir = abs; - - return 0; - } - - char* SysGetCWD(char* buf, size_t size) - { - // Calculate the absolute new path. - Process* process = CurrentProcess(); - const char* wd = process->workingdir; - if ( !wd ) { wd = "/"; } - size_t wdsize = strlen(wd) + 1; - if ( size < wdsize ) { errno = ERANGE; return NULL; } - strcpy(buf, wd); - return buf; - } - - void Init() - { - Syscall::Register(SYSCALL_READDIRENTS, (void*) SysReadDirEnts); - Syscall::Register(SYSCALL_CHDIR, (void*) SysChDir); - Syscall::Register(SYSCALL_GETCWD, (void*) SysGetCWD); - } - - // Allocate a byte too much, in case you want to add a trailing slash. - char* MakeAbsolute(const char* wd, const char* rel) - { - // If given no wd, then interpret from the root. - if ( !wd ) { wd = "/"; } - - // The resulting size won't ever be larger than this. - size_t wdlen = strlen(wd); - size_t resultsize = wdlen + strlen(rel) + 2; - char* result = new char[resultsize + 1]; - if ( !result ) { return NULL; } - - // Detect if rel is relative to / or to wd, and then continue the - // interpretation from that point. - size_t offset; - if ( *rel == '/' ) { result[0] = '/'; offset = 1; rel++; } - else { strcpy(result, wd); offset = wdlen; } - - // Make sure the working directory ends with a slash. - if ( result[offset-1] != '/' ) { result[offset++] = '/'; } - - bool leadingdots = true; - size_t dots = 0; - int c = 1; - while ( c ) // Exit after handling \0 - { - c = *rel++; - - // Don't insert double //'s into the final path. - if ( c == '/' && result[offset-1] == '/' ) { continue; } - - // / or \0 means that we should interpret . and .. - if ( c == '/' || c == '\0' ) - { - // If ., just remove the dot and ignore the slash. - if ( leadingdots && dots == 1 ) - { - result[--offset] = '\0'; - dots = 0; - continue; - } - - // If .., remove .. and one element of the path. - if ( leadingdots && dots == 2 ) - { - offset -= 2; // Remove .. - offset -= 1; // Remove the trailing slash - while ( offset ) - { - if ( result[--offset] == '/' ) { break; } - } - result[offset++] = '/'; // Need to re-insert a slash. - result[offset] = '\0'; - dots = 0; - continue; - } - - // Reset the dot count after a slash. - dots = 0; - } - - // The newest path element consisted solely of dots. - leadingdots = ( c == '/' || c == '.' ); - - // Count the number of leading dots in the path element. - if ( c == '.' && leadingdots ) { dots++; } - - // Insert the character into the result. - result[offset++] = c; - } - - // TODO: To avoid wasting space, should we String::Clone(result)? - - return result; - } - } -} diff --git a/sortix/directory.h b/sortix/directory.h deleted file mode 100644 index 5476c0df..00000000 --- a/sortix/directory.h +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - directory.h - A container for files and other directories. - -*******************************************************************************/ - -#ifndef SORTIX_DIRECTORY_H -#define SORTIX_DIRECTORY_H - -#include "device.h" - -namespace Sortix -{ - // Keep this up to date with - struct sortix_dirent - { - struct sortix_dirent* d_next; - unsigned char d_type; - size_t d_namelen; - char d_name[]; - }; - - class DevDirectory : public Device - { - public: - typedef Device BaseClass; - - public: - virtual void Rewind() = 0; - - // Precondition: available is at least sizeof(sortix_dirent). - virtual int Read(sortix_dirent* dirent, size_t available) = 0; - - public: - virtual bool IsType(unsigned type) const { return type == Device::DIRECTORY; } - - }; - - namespace Directory - { - void Init(); - char* MakeAbsolute(const char* wd, const char* rel); - } -} - -#endif diff --git a/sortix/dtable.cpp b/sortix/dtable.cpp new file mode 100644 index 00000000..007324e9 --- /dev/null +++ b/sortix/dtable.cpp @@ -0,0 +1,198 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + dtable.cpp + Table of file descriptors. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "process.h" + +namespace Sortix { + +DescriptorTable::DescriptorTable() +{ + dtablelock = KTHREAD_MUTEX_INITIALIZER; + entries = NULL; + numentries = 0; +} + +DescriptorTable::~DescriptorTable() +{ + Reset(); +} + +bool DescriptorTable::IsGoodEntry(int i) +{ + bool ret = 0 <= i && i < numentries && entries[i].desc; + return ret; +} + +Ref DescriptorTable::Fork() +{ + ScopedLock lock(&dtablelock); + Ref ret(new DescriptorTable); + if ( !ret ) { return Ref(NULL); } + ret->entries = new dtableent_t[numentries]; + if ( !ret->entries ) { return Ref(NULL); } + for ( ret->numentries = 0; ret->numentries < numentries; ret->numentries++ ) + { + int i = ret->numentries; + if ( entries[i].desc && !(entries[i].flags & FD_CLOFORK) ) + ret->entries[i] = entries[i]; + else + /* Already set to NULL in dtableent_t::desc constructor. */{} + } + return ret; +} + +Ref DescriptorTable::Get(int index) +{ + ScopedLock lock(&dtablelock); + if ( !IsGoodEntry(index) ) { errno = EBADF; return Ref(NULL); } + return entries[index].desc; +} + +bool DescriptorTable::Enlargen(int atleast) +{ + if ( numentries == INT_MAX ) { errno = EOVERFLOW; return -1; } + int newnumentries = INT_MAX - numentries < numentries ? + INT_MAX : + numentries ? 2 * numentries : 8; + if ( newnumentries < atleast ) + newnumentries = atleast; + dtableent_t* newentries = new dtableent_t[newnumentries]; + if ( !newentries ) + return false; + for ( int i = 0; i < numentries; i++ ) + newentries[i].desc = entries[i].desc, + newentries[i].flags = entries[i].flags; + for ( int i = numentries; i < newnumentries; i++ ) + /* newentries[i].desc is set in dtableent_t::desc constructor */ + newentries[i].flags = 0; + delete[] entries; entries = newentries; + numentries = newnumentries; + return true; +} + +int DescriptorTable::Allocate(Ref desc, int flags) +{ + if ( flags & ~__FD_ALLOWED_FLAGS ) + return errno = EINVAL, -1; + ScopedLock lock(&dtablelock); + for ( int i = 0; i < numentries; i++ ) + if ( !entries[i].desc ) + { + entries[i].desc = desc; + entries[i].flags = flags; + return i; + } + int oldnumentries = numentries; + if ( !Enlargen(0) ) + return -1; + int i = oldnumentries++; + entries[i].desc = desc; + entries[i].flags = flags; + return i; +} + +int DescriptorTable::Copy(int from, int to) +{ + ScopedLock lock(&dtablelock); + if ( from < 0 || to < 0 ) + return errno = EINVAL, -1; + if ( !(from < numentries) ) + return errno = EBADF, -1; + if ( !entries[from].desc ) + return errno = EBADF, -1; + while ( !(to < numentries) ) + if ( !Enlargen(to+1) ) + return -1; + if ( entries[to].desc != entries[from].desc ) + { + if ( entries[to].desc ) + /* TODO: Should this be synced or otherwise properly closed? */{} + entries[to].desc = entries[from].desc; + entries[to].flags = entries[from].flags; + } + return to; +} + +Ref DescriptorTable::FreeKeep(int index) +{ + ScopedLock lock(&dtablelock); + if ( !IsGoodEntry(index) ) { errno = EBADF; return Ref(NULL); } + Ref ret = entries[index].desc; + entries[index].desc.Reset(); + return ret; +} + +void DescriptorTable::Free(int index) +{ + FreeKeep(index); +} + +void DescriptorTable::OnExecute() +{ + ScopedLock lock(&dtablelock); + for ( int i = 0; i < numentries; i++ ) + { + if ( !entries[i].desc ) + continue; + if ( !(entries[i].flags & FD_CLOEXEC) ) + continue; + entries[i].desc.Reset(); + } +} + +void DescriptorTable::Reset() +{ + ScopedLock lock(&dtablelock); + numentries = 0; + delete[] entries; + entries = NULL; +} + +bool DescriptorTable::SetFlags(int index, int flags) +{ + if ( flags & ~__FD_ALLOWED_FLAGS ) + return errno = EINVAL, -1; + ScopedLock lock(&dtablelock); + if ( !IsGoodEntry(index) ) { errno = EBADF; return false; } + entries[index].flags = flags; + return true; +} + +int DescriptorTable::GetFlags(int index) +{ + ScopedLock lock(&dtablelock); + if ( !IsGoodEntry(index) ) { errno = EBADF; return -1; } + return entries[index].flags; +} + +} // namespace Sortix diff --git a/sortix/filesystem.cpp b/sortix/filesystem.cpp deleted file mode 100644 index 44c7f522..00000000 --- a/sortix/filesystem.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - filesystem.cpp - Allows access to stored sequences of bytes in an orderly fashion. - -*******************************************************************************/ - -#include -#include -#include -#include -#include "syscall.h" -#include "process.h" -#include "filesystem.h" -#include "directory.h" -#include "mount.h" -#include -#include -#include - -namespace Sortix -{ - namespace FileSystem - { - Device* Open(const char* path, int flags, mode_t mode) - { - Process* process = CurrentProcess(); - const char* wd = process->workingdir; - char* abs = Directory::MakeAbsolute(wd, path); - if ( !abs ) { errno = ENOMEM; return NULL; } - - size_t pathoffset = 0; - DevFileSystem* fs = Mount::WhichFileSystem(abs, &pathoffset); - if ( !fs ) { delete[] abs; return NULL; } - Device* result = fs->Open(abs + pathoffset, flags, mode); - delete[] abs; - return result; - } - - bool Unlink(const char* path) - { - Process* process = CurrentProcess(); - const char* wd = process->workingdir; - char* abs = Directory::MakeAbsolute(wd, path); - if ( !abs ) { errno = ENOMEM; return false; } - - size_t pathoffset = 0; - DevFileSystem* fs = Mount::WhichFileSystem(abs, &pathoffset); - if ( !fs ) { delete[] abs; return false; } - bool result = fs->Unlink(abs + pathoffset); - delete[] abs; - return result; - } - - int SysOpen(const char* path, int flags, mode_t mode) - { - char* pathcopy = String::Clone(path); - if ( !pathcopy ) - return -1; - Process* process = CurrentProcess(); - Device* dev = Open(path, flags, mode); - if ( !dev ) { delete[] pathcopy; return -1; } - int fd = process->descriptors.Allocate(dev, pathcopy); - if ( fd < 0 ) { dev->Unref(); } - int fdflags = 0; - if ( flags & O_CLOEXEC ) { fdflags |= FD_CLOEXEC; } - if ( flags & O_CLOFORK ) { fdflags |= FD_CLOFORK; } - process->descriptors.SetFlags(fd, fdflags); - return fd; - } - - int SysOpenAt(int dirfd, const char* pathname, int flags, mode_t mode) - { - if ( pathname[0] == '/' ) - return SysOpen(pathname, flags, mode); - Process* process = CurrentProcess(); - Device* dir = process->descriptors.Get(dirfd); - if ( !dir ) { errno = EBADF; return -1; } - const char* path = process->descriptors.GetPath(dirfd); - char* fullpath = String::Combine(3, path, "/", pathname); - if ( !fullpath ) - return -1; - int ret = SysOpen(fullpath, flags, mode); - delete[] fullpath; - return ret; - } - - int SysAccess(const char* pathname, int mode) - { - int oflags = 0; - bool exec = mode & X_OK; - bool read = mode & R_OK; - bool write = mode & W_OK; - if ( mode == F_OK ) { oflags = O_RDONLY; } - if ( exec && !read && !write ) { oflags = O_EXEC; } - if ( exec && read && !write ) { oflags = O_EXEC; } - if ( exec && !read && write ) { oflags = O_EXEC; } - if ( exec && read && write ) { oflags = O_EXEC; } - if ( !exec && read && write ) { oflags = O_RDWR; } - if ( !exec && !read && write ) { oflags = O_WRONLY; } - if ( !exec && read && !write ) { oflags = O_RDONLY; } - Device* dev = Open(pathname, oflags, 0); - if ( !dev ) { return -1; } - dev->Unref(); - return 0; - } - - int SysUnlink(const char* path) - { - return Unlink(path) ? 0 : -1; - } - - int SysMkDir(const char* pathname, mode_t mode) - { - // TODO: Add the proper filesystem support! - errno = ENOSYS; - return -1; - } - - int SysRmDir(const char* pathname) - { - // TODO: Add the proper filesystem support! - errno = ENOSYS; - return -1; - } - - int SysTruncate(const char* pathname, off_t length) - { - // TODO: Add the proper filesystem support! - errno = ENOSYS; - return -1; - } - - int SysFTruncate(const char* pathname, off_t length) - { - // TODO: Add the proper filesystem support! - errno = ENOSYS; - return -1; - } - - void HackStat(Device* dev, struct stat* st) - { - memset(st, 0, sizeof(*st)); - st->st_mode = 0777; - st->st_nlink = 1; - if ( dev->IsType(Device::BUFFER) ) - { - st->st_mode |= S_IFREG; - DevBuffer* buffer = (DevBuffer*) dev; - st->st_size = buffer->Size(); - st->st_blksize = 1; - st->st_blocks = st->st_size; - } - if ( dev->IsType(Device::DIRECTORY) ) - { - st->st_mode |= S_IFDIR; - st->st_nlink = 2; - } - } - - int SysStat(const char* pathname, struct stat* st) - { - Device* dev = Open(pathname, O_RDONLY, 0); - if ( !dev && errno == EISDIR ) - { - dev = Open(pathname, O_SEARCH, 0); - } - if ( !dev ) { return -1; } - HackStat(dev, st); - dev->Unref(); - return 0; - } - - int SysFStat(int fd, struct stat* st) - { - Process* process = CurrentProcess(); - DescriptorTable* descs = &(process->descriptors); - Device* dev = descs->Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - HackStat(dev, st); - return 0; - } - - int SysFCntl(int fd, int cmd, unsigned long arg) - { - Process* process = CurrentProcess(); - DescriptorTable* descs = &(process->descriptors); - Device* dev = descs->Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - switch ( cmd ) - { - case F_SETFD: descs->SetFlags(fd, (int) arg); return 0; - case F_GETFD: return descs->GetFlags(fd); - case F_SETFL: errno = ENOSYS; return -1; - case F_GETFL: - if ( dev->IsType(Device::DIRECTORY) ) { return O_SEARCH; } - if ( !dev->IsType(Device::STREAM) ) - { - errno = ENOSYS; - return -1; - } - DevStream* stream = (DevStream*) stream; - bool write = stream->IsWritable(); - bool read = stream->IsReadable(); - if ( write && read ) { return O_RDWR; } - if ( write && !read ) { return O_WRONLY; } - if ( !write && read ) { return O_RDONLY; } - return O_EXEC; - break; - } - errno = EINVAL; - return 1; - } - - void Init() - { - Syscall::Register(SYSCALL_OPEN, (void*) SysOpen); - Syscall::Register(SYSCALL_OPENAT, (void*) SysOpenAt); - Syscall::Register(SYSCALL_UNLINK, (void*) SysUnlink); - Syscall::Register(SYSCALL_MKDIR, (void*) SysMkDir); - Syscall::Register(SYSCALL_RMDIR, (void*) SysRmDir); - Syscall::Register(SYSCALL_TRUNCATE, (void*) SysTruncate); - Syscall::Register(SYSCALL_FTRUNCATE, (void*) SysFTruncate); - Syscall::Register(SYSCALL_STAT, (void*) SysStat); - Syscall::Register(SYSCALL_FSTAT, (void*) SysFStat); - Syscall::Register(SYSCALL_FCNTL, (void*) SysFCntl); - Syscall::Register(SYSCALL_ACCESS, (void*) SysAccess); - } - } - - DevFileWrapper::DevFileWrapper(DevBuffer* buffer, int flags) - { - this->buffer = buffer; - this->flags = flags; - this->offset = 0; - this->buffer->Refer(); - if ( flags & O_TRUNC ) { buffer->Resize(0); } - } - - DevFileWrapper::~DevFileWrapper() - { - buffer->Unref(); - } - - size_t DevFileWrapper::BlockSize() - { - return buffer->BlockSize(); - } - - uintmax_t DevFileWrapper::Size() - { - return buffer->Size(); - } - - uintmax_t DevFileWrapper::Position() - { - return offset; - } - - bool DevFileWrapper::Seek(uintmax_t position) - { - if ( !buffer->Seek(position) ) { return false; } - offset = position; - return true; - } - - bool DevFileWrapper::Resize(uintmax_t size) - { - return buffer->Resize(size); - } - - ssize_t DevFileWrapper::Read(uint8_t* dest, size_t count) - { - // TODO: Enforce read permission! - if ( !buffer->Seek(offset) ) { return -1; } - ssize_t result = buffer->Read(dest, count); - if ( result < 0 ) { return result; } - offset += result; - return result; - } - - ssize_t DevFileWrapper::Write(const uint8_t* src, size_t count) - { - // TODO: Enforce write permission! - if ( !buffer->Seek(offset) ) { return -1; } - ssize_t result = buffer->Write(src, count); - if ( result < 0 ) { return result; } - offset += result; - return result; - } - - bool DevFileWrapper::IsReadable() - { - // TODO: Enforce read permission! - return true; - } - - bool DevFileWrapper::IsWritable() - { - // TODO: Enforce write permission! - return true; - } -} diff --git a/sortix/filesystem.h b/sortix/filesystem.h deleted file mode 100644 index a1eeb61b..00000000 --- a/sortix/filesystem.h +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - filesystem.h - Allows access to stored sequences of bytes in an orderly fashion. - -*******************************************************************************/ - -#ifndef SORTIX_FILESYSTEM_H -#define SORTIX_FILESYSTEM_H - -#include "device.h" -#include "stream.h" -#include "fcntl.h" - -namespace Sortix -{ - class DevFileSystem : public Device - { - public: - virtual Device* Open(const char* path, int flags, mode_t mode) = 0; - virtual bool Unlink(const char* path) = 0; - - public: - virtual bool IsType(unsigned type) const { return type == Device::FILESYSTEM; } - - }; - - class DevFileWrapper : public DevBuffer - { - public: - DevFileWrapper(DevBuffer* buffer, int flags); - virtual ~DevFileWrapper(); - - private: - DevBuffer* buffer; - int flags; - uintmax_t offset; - - public: - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - virtual size_t BlockSize(); - virtual uintmax_t Size(); - virtual uintmax_t Position(); - virtual bool Seek(uintmax_t position); - virtual bool Resize(uintmax_t size); - }; - - namespace FileSystem - { - void Init(); - Device* Open(const char* path, int flags, mode_t mode); - bool Unlink(const char* path); - } -} - -#endif diff --git a/sortix/fs/devfs.cpp b/sortix/fs/devfs.cpp deleted file mode 100644 index afc88b99..00000000 --- a/sortix/fs/devfs.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - fs/devfs.cpp - Provides access to various block, character, and other kinds of devices. - -*******************************************************************************/ - -#include -#include -#include -#include -#include -#include "../filesystem.h" -#include "../directory.h" -#include "../stream.h" -#include "../terminal.h" -#include "../vga.h" -#include "../ata.h" -#include "devfs.h" -#include "videofs.h" - -namespace Sortix -{ - class DevATA : public DevBuffer - { - public: - typedef DevBuffer BaseClass; - - public: - DevATA(ATADrive* drive); - virtual ~DevATA(); - - private: - ATADrive* drive; - off_t offset; - - public: - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - virtual size_t BlockSize(); - virtual uintmax_t Size(); - virtual uintmax_t Position(); - virtual bool Seek(uintmax_t position); - virtual bool Resize(uintmax_t size); - - }; - - DevATA::DevATA(ATADrive* drive) - { - this->drive = drive; - offset = 0; - } - - DevATA::~DevATA() - { - } - - ssize_t DevATA::Read(uint8_t* dest, size_t count) - { - if ( SIZE_MAX < count ) { count = SIZE_MAX; } - if ( drive->GetSize() - offset < count ) { count = drive->GetSize() - offset; } - size_t amount = drive->Read(offset, dest, count); - if ( count && !amount ) { return -1; } - offset += amount; - return amount; - } - - ssize_t DevATA::Write(const uint8_t* src, size_t count) - { - if ( SIZE_MAX < count ) { count = SIZE_MAX; } - if ( drive->GetSize() <= offset && count ) { errno = ENOSPC; return -1; } - if ( drive->GetSize() - offset < count ) { count = drive->GetSize() - offset; } - size_t amount = drive->Write(offset, src, count); - if ( count && !amount ) { return -1; } - offset += amount; - return amount; - } - - bool DevATA::IsReadable() - { - return true; - } - - bool DevATA::IsWritable() - { - return true; - } - - size_t DevATA::BlockSize() - { - return drive->GetSectorSize(); - } - - uintmax_t DevATA::Size() - { - return drive->GetSize(); - } - - uintmax_t DevATA::Position() - { - return offset; - } - - bool DevATA::Seek(uintmax_t position) - { - if ( drive->GetSize() <= position ) { errno = ENOSPC; return false; } - offset = position; - return true; - } - - bool DevATA::Resize(uintmax_t /*size*/) - { - errno = EPERM; - return false; - } - - class DevNull : public DevStream - { - public: - typedef DevStream BaseClass; - - public: - DevNull(); - virtual ~DevNull(); - - public: - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - - }; - - DevNull::DevNull() - { - } - - DevNull::~DevNull() - { - } - - ssize_t DevNull::Read(uint8_t* /*dest*/, size_t /*count*/) - { - return 0; // Return EOF - } - - ssize_t DevNull::Write(const uint8_t* /*src*/, size_t count) - { - if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } - - // O' glorious bitbucket in the sky, I hereby sacrifice to You, my holy - // data in trust You will keep it safe. That You will store it for all - // eternity, until the day You will return to User-Land to rule the land - // and preserve data-integrity for all eternity. Amen. - - return count; - } - - bool DevNull::IsReadable() - { - return true; - } - - bool DevNull::IsWritable() - { - return true; - } - - // TODO: Move this namespace into something like devicefs.cpp. - namespace DeviceFS { - - size_t entriesused; - size_t entrieslength; - DevEntry* deventries; - DevVideoFS* videofs; - - void Init() - { - deventries = NULL; - entriesused = 0; - entrieslength = 0; - videofs = new DevVideoFS; - if ( !videofs ) { Panic("Unable to allocate videofs\n"); } - } - - bool RegisterDevice(const char* name, Device* dev) - { - if ( entriesused == entrieslength ) - { - size_t newentrieslength = entrieslength ? 2 * entrieslength : 32; - DevEntry* newdeventries = new DevEntry[newentrieslength]; - if ( !newdeventries ) { return false; } - size_t bytes = sizeof(DevEntry) * entriesused; - memcpy(newdeventries, deventries, bytes); - delete[] deventries; - entrieslength = newentrieslength; - deventries = newdeventries; - } - - char* nameclone = String::Clone(name); - if ( !nameclone ) { return false; } - - size_t index = entriesused++; - dev->Refer(); - deventries[index].name = nameclone; - deventries[index].dev = dev; - return true; - } - - Device* LookUp(const char* name) - { - for ( size_t i = 0; i < entriesused; i++ ) - { - if ( strcmp(name, deventries[i].name) ) { continue; } - deventries[i].dev->Refer(); - return deventries[i].dev; - } - return NULL; - } - - size_t GetNumDevices() - { - return entriesused; - } - - DevEntry* GetDevice(size_t index) - { - if ( entriesused <= index ) { return NULL; } - return deventries + index; - } - - // TODO: Hack to register ATA devices. - // FIXME: Move class DevATA into ata.cpp. - void RegisterATADrive(unsigned ataid, ATADrive* drive) - { - DevATA* ata = new DevATA(drive); - if ( !ata ) { Panic("Cannot allocate ATA device"); } - ata->Refer(); - assert(ataid < 10); - char name[5] = "ataN"; - name[3] = '0' + ataid; - if ( !RegisterDevice(name, ata) ) - { - PanicF("Cannot register /dev/%s", name); - } - ata->Unref(); - } - - } // namespace DeviceFS - - class DevDevFSDir : public DevDirectory - { - public: - typedef Device DevDirectory; - - public: - DevDevFSDir(); - virtual ~DevDevFSDir(); - - public: - size_t position; - - public: - virtual void Rewind(); - virtual int Read(sortix_dirent* dirent, size_t available); - - }; - - DevDevFSDir::DevDevFSDir() - { - position = 0; - } - - DevDevFSDir::~DevDevFSDir() - { - } - - void DevDevFSDir::Rewind() - { - position = 0; - } - - int DevDevFSDir::Read(sortix_dirent* dirent, size_t available) - { - const char* names[] = { ".", "..", "null", "tty", "video", "vga" }; - const char* name = NULL; - if ( position < DeviceFS::GetNumDevices() ) - { - name = DeviceFS::GetDevice(position)->name; - } - else - { - const size_t nameslength = 4; - size_t index = position - DeviceFS::GetNumDevices(); - if ( nameslength <= index ) - { - dirent->d_namelen = 0; - dirent->d_name[0] = 0; - return 0; - } - name = names[index]; - } - - if ( available <= sizeof(sortix_dirent) ) { return -1; } - - size_t namelen = strlen(name); - size_t needed = sizeof(sortix_dirent) + namelen + 1; - - if ( available < needed ) - { - dirent->d_namelen = needed; - errno = ERANGE; - return -1; - } - - memcpy(dirent->d_name, name, namelen + 1); - dirent->d_namelen = namelen; - position++; - return 0; - } - - DevDevFS::DevDevFS() - { - } - - DevDevFS::~DevDevFS() - { - } - - extern DevTerminal* tty; - - Device* DevDevFS::Open(const char* path, int flags, mode_t mode) - { - int lowerflags = flags & O_LOWERFLAGS; - - if ( !path[0] || (path[0] == '/' && !path[1]) ) - { - if ( lowerflags != O_SEARCH ) { errno = EISDIR; return NULL; } - return new DevDevFSDir(); - } - - if ( strcmp(path, "/null") == 0 ) { return new DevNull; } - if ( strcmp(path, "/tty") == 0 ) { tty->Refer(); return tty; } - if ( strcmp(path, "/vga") == 0 ) { return new DevVGA; } - if ( strcmp(path, "/video") == 0 || - String::StartsWith(path, "/video/") ) - { - return DeviceFS::videofs->Open(path + strlen("/video"), flags, mode); - } - - Device* dev = DeviceFS::LookUp(path + 1); - if ( !dev ) - { - errno = flags & O_CREAT ? EPERM : ENOENT; - return NULL; - } - if ( dev->IsType(Device::BUFFER) ) - { - DevBuffer* buffer = (DevBuffer*) dev; - DevFileWrapper* wrapper = new DevFileWrapper(buffer, flags); - buffer->Unref(); - return wrapper; - } - return dev; - } - - bool DevDevFS::Unlink(const char* path) - { - if ( strcmp(path, "/video") == 0 || - String::StartsWith(path, "/video/") ) - { - return DeviceFS::videofs->Unlink(path); - } - - if ( *path == '\0' || ( *path++ == '/' && *path == '\0' ) ) - { - errno = EISDIR; - return false; - } - - errno = EPERM; - return false; - } -} diff --git a/sortix/fs/devfs.h b/sortix/fs/devfs.h deleted file mode 100644 index 15075812..00000000 --- a/sortix/fs/devfs.h +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - fs/devfs.h - Provides access to various block, character, and other kinds of devices. - -*******************************************************************************/ - -#ifndef SORTIX_FS_DEVFS_H -#define SORTIX_FS_DEVFS_H - -#include -#include "../filesystem.h" - -namespace Sortix -{ - class ATADrive; - - class DevDevFS : public DevFileSystem - { - public: - DevDevFS(); - virtual ~DevDevFS(); - - public: - virtual Device* Open(const char* path, int flags, mode_t mode); - virtual bool Unlink(const char* path); - - }; - - namespace DeviceFS { - - struct DevEntry - { - char* name; - Device* dev; - }; - - void Init(); - void RegisterATADrive(unsigned ataid, ATADrive* drive); - bool RegisterDevice(const char* name, Device* dev); - Device* LookUp(const char* name); - size_t GetNumDevices(); - DevEntry* GetDevice(size_t index); - - } // namespace DeviceFS -} - -#endif diff --git a/sortix/fs/initfs.cpp b/sortix/fs/initfs.cpp deleted file mode 100644 index 643330b3..00000000 --- a/sortix/fs/initfs.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - fs/initfs.cpp - Provides access to the initial ramdisk. - -*******************************************************************************/ - -#include -#include -#include -#include -#include -#include "../filesystem.h" -#include "../directory.h" -#include "../stream.h" -#include "initfs.h" -#include "../initrd.h" - -namespace Sortix -{ - class DevInitFSFile : public DevBuffer - { - public: - typedef DevBuffer BaseClass; - - public: - // Transfers ownership of name. - DevInitFSFile(char* name, const uint8_t* buffer, size_t buffersize); - virtual ~DevInitFSFile(); - - public: - char* name; - - private: - size_t offset; - const uint8_t* buffer; - size_t buffersize; - kthread_mutex_t filelock; - - public: - virtual size_t BlockSize(); - virtual uintmax_t Size(); - virtual uintmax_t Position(); - virtual bool Seek(uintmax_t position); - virtual bool Resize(uintmax_t size); - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - - }; - - DevInitFSFile::DevInitFSFile(char* name, const uint8_t* buffer, size_t buffersize) - { - this->name = name; - this->buffer = buffer; - this->buffersize = buffersize; - this->offset = 0; - this->filelock = KTHREAD_MUTEX_INITIALIZER; - } - - DevInitFSFile::~DevInitFSFile() - { - delete[] name; - } - - size_t DevInitFSFile::BlockSize() - { - return 1; - } - - uintmax_t DevInitFSFile::Size() - { - return buffersize; - } - - uintmax_t DevInitFSFile::Position() - { - ScopedLock lock(&filelock); - return offset; - } - - bool DevInitFSFile::Seek(uintmax_t position) - { - ScopedLock lock(&filelock); - if ( SIZE_MAX < position ) { errno = EOVERFLOW; return false; } - offset = position; - return true; - } - - bool DevInitFSFile::Resize(uintmax_t /*size*/) - { - errno = EBADF; - return false; - } - - ssize_t DevInitFSFile::Read(uint8_t* dest, size_t count) - { - ScopedLock lock(&filelock); - if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } - size_t available = count; - if ( buffersize < offset + count ) { available = buffersize - offset; } - if ( available == 0 ) { return 0; } - memcpy(dest, buffer + offset, available); - offset += available; - return available; - } - - ssize_t DevInitFSFile::Write(const uint8_t* /*src*/, size_t /*count*/) - { - errno = EBADF; - return false; - } - - bool DevInitFSFile::IsReadable() - { - return true; - } - - bool DevInitFSFile::IsWritable() - { - return false; - } - - class DevInitFSDir : public DevDirectory - { - public: - typedef Device DevDirectory; - - public: - DevInitFSDir(uint32_t dir); - virtual ~DevInitFSDir(); - - private: - size_t position; - uint32_t dir; - uint32_t numfiles; - - public: - virtual void Rewind(); - virtual int Read(sortix_dirent* dirent, size_t available); - - }; - - DevInitFSDir::DevInitFSDir(uint32_t dir) - { - this->position = 0; - this->dir = dir; - this->numfiles = InitRD::GetNumFiles(dir); - } - - DevInitFSDir::~DevInitFSDir() - { - } - - void DevInitFSDir::Rewind() - { - position = 0; - } - - int DevInitFSDir::Read(sortix_dirent* dirent, size_t available) - { - if ( available <= sizeof(sortix_dirent) ) { return -1; } - if ( numfiles <= position ) - { - dirent->d_namelen = 0; - dirent->d_name[0] = 0; - return 0; - } - - const char* name = InitRD::GetFilename(dir, position); - size_t namelen = strlen(name); - size_t needed = sizeof(sortix_dirent) + namelen + 1; - - if ( available < needed ) - { - dirent->d_namelen = needed; - errno = ERANGE; - return -1; - } - - memcpy(dirent->d_name, name, namelen + 1); - dirent->d_namelen = namelen; - position++; - return 0; - } - - DevInitFS::DevInitFS() - { - } - - DevInitFS::~DevInitFS() - { - } - - Device* DevInitFS::Open(const char* path, int flags, mode_t mode) - { - size_t buffersize; - int lowerflags = flags & O_LOWERFLAGS; - - if ( !path[0] || (path[0] == '/' && !path[1]) ) - { - if ( lowerflags != O_SEARCH ) { errno = EISDIR; return NULL; } - return new DevInitFSDir(InitRD::Root()); - } - - if ( *path++ != '/' ) { errno = ENOENT; return NULL; } - - uint32_t ino = InitRD::Traverse(InitRD::Root(), path); - if ( !ino ) { return NULL; } - - const uint8_t* buffer = InitRD::Open(ino, &buffersize); - if ( !buffer ) { return NULL; } - - if ( lowerflags == O_SEARCH ) { errno = ENOTDIR; return NULL; } - if ( lowerflags != O_RDONLY ) { errno = EROFS; return NULL; } - - char* newpath = String::Clone(path); - if ( !newpath ) { errno = ENOSPC; return NULL; } - - Device* result = new DevInitFSFile(newpath, buffer, buffersize); - if ( !result ) { delete[] newpath; errno = ENOSPC; return NULL; } - - return result; - } - - bool DevInitFS::Unlink(const char* path) - { - errno = EROFS; - return false; - } -} diff --git a/sortix/fs/kram.cpp b/sortix/fs/kram.cpp new file mode 100644 index 00000000..5b3324c2 --- /dev/null +++ b/sortix/fs/kram.cpp @@ -0,0 +1,397 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + fs/kram.cpp + Kernel RAM filesystem. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kram.h" + +namespace Sortix { +namespace KRAMFS { + +File::File(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode) +{ + inode_type = INODE_TYPE_FILE; + if ( !dev ) + dev = (dev_t) this; + if ( !ino ) + ino = (ino_t) this; + filelock = KTHREAD_MUTEX_INITIALIZER; + this->type = S_IFREG; + this->stat_uid = owner; + this->stat_gid = group; + this->stat_mode = (mode & S_SETABLE) | this->type; + this->stat_size = 0; + this->stat_blksize = 1; + this->dev = dev; + this->ino = ino; + size = 0; + bufsize = 0; + buf = NULL; +} + +File::~File() +{ + delete[] buf; +} + +int File::truncate(ioctx_t* ctx, off_t length) +{ + ScopedLock lock(&filelock); + return truncate_unlocked(ctx, length); +} + +int File::truncate_unlocked(ioctx_t* /*ctx*/, off_t length) +{ + if ( SIZE_MAX < (uintmax_t) length ) { errno = EFBIG; return -1; } + if ( (uintmax_t) length < size ) + memset(buf + length, 0, size - length); + if ( bufsize < (size_t) length ) + { + // TODO: Don't go above OFF_MAX (or what it is called)! + size_t newbufsize = bufsize ? 2UL * bufsize : 128UL; + if ( newbufsize < (size_t) length ) + newbufsize = (size_t) length; + uint8_t* newbuf = new uint8_t[newbufsize]; + if ( !newbuf ) + return -1; + memcpy(newbuf, buf, size); + delete[] buf; buf = newbuf; bufsize = newbufsize; + } + kthread_mutex_lock(&metalock); + size = stat_size = length; + kthread_mutex_unlock(&metalock); + return 0; +} + +off_t File::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) +{ + ScopedLock lock(&filelock); + if ( whence == SEEK_SET ) + return offset; + if ( whence == SEEK_END ) + return (off_t) size + offset; + errno = EINVAL; + return -1; +} + +ssize_t File::pread(ioctx_t* ctx, uint8_t* dest, size_t count, off_t off) +{ + ScopedLock lock(&filelock); + if ( size < (uintmax_t) off ) + return 0; + size_t available = size - off; + if ( available < count ) + count = available; + if ( !ctx->copy_to_dest(dest, buf + off, count) ) + return -1; + return count; +} + +ssize_t File::pwrite(ioctx_t* ctx, const uint8_t* src, size_t count, off_t off) +{ + ScopedLock lock(&filelock); + // TODO: Avoid having off + count overflow! + if ( size < off + count ) + truncate_unlocked(ctx, off+count); + if ( size <= (uintmax_t) off ) + return -1; + size_t available = size - off; + if ( available < count ) + count = available; + ctx->copy_from_src(buf + off, src, count); + return count; +} + +Dir::Dir(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode) +{ + inode_type = INODE_TYPE_DIR; + if ( !dev ) + dev = (dev_t) this; + if ( !ino ) + ino = (ino_t) this; + dirlock = KTHREAD_MUTEX_INITIALIZER; + this->stat_gid = owner; + this->stat_gid = group; + this->type = S_IFDIR; + this->stat_mode = (mode & S_SETABLE) | this->type; + this->dev = dev; + this->ino = ino; + numchildren = 0; + childrenlen = 0; + children = NULL; + shutdown = false; +} + +Dir::~Dir() +{ + // We must not be deleted or garbage collected if we are still used by + // someone. In that case the deleter should either delete our children or + // simply forget about us. + assert(!numchildren); + delete[] children; +} + +ssize_t Dir::readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, + size_t size, off_t start, size_t /*maxcount*/) +{ + ScopedLock lock(&dirlock); + if ( numchildren <= (uintmax_t) start ) + return 0; + struct kernel_dirent retdirent; + memset(&retdirent, 0, sizeof(retdirent)); + const char* name = children[start].name; + size_t namelen = strlen(name); + size_t needed = sizeof(*dirent) + namelen + 1; + ssize_t ret = -1; + if ( size < needed ) + { + errno = ERANGE; + retdirent.d_namelen = namelen; + } + else + { + Ref inode = children[start].inode; + ret = needed; + retdirent.d_reclen = needed; + retdirent.d_off = 0; + retdirent.d_namelen = namelen; + retdirent.d_ino = inode->ino; + retdirent.d_dev = inode->dev; + retdirent.d_type = ModeToDT(inode->type); + } + if ( !ctx->copy_to_dest(dirent, &retdirent, sizeof(retdirent)) ) + return -1; + if ( 0 <= ret && !ctx->copy_to_dest(dirent->d_name, name, namelen+1) ) + return -1; + return ret; +} + +size_t Dir::FindChild(const char* filename) +{ + for ( size_t i = 0; i < numchildren; i++ ) + if ( !strcmp(filename, children[i].name) ) + return i; + return SIZE_MAX; +} + +bool Dir::AddChild(const char* filename, Ref inode) +{ + if ( numchildren == childrenlen ) + { + size_t newchildrenlen = childrenlen ? 2 * childrenlen : 4; + DirEntry* newchildren = new DirEntry[newchildrenlen]; + if ( !newchildren ) + return false; + for ( size_t i = 0; i < numchildren; i++ ) + newchildren[i].inode = children[i].inode, + newchildren[i].name = children[i].name; + delete[] children; children = newchildren; + childrenlen = newchildrenlen; + } + char* filenamecopy = String::Clone(filename); + if ( !filenamecopy ) + return false; + inode->linked(); + DirEntry* dirent = children + numchildren++; + dirent->inode = inode; + dirent->name = filenamecopy; + return true; +} + +void Dir::RemoveChild(size_t index) +{ + assert(index < numchildren); + if ( index != numchildren-1 ) + { + DirEntry tmp = children[index]; + children[index] = children[numchildren-1]; + children[numchildren-1] = tmp; + index = numchildren-1; + } + children[index].inode.Reset(); + delete[] children[index].name; + numchildren--; +} + +Ref Dir::open(ioctx_t* ctx, const char* filename, int flags, mode_t mode) +{ + ScopedLock lock(&dirlock); + if ( shutdown ) { errno = ENOENT; return Ref(NULL); } + size_t childindex = FindChild(filename); + if ( childindex != SIZE_MAX ) + { + if ( flags & O_EXCL ) { errno = EEXIST; return Ref(NULL); } + return children[childindex].inode; + } + if ( !(flags & O_CREAT) ) + return errno = ENOENT, Ref(NULL); + Ref file(new File(dev, 0, ctx->uid, ctx->gid, mode)); + if ( !file ) + return Ref(NULL); + if ( !AddChild(filename, file) ) + return Ref(NULL); + return file; +} + +int Dir::mkdir(ioctx_t* ctx, const char* filename, mode_t mode) +{ + ScopedLock lock(&dirlock); + if ( shutdown ) { errno = ENOENT; return -1; } + size_t childindex = FindChild(filename); + if ( childindex != SIZE_MAX ) { errno = EEXIST; return -1; } + Ref dir(new Dir(dev, 0, ctx->uid, ctx->gid, mode)); + if ( !dir ) + goto cleanup_done; + if ( dir->link_raw(ctx, ".", dir) ) + goto cleanup_done; + if ( dir->link_raw(ctx, "..", Ref(this)) ) + goto cleanup_dot; + if ( !AddChild(filename, dir) ) + goto cleanup_dotdot; + return 0; +cleanup_dotdot: + dir->unlink_raw(ctx, ".."); +cleanup_dot: + dir->unlink_raw(ctx, "."); +cleanup_done: + return -1; +} + +int Dir::rmdir(ioctx_t* ctx, const char* filename) +{ + if ( IsDotOrDotDot(filename) ) { errno = ENOTEMPTY; return -1; } + ScopedLock lock(&dirlock); + if ( shutdown ) { errno = ENOENT; return -1; } + size_t childindex = FindChild(filename); + if ( childindex == SIZE_MAX ) { errno = ENOENT; return -1; } + Inode* child = children[childindex].inode.Get(); + if ( !S_ISDIR(child->type) ) { errno = ENOTDIR; return -1; } + if ( child->rmdir_me(ctx) < 0 ) { return -1; } + RemoveChild(childindex); + return 0; +} + +int Dir::rmdir_me(ioctx_t* /*ctx*/) +{ + ScopedLock lock(&dirlock); + if ( shutdown ) { errno = ENOENT; return -1; } + for ( size_t i = 0; i < numchildren; i++ ) + if ( !IsDotOrDotDot(children[i].name) ) + return errno = ENOTEMPTY, -1; + shutdown = true; + for ( size_t i = 0; i < numchildren; i++ ) + { + children[i].inode->unlinked(); + children[i].inode.Reset(); + delete[] children[i].name; + } + delete[] children; children = NULL; + numchildren = childrenlen = 0; + return 0; +} + +int Dir::link(ioctx_t* /*ctx*/, const char* filename, Ref node) +{ + if ( S_ISDIR(node->type) ) { errno = EPERM; return -1; } + // TODO: Is this needed? This may protect against file descriptors to + // deleted directories being used to corrupt kernel state, or something. + if ( IsDotOrDotDot(filename) ) { errno = EEXIST; return -1; } + if ( node->dev != dev ) { errno = EXDEV; return -1; } + ScopedLock lock(&dirlock); + if ( shutdown ) { errno = ENOENT; return -1; } + size_t childindex = FindChild(filename); + if ( childindex != SIZE_MAX ) + { + errno = EEXIST; + return -1; + } + else + if ( !AddChild(filename, node) ) + return -1; + return 0; +} + +int Dir::link_raw(ioctx_t* /*ctx*/, const char* filename, Ref node) +{ + if ( node->dev != dev ) { errno = EXDEV; return -1; } + ScopedLock lock(&dirlock); + size_t childindex = FindChild(filename); + if ( childindex != SIZE_MAX ) + { + children[childindex].inode->unlinked(); + children[childindex].inode = node; + children[childindex].inode->linked(); + } + else + if ( !AddChild(filename, node) ) + return -1; + return 0; +} + +int Dir::unlink(ioctx_t* /*ctx*/, const char* filename) +{ + ScopedLock lock(&dirlock); + if ( shutdown ) { errno = ENOENT; return -1; } + size_t childindex = FindChild(filename); + if ( childindex == SIZE_MAX ) { errno = ENOENT; return -1; } + Inode* child = children[childindex].inode.Get(); + if ( S_ISDIR(child->type) ) { errno = EISDIR; return -1; } + RemoveChild(childindex); + return 0; +} + +int Dir::unlink_raw(ioctx_t* /*ctx*/, const char* filename) +{ + ScopedLock lock(&dirlock); + size_t childindex = FindChild(filename); + if ( childindex == SIZE_MAX ) { errno = ENOENT; return -1; } + RemoveChild(childindex); + return 0; +} + +int Dir::symlink(ioctx_t* /*ctx*/, const char* oldname, const char* filename) +{ + ScopedLock lock(&dirlock); + if ( shutdown ) { errno = ENOENT; return -1; } + (void) oldname; + (void) filename; + errno = ENOSYS; + return -1; +} + +} // namespace KRAMFS +} // namespace Sortix diff --git a/sortix/fs/kram.h b/sortix/fs/kram.h new file mode 100644 index 00000000..dd740fca --- /dev/null +++ b/sortix/fs/kram.h @@ -0,0 +1,103 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + fs/kram.h + Kernel RAM filesystem. + +*******************************************************************************/ + +#ifndef SORTIX_FS_KRAM_H +#define SORTIX_FS_KRAM_H + +#include +#include + +namespace Sortix { +namespace KRAMFS { + +struct DirEntry +{ + Ref inode; + char* name; +}; + +class File : public AbstractInode +{ +public: + File(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode); + virtual ~File(); + virtual int truncate(ioctx_t* ctx, off_t length); + virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); + virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, + off_t off); + virtual ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, + off_t off); + +private: + virtual int truncate_unlocked(ioctx_t* ctx, off_t length); + +private: + kthread_mutex_t filelock; + uid_t owner; + uid_t group; + mode_t mode; + size_t size; + size_t bufsize; + uint8_t* buf; + size_t numlinks; + +}; + +class Dir : public AbstractInode +{ +public: + Dir(dev_t dev, ino_t ino, uid_t owner, gid_t group, mode_t mode); + virtual ~Dir(); + virtual ssize_t readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, + size_t size, off_t start, size_t maxcount); + virtual Ref open(ioctx_t* ctx, const char* filename, int flags, + mode_t mode); + virtual int mkdir(ioctx_t* ctx, const char* filename, mode_t mode); + virtual int link(ioctx_t* ctx, const char* filename, Ref node); + virtual int link_raw(ioctx_t* ctx, const char* filename, Ref node); + virtual int unlink(ioctx_t* ctx, const char* filename); + virtual int unlink_raw(ioctx_t* ctx, const char* filename); + virtual int rmdir(ioctx_t* ctx, const char* filename); + virtual int rmdir_me(ioctx_t* ctx); + virtual int symlink(ioctx_t* ctx, const char* oldname, + const char* filename); + +private: + size_t FindChild(const char* filename); + bool AddChild(const char* filename, Ref inode); + void RemoveChild(size_t index); + +private: + kthread_mutex_t dirlock; + size_t numchildren; + size_t childrenlen; + DirEntry* children; + bool shutdown; + +}; + +} // namespace KRAMFS +} // namespace Sortix + +#endif diff --git a/sortix/fs/ramfs.cpp b/sortix/fs/ramfs.cpp deleted file mode 100644 index 4a5e938a..00000000 --- a/sortix/fs/ramfs.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - fs/ramfs.cpp - A filesystem stored entirely in RAM. - -*******************************************************************************/ - -#include -#include -#include -#include -#include -#include "../filesystem.h" -#include "../directory.h" -#include "../stream.h" -#include "ramfs.h" - -namespace Sortix -{ - class DevRAMFSFile : public DevBuffer - { - public: - typedef DevBuffer BaseClass; - - public: - // Transfers ownership of name. - DevRAMFSFile(char* name); - virtual ~DevRAMFSFile(); - - public: - char* name; - - private: - size_t offset; - uint8_t* buffer; - size_t bufferused; - size_t buffersize; - - public: - virtual size_t BlockSize(); - virtual uintmax_t Size(); - virtual uintmax_t Position(); - virtual bool Seek(uintmax_t position); - virtual bool Resize(uintmax_t size); - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - - }; - - DevRAMFSFile::DevRAMFSFile(char* name) - { - this->name = name; - buffer = NULL; - bufferused = 0; - buffersize = 0; - } - - DevRAMFSFile::~DevRAMFSFile() - { - delete[] name; - delete[] buffer; - } - - size_t DevRAMFSFile::BlockSize() - { - return 1; - } - - uintmax_t DevRAMFSFile::Size() - { - return bufferused; - } - - uintmax_t DevRAMFSFile::Position() - { - return offset; - } - - bool DevRAMFSFile::Seek(uintmax_t position) - { - if ( SIZE_MAX < position ) { errno = EOVERFLOW; return false; } - offset = position; - return true; - } - - bool DevRAMFSFile::Resize(uintmax_t size) - { - if ( SIZE_MAX < size ) { errno = EOVERFLOW; return false; } - uint8_t* newbuffer = new uint8_t[size]; - if ( !newbuffer ) { errno = ENOSPC; return false; } - size_t sharedmemsize = ( size < bufferused ) ? size : bufferused; - memcpy(newbuffer, buffer, sharedmemsize); - delete[] buffer; - buffer = newbuffer; - bufferused = sharedmemsize; - buffersize = size; - return true; - } - - ssize_t DevRAMFSFile::Read(uint8_t* dest, size_t count) - { - if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } - size_t available = count; - if ( bufferused < offset + count ) { available = bufferused - offset; } - if ( available == 0 ) { return 0; } - memcpy(dest, buffer + offset, available); - offset += available; - return available; - } - - ssize_t DevRAMFSFile::Write(const uint8_t* src, size_t count) - { - if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } - if ( buffersize < offset + count ) - { - uintmax_t newsize = (uintmax_t) offset + (uintmax_t) count; - if ( newsize < buffersize * 2 ) { newsize = buffersize * 2; } - if ( !Resize(newsize) ) { return -1; } - } - - memcpy(buffer + offset, src, count); - offset += count; - if ( bufferused < offset ) { bufferused = offset; } - return count; - } - - bool DevRAMFSFile::IsReadable() - { - return true; - } - - bool DevRAMFSFile::IsWritable() - { - return true; - } - - DevRAMFS::DevRAMFS() - { - files = NULL; - } - - DevRAMFS::~DevRAMFS() - { - if ( files ) - { - while ( !files->Empty() ) { delete files->Remove(0); } - delete files; - } - } - - class DevRAMFSDir : public DevDirectory - { - public: - typedef Device DevDirectory; - - public: - DevRAMFSDir(DevRAMFS* fs); - virtual ~DevRAMFSDir(); - - private: - DevRAMFS* fs; - size_t position; - - public: - virtual void Rewind(); - virtual int Read(sortix_dirent* dirent, size_t available); - - }; - - DevRAMFSDir::DevRAMFSDir(DevRAMFS* fs) - { - position = 0; - this->fs = fs; - fs->Refer(); - } - - DevRAMFSDir::~DevRAMFSDir() - { - fs->Unref(); - } - - void DevRAMFSDir::Rewind() - { - position = 0; - } - - int DevRAMFSDir::Read(sortix_dirent* dirent, size_t available) - { - if ( available <= sizeof(sortix_dirent) ) { return -1; } - if ( fs->GetNumFiles() <= position ) - { - dirent->d_namelen = 0; - dirent->d_name[0] = 0; - return 0; - } - - const char* name = fs->GetFilename(position); - if ( !name ) { return -1; } - size_t namelen = strlen(name); - size_t needed = sizeof(sortix_dirent) + namelen + 1; - - if ( available < needed ) - { - dirent->d_namelen = needed; - errno = ERANGE; - return -1; - } - - memcpy(dirent->d_name, name, namelen + 1); - dirent->d_namelen = namelen; - position++; - return 0; - } - - int CompareFiles(DevRAMFSFile* file1, DevRAMFSFile* file2) - { - return strcmp(file1->name, file2->name); - } - - int LookupFile(DevRAMFSFile* file, const char* name) - { - return strcmp(file->name, name); - } - - Device* DevRAMFS::Open(const char* path, int flags, mode_t mode) - { - if ( path[0] == 0 || (path[0] == '/' && path[1] == 0) ) - { - if ( (flags & O_LOWERFLAGS) == O_SEARCH ) - { - return new DevRAMFSDir(this); - } - - errno = EISDIR; - return NULL; - } - - if ( (flags & O_LOWERFLAGS) == O_SEARCH ) { errno = ENOTDIR; return NULL; } - - if ( *path++ != '/' ) { errno = ENOENT; return NULL; } - - size_t pathlen = strlen(path); - for ( size_t i = 0; i < pathlen; i++ ) - { - if ( path[i] == '/' ) { errno = ENOENT; return NULL; } - } - - DevBuffer* file = OpenFile(path, flags, mode); - if ( !file ) { return NULL; } - Device* wrapper = new DevFileWrapper(file, flags); - if ( !wrapper ) { errno = ENOSPC; return NULL; } - return wrapper; - } - - DevBuffer* DevRAMFS::OpenFile(const char* path, int flags, mode_t mode) - { - // Hack to prevent / from being a filename. - if ( path == 0 ) { errno = ENOENT; return NULL; } - - if ( files ) - { - size_t fileindex = files->Search(LookupFile, path); - if ( fileindex != SIZE_MAX ) - { - DevRAMFSFile* file = files->Get(fileindex); - if ( flags & O_TRUNC ) { file->Resize(0); } - return file; - } - } - - return CreateFile(path, flags, mode); - } - - DevBuffer* DevRAMFS::CreateFile(const char* path, int flags, mode_t mode) - { - if ( !(flags & O_CREAT) ) { errno = ENOENT; return NULL; } - - if ( !files ) - { - files = new SortedList(CompareFiles); - if ( !files) { errno = ENOSPC; return NULL; } - } - - if ( files->Search(LookupFile, path) != SIZE_MAX ) - { - errno = EEXIST; - return NULL; - } - - char* newpath = String::Clone(path); - if ( !newpath ) { errno = ENOSPC; return NULL; } - - DevRAMFSFile* file = new DevRAMFSFile(newpath); - if ( !file ) { delete[] newpath; errno = ENOSPC; return NULL; } - if ( !files->Add(file) ) { delete file; errno = ENOSPC; return NULL; } - - file->Refer(); - - return file; - } - - bool DevRAMFS::Unlink(const char* path) - { - if ( *path == '\0' || ( *path++ == '/' && *path == '\0' ) ) - { - errno = EISDIR; - return false; - } - - if ( !files ) { errno = ENOENT; return false; } - size_t index = files->Search(LookupFile, path); - if ( index == SIZE_MAX ) { errno = ENOENT; return false; } - - Device* dev = files->Remove(index); - assert(dev); - dev->Unref(); - return true; - } - - const bool BINDEVHACK = true; - - size_t DevRAMFS::GetNumFiles() - { - size_t result = 2 + (BINDEVHACK ? 2 : 0); - if ( files ) { result += files->Length(); } - return result; - } - - const char* DevRAMFS::GetFilename(size_t index) - { - switch ( index ) - { - case 0: return "."; - case 1: return ".."; - default: index -= 2; - } - if ( BINDEVHACK ) switch ( index ) - { - case 0: return "bin"; - case 1: return "dev"; - default: index -= 2; - } - if ( !files ) - return NULL; - if ( files->Length() <= index ) - return NULL; - DevRAMFSFile* file = files->Get(index); - return file->name; - } -} diff --git a/sortix/fs/ramfs.h b/sortix/fs/ramfs.h deleted file mode 100644 index a60eb43d..00000000 --- a/sortix/fs/ramfs.h +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - fs/ramfs.h - A filesystem stored entirely in RAM. - -*******************************************************************************/ - -#ifndef SORTIX_FS_RAMFS_H -#define SORTIX_FS_RAMFS_H - -#include -#include "../filesystem.h" - -namespace Sortix -{ - class DevRAMFSFile; - - class DevRAMFS : public DevFileSystem - { - public: - DevRAMFS(); - virtual ~DevRAMFS(); - - public: - virtual Device* Open(const char* path, int flags, mode_t mode); - virtual bool Unlink(const char* path); - - private: - SortedList* files; - - public: - size_t GetNumFiles(); - const char* GetFilename(size_t index); - - private: - virtual DevBuffer* OpenFile(const char* path, int flags, mode_t mode); - virtual DevBuffer* CreateFile(const char* path, int flags, mode_t mode); - - }; -} - -#endif diff --git a/sortix/fs/util.cpp b/sortix/fs/util.cpp index 0a80804b..756a6a0f 100644 --- a/sortix/fs/util.cpp +++ b/sortix/fs/util.cpp @@ -23,193 +23,93 @@ *******************************************************************************/ #include +#include +#include +#include +#include +#include +#include +#include #include #include #include "util.h" namespace Sortix { -DevStringBuffer::DevStringBuffer(char* str) -{ - this->str = str; - strlength = strlen(str); - off = 0; -} - -DevStringBuffer::~DevStringBuffer() -{ - delete[] str; -} - -size_t DevStringBuffer::BlockSize() -{ - return 1; -} - -uintmax_t DevStringBuffer::Size() -{ - return strlength; -} - -uintmax_t DevStringBuffer::Position() -{ - return off; -} - -bool DevStringBuffer::Seek(uintmax_t position) -{ - if ( strlength <= position ) { errno = EINVAL; return false; } - off = position; - return true; -} - -bool DevStringBuffer::Resize(uintmax_t size) -{ - if ( size != strlength ) { errno = EBADF; } - return false; -} - -ssize_t DevStringBuffer::Read(uint8_t* dest, size_t count) -{ - size_t available = strlength - off; - if ( available < count ) { count = available; } - memcpy(dest, str + off, count); - off += count; - return count; -} - -ssize_t DevStringBuffer::Write(const uint8_t* /*src*/, size_t /*count*/) -{ - errno = EBADF; - return -1; -} - -bool DevStringBuffer::IsReadable() -{ - return true; -} - -bool DevStringBuffer::IsWritable() -{ - return false; -} - - -DevLineCommand::DevLineCommand(bool (*handler)(void*, const char*), void* user) -{ - this->handler = handler; - this->user = user; - this->handled = false; - this->sofar = 0; -} - -DevLineCommand::~DevLineCommand() -{ -} - -ssize_t DevLineCommand::Read(uint8_t* /*dest*/, size_t /*count*/) -{ - errno = EBADF; - return -1; -} - -ssize_t DevLineCommand::Write(const uint8_t* src, size_t count) -{ - if ( handled ) { errno = EINVAL; return -1; } - size_t available = CMDMAX - sofar; - if ( !available && count ) { errno = ENOSPC; return -1; } - if ( available < count ) { count = available; } - memcpy(cmd + sofar, src, count); - cmd[sofar += count] = 0; - size_t newlinepos = strcspn(cmd, "\n"); - if ( !cmd[newlinepos] ) { return count; } - cmd[newlinepos] = 0; - if ( !handler(user, cmd) ) { return -1; } - return count; -} - -bool DevLineCommand::IsReadable() -{ - return false; -} - -bool DevLineCommand::IsWritable() -{ - return true; -} - - -DevMemoryBuffer::DevMemoryBuffer(uint8_t* buf, size_t bufsize, bool write, - bool deletebuf) +UtilMemoryBuffer::UtilMemoryBuffer(dev_t dev, ino_t ino, uid_t owner, + gid_t group, mode_t mode, uint8_t* buf, + size_t bufsize, bool write, bool deletebuf) { + inode_type = INODE_TYPE_FILE; + this->filelock = KTHREAD_MUTEX_INITIALIZER; + this->stat_uid = owner; + this->stat_gid = group; + this->type = S_IFREG; + this->stat_mode = (mode & S_SETABLE) | this->type; + this->stat_blksize = 1; + this->stat_size = (off_t) bufsize; + this->dev = dev; + this->ino = ino ? ino : (ino_t) this; this->buf = buf; this->bufsize = bufsize; this->write = write; this->deletebuf = deletebuf; } -DevMemoryBuffer::~DevMemoryBuffer() +UtilMemoryBuffer::~UtilMemoryBuffer() { if ( deletebuf ) delete[] buf; } -size_t DevMemoryBuffer::BlockSize() +int UtilMemoryBuffer::truncate(ioctx_t* /*ctx*/, off_t length) { - return 1; + ScopedLock lock(&filelock); + if ( (uintmax_t) length != (uintmax_t) bufsize ) + return errno = ENOTSUP, -1; + return 0; } -uintmax_t DevMemoryBuffer::Size() +off_t UtilMemoryBuffer::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) { - return bufsize; + ScopedLock lock(&filelock); + if ( whence == SEEK_SET ) + return offset; + if ( whence == SEEK_END ) + return (off_t) bufsize + offset; + errno = EINVAL; + return -1; } -uintmax_t DevMemoryBuffer::Position() +ssize_t UtilMemoryBuffer::pread(ioctx_t* ctx, uint8_t* dest, size_t count, + off_t off) { - return off; -} - -bool DevMemoryBuffer::Seek(uintmax_t position) -{ - off = position; - return true; -} - -bool DevMemoryBuffer::Resize(uintmax_t size) -{ - if ( size != bufsize ) { errno = EPERM; return false; } - return true; -} - -ssize_t DevMemoryBuffer::Read(uint8_t* dest, size_t count) -{ - if ( bufsize <= off ) { return 0; } + ScopedLock lock(&filelock); + if ( (uintmax_t) bufsize < (uintmax_t) off ) + return 0; size_t available = bufsize - off; - if ( available < count ) { count = available; } - memcpy(dest, buf + off, count); - off += count; + if ( available < count ) + count = available; + if ( !ctx->copy_to_dest(dest, buf + off, count) ) + return -1; return count; } -ssize_t DevMemoryBuffer::Write(const uint8_t* src, size_t count) +ssize_t UtilMemoryBuffer::pwrite(ioctx_t* ctx, const uint8_t* src, size_t count, + off_t off) { + ScopedLock lock(&filelock); if ( !write ) { errno = EBADF; return -1; } - if ( bufsize <= off ) { errno = EPERM; return -1; } + // TODO: Avoid having off + count overflow! + if ( bufsize < off + count ) + return 0; + if ( (uintmax_t) bufsize <= (uintmax_t) off ) + return -1; size_t available = bufsize - off; - if ( available < count ) { count = available; } - memcpy(buf + off, src, count); - off += count; + if ( available < count ) + count = available; + ctx->copy_from_src(buf + off, src, count); return count; } -bool DevMemoryBuffer::IsReadable() -{ - return true; -} - -bool DevMemoryBuffer::IsWritable() -{ - return write; -} - } // namespace Sortix diff --git a/sortix/fs/util.h b/sortix/fs/util.h index 1bfaca3a..e73830cb 100644 --- a/sortix/fs/util.h +++ b/sortix/fs/util.h @@ -25,79 +25,31 @@ #ifndef SORTIX_FS_UTIL_H #define SORTIX_FS_UTIL_H -#include "../stream.h" +#include namespace Sortix { -class DevStringBuffer : public DevBuffer +class UtilMemoryBuffer : public AbstractInode { public: - DevStringBuffer(char* str); - virtual ~DevStringBuffer(); - -private: - char* str; - size_t strlength; - size_t off; - -public: - virtual size_t BlockSize(); - virtual uintmax_t Size(); - virtual uintmax_t Position(); - virtual bool Seek(uintmax_t position); - virtual bool Resize(uintmax_t size); - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - -}; - -class DevLineCommand : public DevStream -{ -public: - DevLineCommand(bool (*handler)(void*, const char*), void* user); - virtual ~DevLineCommand(); - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - -private: - bool (*handler)(void*, const char*); - void* user; - size_t sofar; - static const size_t CMDMAX = 255; - char cmd[CMDMAX+1]; - bool handled; - -}; - -class DevMemoryBuffer : public DevBuffer -{ -public: - DevMemoryBuffer(uint8_t* buf, size_t bufsize, bool write = true, - bool deletebuf = true); - ~DevMemoryBuffer(); + UtilMemoryBuffer(dev_t dev, ino_t ino, uid_t owner, gid_t group, + mode_t mode, uint8_t* buf, size_t bufsize, + bool write = true, bool deletebuf = true); + virtual ~UtilMemoryBuffer(); + virtual int truncate(ioctx_t* ctx, off_t length); + virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); + virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, + off_t off); + virtual ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, + off_t off); private: + kthread_mutex_t filelock; uint8_t* buf; size_t bufsize; - size_t off; bool write; bool deletebuf; -public: - virtual size_t BlockSize(); - virtual uintmax_t Size(); - virtual uintmax_t Position(); - virtual bool Seek(uintmax_t position); - virtual bool Resize(uintmax_t size); - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - }; } // namespace Sortix diff --git a/sortix/fs/videofs.cpp b/sortix/fs/videofs.cpp deleted file mode 100644 index a263c8d6..00000000 --- a/sortix/fs/videofs.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2012. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - fs/videofs.cpp - Provides filesystem access to the video framework. - -*******************************************************************************/ - -#include -#include -#include -#include -#include -#include "../directory.h" -#include "util.h" -#include "videofs.h" - -namespace Sortix { - -class DevFrameBuffer : public DevBuffer -{ -public: - DevFrameBuffer(); - virtual ~DevFrameBuffer(); - -private: - uintmax_t off; - -public: - virtual size_t BlockSize(); - virtual uintmax_t Size(); - virtual uintmax_t Position(); - virtual bool Seek(uintmax_t position); - virtual bool Resize(uintmax_t size); - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - -}; - -DevFrameBuffer::DevFrameBuffer() -{ - off = 0; -} - -DevFrameBuffer::~DevFrameBuffer() -{ -} - -size_t DevFrameBuffer::BlockSize() -{ - return 1; // Well, not really. -} - -uintmax_t DevFrameBuffer::Size() -{ - return Video::FrameSize(); -} - -uintmax_t DevFrameBuffer::Position() -{ - return off; -} - -bool DevFrameBuffer::Seek(uintmax_t position) -{ - off = position; - return true; -} - -bool DevFrameBuffer::Resize(uintmax_t /*size*/) -{ - errno = EBADF; - return false; -} - -ssize_t DevFrameBuffer::Read(uint8_t* dest, size_t count) -{ - ssize_t result = Video::ReadAt(off, dest, count); - if ( 0 <= result ) { off += result; } - return result; -} - -ssize_t DevFrameBuffer::Write(const uint8_t* src, size_t count) -{ - ssize_t result = Video::WriteAt(off, src, count); - if ( 0 <= result ) { off += result; } - return result; -} - -bool DevFrameBuffer::IsReadable() -{ - return true; -} - -bool DevFrameBuffer::IsWritable() -{ - return true; -} - -bool SetModeHandler(void* /*user*/, const char* cmd) -{ - return Video::SwitchMode(cmd); -} - -bool SupportsModeHandler(void* /*user*/, const char* cmd) -{ - return Video::Supports(cmd); -} - -Device* MakeGetMode(int /*flags*/, mode_t /*mode*/) -{ - char* mode = Video::GetCurrentMode(); - if ( !mode ) { return NULL; } - char* modeline = String::Combine(2, mode, "\n"); - delete[] mode; mode = NULL; - if ( !modeline ) { return NULL; } - Device* result = new DevStringBuffer(modeline); - if ( !result ) { delete[] modeline; return NULL; } - return result; -} - -Device* MakeSetMode(int /*flags*/, mode_t /*mode*/) -{ - return new DevLineCommand(SetModeHandler, NULL); -} - -Device* MakeMode(int flags, mode_t mode) -{ - int lowerflags = flags & O_LOWERFLAGS; - if ( lowerflags == O_SEARCH ) { errno = ENOTDIR; return NULL; } - if ( lowerflags == O_RDONLY ) { return MakeGetMode(flags, mode); } - if ( lowerflags == O_WRONLY ) { return MakeSetMode(flags, mode); } - errno = EPERM; - return NULL; -} - -Device* MakeModes(int flags, mode_t /*mode*/) -{ - int lowerflags = flags & O_LOWERFLAGS; - if ( lowerflags == O_SEARCH ) { errno = ENOTDIR; return NULL; } - if ( lowerflags != O_RDONLY ) { errno = EPERM; return NULL; } - size_t nummodes = 0; - char** modes = Video::GetModes(&nummodes); - if ( !modes ) { return NULL; } - Device* result = NULL; - size_t combinedlen = 0; - for ( size_t i = 0; i < nummodes; i++ ) - { - combinedlen += strlen(modes[i]) + 1 /*newline*/; - } - size_t sofar = 0; - char* modesstr = new char[combinedlen + 1]; - if ( !modesstr ) { goto out; } - for ( size_t i = 0; i < nummodes; i++ ) - { - strcpy(modesstr + sofar, modes[i]); - sofar += strlen(modes[i]); - modesstr[sofar++] = '\n'; - } - modesstr[sofar] = 0; - result = new DevStringBuffer(modesstr); - if ( !result ) { delete[] modesstr; } -out: - for ( size_t i = 0; i < nummodes; i++ ) { delete[] modes[i]; } - delete[] modes; - return result; -} - -Device* MakeSupports(int flags, mode_t /*mode*/) -{ - int lowerflags = flags & O_LOWERFLAGS; - if ( lowerflags == O_SEARCH ) { errno = ENOTDIR; return NULL; } - return new DevLineCommand(SupportsModeHandler, NULL); -} - -Device* MakeFB(int flags, mode_t /*mode*/) -{ - int lowerflags = flags & O_LOWERFLAGS; - if ( lowerflags == O_SEARCH ) { errno = ENOTDIR; return NULL; } - return new DevFrameBuffer(); -} - -Device* MakeDot(int /*flags*/, mode_t /*mode*/) -{ - return NULL; -} - -Device* MakeDotDot(int /*flags*/, mode_t /*mode*/) -{ - return NULL; -} - -struct -{ - const char* name; - Device* (*factory)(int, mode_t); -} nodes[] = -{ - { ".", MakeDot }, - { "..", MakeDotDot }, - { "mode", MakeMode }, - { "modes", MakeModes }, - { "supports", MakeSupports }, - { "fb", MakeFB }, -}; - -static inline size_t NumNodes() -{ - return sizeof(nodes)/sizeof(nodes[0]); -} - -class DevVideoFSDir : public DevDirectory -{ -public: - DevVideoFSDir(); - virtual ~DevVideoFSDir(); - -private: - size_t position; - -public: - virtual void Rewind(); - virtual int Read(sortix_dirent* dirent, size_t available); - -}; - -DevVideoFSDir::DevVideoFSDir() -{ - position = 0; -} - -DevVideoFSDir::~DevVideoFSDir() -{ -} - -void DevVideoFSDir::Rewind() -{ - position = 0; -} - -int DevVideoFSDir::Read(sortix_dirent* dirent, size_t available) -{ - if ( available <= sizeof(sortix_dirent) ) { return -1; } - if ( NumNodes() <= position ) - { - dirent->d_namelen = 0; - dirent->d_name[0] = 0; - return 0; - } - - const char* name = nodes[position].name; - size_t namelen = strlen(name); - size_t needed = sizeof(sortix_dirent) + namelen + 1; - - if ( available < needed ) - { - dirent->d_namelen = needed; - errno = ERANGE; - return -1; - } - - memcpy(dirent->d_name, name, namelen + 1); - dirent->d_namelen = namelen; - position++; - return 0; -} - -DevVideoFS::DevVideoFS() -{ -} - -DevVideoFS::~DevVideoFS() -{ -} - -Device* DevVideoFS::Open(const char* path, int flags, mode_t mode) -{ - if ( !strcmp(path, "") || !strcmp(path, "/") ) - { - if ( (flags & O_LOWERFLAGS) == O_SEARCH ) { return new DevVideoFSDir; } - errno = EISDIR; - return NULL; - } - - if ( *path++ != '/' ) { errno = ENOENT; return NULL; } - - for ( size_t i = 0; i < NumNodes(); i++ ) - { - if ( strcmp(path, nodes[i].name) ) { continue; } - return nodes[i].factory(flags, mode); - } - errno = ENOENT; - return NULL; -} - -bool DevVideoFS::Unlink(const char* /*path*/) -{ - errno = EPERM; - return false; -} - -} // namespace Sortix diff --git a/sortix/fsfunc.cpp b/sortix/fsfunc.cpp new file mode 100644 index 00000000..7bb19575 --- /dev/null +++ b/sortix/fsfunc.cpp @@ -0,0 +1,81 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + fsfunc.cpp + Filesystem related utility functions. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +namespace Sortix { + +bool ModeToDT(mode_t mode) +{ + if ( S_ISSOCK(mode) ) + return DT_SOCK; + if ( S_ISLNK(mode) ) + return DT_LNK; + if ( S_ISREG(mode) ) + return DT_REG; + if ( S_ISBLK(mode) ) + return DT_BLK; + if ( S_ISDIR(mode) ) + return DT_DIR; + if ( S_ISCHR(mode) ) + return DT_CHR; + if ( S_ISFIFO(mode) ) + return DT_FIFO; + return DT_UNKNOWN; +} + +// '' -> '' '' +// '/' -> '' '/' +// '///' -> '' '///' +// '.' -> '' '.' +// 'test' -> '' 'test' +// 'test/dir' -> 'test/' 'dir' +// 'test/dir/foo' -> 'test/dir/' 'foo' +// 'test/dir/' -> 'test/' 'dir/' +// '../' -> '' '../' +// 'foo///bar//test///' -> 'foo///bar//' 'test///' + +bool SplitFinalElem(const char* path, char** dir, char** final) +{ + size_t pathlen = strlen(path); + size_t splitat = pathlen; + while ( splitat && path[splitat-1] == '/' ) + splitat--; + while ( splitat && path[splitat-1] != '/' ) + splitat--; + char* retdir = String::Substring(path, 0, splitat); + if ( !retdir ) { return false; } + char* retfinal = String::Substring(path, splitat, pathlen - splitat); + if ( !retfinal ) { delete[] retdir; return false; } + *dir = retdir; + *final = retfinal; + return true; +} + +} // namespace Sortix diff --git a/sortix/include/sortix/dirent.h b/sortix/include/sortix/dirent.h new file mode 100644 index 00000000..a00ae359 --- /dev/null +++ b/sortix/include/sortix/dirent.h @@ -0,0 +1,61 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/dirent.h + Format of directory entries. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_DIRENT_H +#define INCLUDE_SORTIX_DIRENT_H + +#include + +__BEGIN_DECLS + +#define DT_UNKNOWN 0 +#define DT_BLK 1 +#define DT_CHR 2 +#define DT_DIR 3 +#define DT_FIFO 4 +#define DT_LNK 5 +#define DT_REG 6 +#define DT_SOCK 7 + +struct kernel_dirent +{ + size_t d_reclen; + size_t d_off; + size_t d_namelen; + ino_t d_ino; + dev_t d_dev; + unsigned char d_type; + char d_name[]; +}; + +static inline struct kernel_dirent* kernel_dirent_next(struct kernel_dirent* ent) +{ + if ( !ent->d_off ) + return NULL; + return (struct kernel_dirent*) ((uint8_t*) ent + ent->d_off); +} + +__END_DECLS + +#endif diff --git a/sortix/include/sortix/fcntl.h b/sortix/include/sortix/fcntl.h index 5e954b79..b87f3a57 100644 --- a/sortix/include/sortix/fcntl.h +++ b/sortix/include/sortix/fcntl.h @@ -47,11 +47,15 @@ __BEGIN_DECLS #define FD_CLOEXEC (1<<0) #define FD_CLOFORK (1<<1) +#define __FD_ALLOWED_FLAGS (FD_CLOEXEC | FD_CLOFORK) + #define F_SETFD 0 #define F_GETFD 1 #define F_SETFL 2 #define F_GETFL 3 +#define AT_FDCWD (-100) + __END_DECLS #endif diff --git a/sortix/include/sortix/initrd.h b/sortix/include/sortix/initrd.h index b826809a..66acc63a 100644 --- a/sortix/include/sortix/initrd.h +++ b/sortix/include/sortix/initrd.h @@ -22,8 +22,8 @@ *******************************************************************************/ -#ifndef SORTIX_INITRD_H -#define SORTIX_INITRD_H +#ifndef INCLUDE_SORTIX_INITRD_H +#define INCLUDE_SORTIX_INITRD_H #include diff --git a/sortix/fs/initfs.h b/sortix/include/sortix/kernel/copy.h similarity index 60% rename from sortix/fs/initfs.h rename to sortix/include/sortix/kernel/copy.h index c80641d9..6e6bbfbe 100644 --- a/sortix/fs/initfs.h +++ b/sortix/include/sortix/kernel/copy.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011. + Copyright(C) Jonas 'Sortie' Termansen 2012. This file is part of Sortix. @@ -17,32 +17,24 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - fs/initfs.h - Provides access to the initial ramdisk. + sortix/kernel/copy.h + The context for io operations: who made it, how should data be copied, etc. *******************************************************************************/ -#ifndef SORTIX_FS_INITFS_H -#define SORTIX_FS_INITFS_H +#ifndef SORTIX_COPY_H +#define SORTIX_COPY_H -#include -#include "../filesystem.h" +#include -namespace Sortix -{ - class DevInitFSFile; +namespace Sortix { - class DevInitFS : public DevFileSystem - { - public: - DevInitFS(); - virtual ~DevInitFS(); +bool CopyToUser(void* userdst, const void* ksrc, size_t count); +bool CopyFromUser(void* kdst, const void* usersrc, size_t count); +bool CopyToKernel(void* kdst, const void* ksrc, size_t count); +bool CopyFromKernel(void* kdst, const void* ksrc, size_t count); +char* GetStringFromUser(const char* str); - public: - virtual Device* Open(const char* path, int flags, mode_t mode); - virtual bool Unlink(const char* path); - - }; -} +} // namespace Sortix #endif diff --git a/sortix/include/sortix/kernel/descriptor.h b/sortix/include/sortix/kernel/descriptor.h new file mode 100644 index 00000000..fb61b45b --- /dev/null +++ b/sortix/include/sortix/kernel/descriptor.h @@ -0,0 +1,108 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/descriptor.h + A file descriptor. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_KERNEL_DESCRIPTOR_H +#define INCLUDE_SORTIX_KERNEL_DESCRIPTOR_H + +#include + +#include + +#include + +#include +#include + +struct stat; +struct timeval; +struct winsize; +struct kernel_dirent; + +namespace Sortix { + +class Inode; +class Vnode; +struct ioctx_struct; +typedef struct ioctx_struct ioctx_t; + +class Descriptor : public Refcountable +{ +public: + Descriptor(Ref vnode, int dflags); + virtual ~Descriptor(); + Ref Fork(); + int sync(ioctx_t* ctx); + int stat(ioctx_t* ctx, struct stat* st); + int chmod(ioctx_t* ctx, mode_t mode); + int chown(ioctx_t* ctx, uid_t owner, gid_t group); + int truncate(ioctx_t* ctx, off_t length); + off_t lseek(ioctx_t* ctx, off_t offset, int whence); + ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); + ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off); + ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); + ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off); + int utimes(ioctx_t* ctx, const struct timeval times[2]); + int isatty(ioctx_t* ctx); + ssize_t readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, size_t size, + size_t maxcount); + Ref open(ioctx_t* ctx, const char* filename, int flags, + mode_t mode = 0); + int mkdir(ioctx_t* ctx, const char* filename, mode_t mode); + int link(ioctx_t* ctx, const char* filename, Ref node); + int unlink(ioctx_t* ctx, const char* filename); + int rmdir(ioctx_t* ctx, const char* filename); + int symlink(ioctx_t* ctx, const char* oldname, const char* filename); + ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz); + int tcgetwinsize(ioctx_t* ctx, struct winsize* ws); + int settermmode(ioctx_t* ctx, unsigned mode); + int gettermmode(ioctx_t* ctx, unsigned* mode); + +private: + Ref open_elem(ioctx_t* ctx, const char* filename, int flags, + mode_t mode); + bool IsSeekable(); + +public: /* These must never change after construction and is read-only. */ + ino_t ino; + dev_t dev; + mode_t type; // For use by S_IS* macros. + +public /*TODO: private*/: + Ref vnode; + kthread_mutex_t curofflock; + bool seekable; + bool checked_seekable; + off_t curoff; + int dflags; + +}; + +bool LinkInodeInDir(ioctx_t* ctx, Ref dir, const char* name, + Ref inode); +Ref OpenDirContainingPath(ioctx_t* ctx, Ref from, + const char* path, char** finalp); + +} // namespace Sortix + +#endif diff --git a/sortix/include/sortix/kernel/dtable.h b/sortix/include/sortix/kernel/dtable.h new file mode 100644 index 00000000..e4add5ec --- /dev/null +++ b/sortix/include/sortix/kernel/dtable.h @@ -0,0 +1,69 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/dtable.h + Table of file descriptors. + +*******************************************************************************/ + +#ifndef SORTIX_DTABLE_H +#define SORTIX_DTABLE_H + +#include + +namespace Sortix { + +class Descriptor; + +typedef struct dtableent_struct +{ + Ref desc; + int flags; +} dtableent_t; + +class DescriptorTable : public Refcountable +{ +public: + DescriptorTable(); + ~DescriptorTable(); + Ref Fork(); + Ref Get(int index); + int Allocate(Ref desc, int flags); + int Copy(int from, int to); + void Free(int index); + Ref FreeKeep(int index); + void OnExecute(); + bool SetFlags(int index, int flags); + int GetFlags(int index); + +private: + void Reset(); // Hey, reference counted. Don't call this. + bool IsGoodEntry(int i); + bool Enlargen(int atleast); + +private: + kthread_mutex_t dtablelock; + dtableent_t* entries; + int numentries; + +}; + +} // namespace Sortix + +#endif diff --git a/sortix/fs/videofs.h b/sortix/include/sortix/kernel/fsfunc.h similarity index 71% rename from sortix/fs/videofs.h rename to sortix/include/sortix/kernel/fsfunc.h index 16295c2f..acabe150 100644 --- a/sortix/fs/videofs.h +++ b/sortix/include/sortix/kernel/fsfunc.h @@ -17,29 +17,24 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - fs/videofs.h - Provides filesystem access to the video framework. + sortix/kernel/fsfunc.h + Filesystem related utility functions. *******************************************************************************/ -#ifndef SORTIX_FS_VIDEOFS_H -#define SORTIX_FS_VIDEOFS_H - -#include "../filesystem.h" +#ifndef SORTIX_FSFUNC_H +#define SORTIX_FSFUNC_H namespace Sortix { -class DevVideoFS : public DevFileSystem +static inline bool IsDotOrDotDot(const char* path) { -public: - DevVideoFS(); - virtual ~DevVideoFS(); + return path[0] == '.' && ((path[1] == '\0') || + (path[1] == '.' && path[2] == '\0')); +} -public: - virtual Device* Open(const char* path, int flags, mode_t mode); - virtual bool Unlink(const char* path); - -}; +bool ModeToDT(mode_t mode); +bool SplitFinalElem(const char* path, char** dir, char** final); } // namespace Sortix diff --git a/sortix/include/sortix/kernel/inode.h b/sortix/include/sortix/kernel/inode.h new file mode 100644 index 00000000..3cfd96f0 --- /dev/null +++ b/sortix/include/sortix/kernel/inode.h @@ -0,0 +1,159 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/inode.h + Interfaces and utility classes for implementing inodes. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_KERNEL_INODE_H +#define INCLUDE_SORTIX_KERNEL_INODE_H + +#include + +#include +#include + +#include + +#include + +struct stat; +struct timeval; +struct winsize; +struct kernel_dirent; + +namespace Sortix { + +struct ioctx_struct; +typedef struct ioctx_struct ioctx_t; + +// An interface describing all operations possible on an inode. +class Inode : public Refcountable +{ +public: /* These must never change after construction and is read-only. */ + ino_t ino; + dev_t dev; + mode_t type; // For use by S_IS* macros. + +public: + virtual ~Inode() { } + virtual void linked() = 0; + virtual void unlinked() = 0; + virtual int sync(ioctx_t* ctx) = 0; + virtual int stat(ioctx_t* ctx, struct stat* st) = 0; + virtual int chmod(ioctx_t* ctx, mode_t mode) = 0; + virtual int chown(ioctx_t* ctx, uid_t owner, gid_t group) = 0; + virtual int truncate(ioctx_t* ctx, off_t length) = 0; + virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence) = 0; + virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count) = 0; + virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, + off_t off) = 0; + virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count) = 0; + virtual ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, + off_t off) = 0; + virtual int utimes(ioctx_t* ctx, const struct timeval times[2]) = 0; + virtual int isatty(ioctx_t* ctx) = 0; + virtual ssize_t readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, + size_t size, off_t start, size_t maxcount) = 0; + virtual Ref open(ioctx_t* ctx, const char* filename, int flags, + mode_t mode) = 0; + virtual int mkdir(ioctx_t* ctx, const char* filename, mode_t mode) = 0; + virtual int link(ioctx_t* ctx, const char* filename, Ref node) = 0; + virtual int link_raw(ioctx_t* ctx, const char* filename, Ref node) = 0; + virtual int unlink(ioctx_t* ctx, const char* filename) = 0; + virtual int unlink_raw(ioctx_t* ctx, const char* filename) = 0; + virtual int rmdir(ioctx_t* ctx, const char* filename) = 0; + virtual int rmdir_me(ioctx_t* ctx) = 0; + virtual int symlink(ioctx_t* ctx, const char* oldname, + const char* filename) = 0; + virtual ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz) = 0; + virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws) = 0; + virtual int settermmode(ioctx_t* ctx, unsigned mode) = 0; + virtual int gettermmode(ioctx_t* ctx, unsigned* mode) = 0; + +}; + +enum InodeType +{ + INODE_TYPE_UNKNOWN = 0, + INODE_TYPE_FILE, + INODE_TYPE_STREAM, + INODE_TYPE_TTY, + INODE_TYPE_DIR, +}; + +class AbstractInode : public Inode +{ +protected: + kthread_mutex_t metalock; + InodeType inode_type; + mode_t stat_mode; + /*nlink_t*/ unsigned long stat_nlink; + uid_t stat_uid; + gid_t stat_gid; + off_t stat_size; + time_t stat_atime; + time_t stat_mtime; + time_t stat_ctime; + /* TODO: stat_atim, stat_mtim, stat_ctim */ + blksize_t stat_blksize; + blkcnt_t stat_blocks; + +public: + AbstractInode(); + virtual ~AbstractInode(); + virtual void linked(); + virtual void unlinked(); + virtual int sync(ioctx_t* ctx); + virtual int stat(ioctx_t* ctx, struct stat* st); + virtual int chmod(ioctx_t* ctx, mode_t mode); + virtual int chown(ioctx_t* ctx, uid_t owner, gid_t group); + virtual int truncate(ioctx_t* ctx, off_t length); + virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); + virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); + virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off); + virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); + virtual ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, + off_t off); + virtual int utimes(ioctx_t* ctx, const struct timeval times[2]); + virtual int isatty(ioctx_t* ctx); + virtual ssize_t readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, + size_t size, off_t start, size_t maxcount); + virtual Ref open(ioctx_t* ctx, const char* filename, int flags, + mode_t mode); + virtual int mkdir(ioctx_t* ctx, const char* filename, mode_t mode); + virtual int link(ioctx_t* ctx, const char* filename, Ref node); + virtual int link_raw(ioctx_t* ctx, const char* filename, Ref node); + virtual int unlink(ioctx_t* ctx, const char* filename); + virtual int unlink_raw(ioctx_t* ctx, const char* filename); + virtual int rmdir(ioctx_t* ctx, const char* filename); + virtual int rmdir_me(ioctx_t* ctx); + virtual int symlink(ioctx_t* ctx, const char* oldname, + const char* filename); + virtual ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz); + virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws); + virtual int settermmode(ioctx_t* ctx, unsigned mode); + virtual int gettermmode(ioctx_t* ctx, unsigned* mode); + +}; + +} // namespace Sortix + +#endif diff --git a/sortix/device.cpp b/sortix/include/sortix/kernel/ioctx.h similarity index 56% rename from sortix/device.cpp rename to sortix/include/sortix/kernel/ioctx.h index 18c24e0b..e3fd03a5 100644 --- a/sortix/device.cpp +++ b/sortix/include/sortix/kernel/ioctx.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012. This file is part of Sortix. @@ -17,42 +17,33 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - device.cpp - A base class for all devices. + sortix/kernel/ioctx.h + The context for io operations: who made it, how should data be copied, etc. *******************************************************************************/ -#include -#include -#include -#include "device.h" +#ifndef SORTIX_IOCTX_H +#define SORTIX_IOCTX_H -namespace Sortix +#include + +#include + +namespace Sortix { + +struct ioctx_struct { - Device::Device() - { - refcountlock = KTHREAD_MUTEX_INITIALIZER; - refcount = 0; - } + uid_t uid, auth_uid; + gid_t gid, auth_gid; + bool (*copy_to_dest)(void* dest, const void* src, size_t n); + bool (*copy_from_src)(void* dest, const void* src, size_t n); +}; - Device::~Device() - { +typedef struct ioctx_struct ioctx_t; - } +void SetupUserIOCtx(ioctx_t* ctx); +void SetupKernelIOCtx(ioctx_t* ctx); - void Device::Unref() - { - bool shoulddelete = false; - kthread_mutex_lock(&refcountlock); - shoulddelete = --refcount == 0 || refcount == SIZE_MAX; - kthread_mutex_unlock(&refcountlock); - if ( shoulddelete ) - delete this; - } +} // namespace Sortix - void Device::Refer() - { - ScopedLock lock(&refcountlock); - refcount++; - } -} +#endif diff --git a/sortix/keyboard.h b/sortix/include/sortix/kernel/keyboard.h similarity index 60% rename from sortix/keyboard.h rename to sortix/include/sortix/kernel/keyboard.h index be0776cb..269894db 100644 --- a/sortix/keyboard.h +++ b/sortix/include/sortix/kernel/keyboard.h @@ -17,45 +17,46 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - keyboard.h - An interface to keyboards. + sortix/kernel/keyboard.h + Various interfaces for keyboard devices and layouts. *******************************************************************************/ #ifndef SORTIX_KEYBOARD_H #define SORTIX_KEYBOARD_H -namespace Sortix +namespace Sortix { + +class Keyboard; +class KeyboardOwner; + +class Keyboard { - class Keyboard; - class KeyboardOwner; +public: + virtual ~Keyboard() { } + virtual int Read() = 0; + virtual size_t GetPending() const = 0; + virtual bool HasPending() const = 0; + virtual void SetOwner(KeyboardOwner* owner, void* user) = 0; - class Keyboard - { - public: - static void Init(); +}; - public: - virtual ~Keyboard() { } - virtual int Read() = 0; - virtual size_t GetPending() const = 0; - virtual bool HasPending() const = 0; - virtual void SetOwner(KeyboardOwner* owner, void* user) = 0; - }; +class KeyboardOwner +{ +public: + virtual ~KeyboardOwner() { } + virtual void OnKeystroke(Keyboard* keyboard, void* user) = 0; - class KeyboardOwner - { - public: - virtual ~KeyboardOwner() { } - virtual void OnKeystroke(Keyboard* keyboard, void* user) = 0; - }; +}; - class KeyboardLayout - { - public: - virtual ~KeyboardLayout() { } - virtual uint32_t Translate(int kbkey) = 0; - }; -} +class KeyboardLayout +{ +public: + virtual ~KeyboardLayout() { } + virtual uint32_t Translate(int kbkey) = 0; + +}; + +} // namespace Sortix #endif diff --git a/sortix/include/sortix/kernel/kthread.h b/sortix/include/sortix/kernel/kthread.h index 4fa24b50..0e62ac56 100644 --- a/sortix/include/sortix/kernel/kthread.h +++ b/sortix/include/sortix/kernel/kthread.h @@ -27,8 +27,6 @@ #include -#define GOT_ACTUAL_KTHREAD - namespace Sortix { extern "C" { diff --git a/sortix/device.h b/sortix/include/sortix/kernel/mtable.h similarity index 58% rename from sortix/device.h rename to sortix/include/sortix/kernel/mtable.h index afae1eb8..327422fc 100644 --- a/sortix/device.h +++ b/sortix/include/sortix/kernel/mtable.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. This file is part of Sortix. @@ -17,44 +17,46 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - device.h - A base class for all devices. + sortix/kernel/mtable.h + Class to keep track of mount points. *******************************************************************************/ -#ifndef SORTIX_DEVICE_H -#define SORTIX_DEVICE_H +#ifndef SORTIX_MTABLE_H +#define SORTIX_MTABLE_H #include +#include -namespace Sortix +namespace Sortix { + +class Inode; + +typedef struct { - class Device - { - public: - static const unsigned STREAM = 0; - static const unsigned BUFFER = 1; - static const unsigned VGABUFFER = 2; - static const unsigned FILESYSTEM = 3; - static const unsigned DIRECTORY = 4; - static const unsigned TERMINAL = 5; + Ref inode; + ino_t ino; + dev_t dev; +} mountpoint_t; - public: - Device(); - virtual ~Device(); +class MountTable : public Refcountable +{ +public: + MountTable(); + ~MountTable(); + Ref Fork(); + bool AddMount(ino_t ino, dev_t dev, Ref inode); - private: - kthread_mutex_t refcountlock; - size_t refcount; +public: // Consider these read-only. + kthread_mutex_t mtablelock; + mountpoint_t* mounts; + size_t nummounts; - public: - void Refer(); - void Unref(); +private: + size_t mountsalloced; - public: - virtual bool IsType(unsigned type) const = 0; +}; - }; -} +} // namespace Sortix #endif diff --git a/sortix/include/sortix/kernel/refcount.h b/sortix/include/sortix/kernel/refcount.h index 982fc03b..61fb7060 100644 --- a/sortix/include/sortix/kernel/refcount.h +++ b/sortix/include/sortix/kernel/refcount.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. This file is part of Sortix. @@ -29,21 +29,95 @@ namespace Sortix { -class Refcounted +class Refcountable { public: - Refcounted(); - ~Refcounted(); + Refcountable(); + virtual ~Refcountable(); public: - void Refer(); - void Unref(); - inline size_t Refcount() const { return refcount; } + void Refer_Renamed(); + void Unref_Renamed(); + size_t Refcount() const { return refcount; } + bool IsUnique() const { return refcount == 1; } private: kthread_mutex_t reflock; size_t refcount; +public: + bool being_deleted; + +}; + +template class Ref +{ +public: + constexpr Ref() : obj(NULL) { } + explicit Ref(T* obj) : obj(obj) { if ( obj ) obj->Refer_Renamed(); } + template + explicit Ref(U* obj) : obj(obj) { if ( obj ) obj->Refer_Renamed(); } + Ref(const Ref& r) : obj(r.Get()) { if ( obj ) obj->Refer_Renamed(); } + template + Ref(const Ref& r) : obj(r.Get()) { if ( obj ) obj->Refer_Renamed(); } + ~Ref() { if ( obj ) obj->Unref_Renamed(); } + + Ref& operator=(const Ref r) + { + if ( obj ) { obj->Unref_Renamed(); obj = NULL; } + if ( (obj = r.Get()) ) obj->Refer_Renamed(); + return *this; + } + + template + Ref operator=(const Ref r) + { + if ( obj ) { obj->Unref_Renamed(); obj = NULL; } + if ( (obj = r.Get()) ) obj->Refer_Renamed(); + return *this; + } + + bool operator==(const Ref& other) + { + return (*this).Get() == other.Get(); + } + + template bool operator==(const Ref& other) + { + return (*this).Get() == other.Get(); + } + + template bool operator==(const U* const& other) + { + return (*this).Get() == other; + } + + bool operator!=(const Ref& other) + { + return !((*this) == other); + } + + template bool operator!=(const Ref& other) + { + return !((*this) == other); + } + + template bool operator!=(const U* const& other) + { + return !((*this) == other); + } + + void Reset() { if ( obj ) obj->Unref_Renamed(); obj = NULL; } + T* Get() const { return obj; } + T& operator *() const { return *obj; } + T* operator->() const { return obj; } + operator bool() const { return obj != NULL; } + size_t Refcount() const { return obj ? obj->Refcount : 0; } + bool IsUnique() const { return obj->IsUnique(); } + +private: + T* obj; + }; } // namespace Sortix diff --git a/sortix/include/sortix/kernel/textbuffer.h b/sortix/include/sortix/kernel/textbuffer.h index cfda471d..43b879f0 100644 --- a/sortix/include/sortix/kernel/textbuffer.h +++ b/sortix/include/sortix/kernel/textbuffer.h @@ -72,12 +72,12 @@ public: // the screen resolution or the graphics driver. The backing text buffer can // only be changed when there are no references (but our own) to the text buffer // so don't forget to release it when you are done. -class TextBufferHandle : public Refcounted +class TextBufferHandle : public Refcountable { public: TextBufferHandle(TextBuffer* textbuf = NULL, bool deletebuf = true, TextBuffer* def = NULL, bool deletedef = true); - ~TextBufferHandle(); + virtual ~TextBufferHandle(); TextBuffer* Acquire(); void Release(TextBuffer* textbuf); void Replace(TextBuffer* newtextbuf, bool deletebuf = true); @@ -91,7 +91,6 @@ private: bool deletedef; bool deletebuf; - }; } // namespace Sortix diff --git a/sortix/include/sortix/kernel/video.h b/sortix/include/sortix/kernel/video.h index 352172f5..b3f9aecf 100644 --- a/sortix/include/sortix/kernel/video.h +++ b/sortix/include/sortix/kernel/video.h @@ -25,6 +25,8 @@ #ifndef SORTIX_VIDEO_H #define SORTIX_VIDEO_H +#include + namespace Sortix { class TextBuffer; @@ -51,7 +53,7 @@ public: namespace Video { -void Init(TextBufferHandle* textbufhandle); +void Init(Ref textbufhandle); bool RegisterDriver(const char* name, VideoDriver* driver); char* GetCurrentMode(); char* GetDriverName(size_t index); diff --git a/sortix/include/sortix/kernel/vnode.h b/sortix/include/sortix/kernel/vnode.h new file mode 100644 index 00000000..ac738aea --- /dev/null +++ b/sortix/include/sortix/kernel/vnode.h @@ -0,0 +1,88 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/vnode.h + Nodes in the virtual filesystem. + +*******************************************************************************/ + +#ifndef SORTIX_VNODE_H +#define SORTIX_VNODE_H + +#include + +struct stat; +struct timeval; +struct winsize; +struct kernel_dirent; + +namespace Sortix { + +class Inode; +struct ioctx_struct; +typedef struct ioctx_struct ioctx_t; + +// An interface describing all operations possible on an vnode. +class Vnode : public Refcountable +{ +public: /* These must never change after construction and is read-only. */ + ino_t ino; + dev_t dev; + mode_t type; // For use by S_IS* macros. + +public: + Vnode(Ref inode, Ref mountedat, ino_t rootino, dev_t rootdev); + virtual ~Vnode(); + int sync(ioctx_t* ctx); + int stat(ioctx_t* ctx, struct stat* st); + int chmod(ioctx_t* ctx, mode_t mode); + int chown(ioctx_t* ctx, uid_t owner, gid_t group); + int truncate(ioctx_t* ctx, off_t length); + off_t lseek(ioctx_t* ctx, off_t offset, int whence); + ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); + ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off); + ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); + ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off); + int utimes(ioctx_t* ctx, const struct timeval times[2]); + int isatty(ioctx_t* ctx); + ssize_t readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, + size_t size, off_t start, size_t maxcount); + Ref open(ioctx_t* ctx, const char* filename, int flags, mode_t mode); + int mkdir(ioctx_t* ctx, const char* filename, mode_t mode); + int unlink(ioctx_t* ctx, const char* filename); + int rmdir(ioctx_t* ctx, const char* filename); + int link(ioctx_t* ctx, const char* filename, Ref node); + int symlink(ioctx_t* ctx, const char* oldname, const char* filename); + ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz); + int fsbind(ioctx_t* ctx, Vnode* node, int flags); + int tcgetwinsize(ioctx_t* ctx, struct winsize* ws); + int settermmode(ioctx_t* ctx, unsigned mode); + int gettermmode(ioctx_t* ctx, unsigned* mode); + +private: + Ref inode; + Ref mountedat; + ino_t rootino; + dev_t rootdev; + +}; + +} // namespace Sortix + +#endif diff --git a/sortix/mount.h b/sortix/include/sortix/mount.h similarity index 64% rename from sortix/mount.h rename to sortix/include/sortix/mount.h index 28ec0ae0..fe5100b9 100644 --- a/sortix/mount.h +++ b/sortix/include/sortix/mount.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011. + Copyright(C) Jonas 'Sortie' Termansen 2012. This file is part of Sortix. @@ -17,24 +17,23 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - mount.h - Handles system wide mount points and initialization of new file systems. + sortix/mount.h + Constants related to mounting and binding. *******************************************************************************/ -#ifndef SORTIX_MOUNT_H -#define SORTIX_MOUNT_H +#ifndef SORTIX_INCLUDE_MOUNT_H +#define SORTIX_INCLUDE_MOUNT_H -namespace Sortix -{ - class DevFileSystem; +#include - namespace Mount - { - void Init(); - DevFileSystem* WhichFileSystem(const char* path, size_t* pathoffset); - bool Register(DevFileSystem* fs, const char* path); - } -} +__BEGIN_DECLS + +#define MREPL (0<<0) /* Replace binding. */ +#define MBEFORE (1<<0) /* Add to start of directory union. */ +#define MAFTER (2<<0) /* Add to end of directory union. */ +#define MCREATE (1<<2) /* Create files here, otherwise try next in union. */ + +__END_DECLS #endif diff --git a/sortix/include/sortix/stat.h b/sortix/include/sortix/stat.h index bbb21e3f..74b344a0 100644 --- a/sortix/include/sortix/stat.h +++ b/sortix/include/sortix/stat.h @@ -27,6 +27,7 @@ #define SORTIX_STAT_H #include +#include __BEGIN_DECLS @@ -71,6 +72,7 @@ struct stat /*#define S_ISUID 0x0800 */ /*#define S_ISGID 0x0400 */ #define S_ISVTX 0x0200 +#define S_SETABLE (0777 | 0x0200 | 0x0400 | 0x0800) #define S_ISSOCK(mode) ((mode & S_IFMT) == S_IFSOCK) #define S_ISLNK(mode) ((mode & S_IFMT) == S_IFLNK) #define S_ISREG(mode) ((mode & S_IFMT) == S_IFREG) diff --git a/sortix/include/sortix/syscallnum.h b/sortix/include/sortix/syscallnum.h index d113aec2..9c4e148c 100644 --- a/sortix/include/sortix/syscallnum.h +++ b/sortix/include/sortix/syscallnum.h @@ -49,7 +49,6 @@ #define SYSCALL_OPEN 23 #define SYSCALL_READDIRENTS 24 #define SYSCALL_CHDIR 25 -#define SYSCALL_GETCWD 26 #define SYSCALL_UNLINK 27 #define SYSCALL_REGISTER_ERRNO 28 #define SYSCALL_REGISTER_SIGNAL_HANDLER 29 @@ -78,6 +77,7 @@ #define SYSCALL_RAISE 53 #define SYSCALL_OPENAT 54 #define SYSCALL_DISPMSG_ISSUE 55 -#define SYSCALL_MAX_NUM 56 /* index of highest constant + 1 */ +#define SYSCALL_FSTATAT 56 +#define SYSCALL_MAX_NUM 57 /* index of highest constant + 1 */ #endif diff --git a/sortix/initrd.cpp b/sortix/initrd.cpp index d08b6e24..7bfeaaaf 100644 --- a/sortix/initrd.cpp +++ b/sortix/initrd.cpp @@ -23,12 +23,18 @@ *******************************************************************************/ #include +#include +#include #include #include #include +#include +#include +#include +#include #include #include -#include +#include #include #include #include "initrd.h" @@ -37,7 +43,7 @@ namespace Sortix { namespace InitRD { -uint8_t* initrd; +uint8_t* initrd = NULL; size_t initrdsize; const initrd_superblock_t* sb; @@ -129,7 +135,6 @@ uint32_t Traverse(uint32_t ino, const char* name) return 0; } - const char* GetFilename(uint32_t dir, size_t index) { const initrd_inode_t* inode = GetInode(dir); @@ -195,6 +200,7 @@ void CheckSum() void Init(addr_t phys, size_t size) { + assert(!initrd); // First up, map the initrd onto the kernel's address space. addr_t virt = Memory::GetInitRD(); size_t amount = 0; @@ -243,5 +249,96 @@ void Init(addr_t phys, size_t size) CheckSum(); } +static bool ExtractDir(ioctx_t* ctx, uint32_t ino, Ref node); +static bool ExtractFile(ioctx_t* ctx, uint32_t ino, Ref file); +static bool ExtractNode(ioctx_t* ctx, uint32_t ino, Ref node); + +static bool ExtractDir(ioctx_t* ctx, uint32_t ino, Ref dir) +{ + size_t numfiles = GetNumFiles(ino); + for ( size_t i = 0; i < numfiles; i++ ) + { + const char* name = GetFilename(ino, i); + if ( !name ) + return false; + if ( IsDotOrDotDot(name) ) + continue; + uint32_t childino = Traverse(ino, name); + if ( !childino ) + return false; + const initrd_inode_t* child = GetInode(childino); + mode_t mode = InitRDModeToHost(child->mode); + if ( INITRD_S_ISDIR(child->mode) ) + { + if ( dir->mkdir(ctx, name, mode) && errno != EEXIST ) + return false; + Ref desc = dir->open(ctx, name, O_RDWR | O_DIRECTORY, 0); + if ( !desc ) + return false; + if ( !ExtractNode(ctx, childino, desc) ) + return false; + } + if ( INITRD_S_ISREG(child->mode) ) + { + Ref desc = dir->open(ctx, name, O_WRONLY | O_CREAT, mode); + if ( !desc ) + return false; + if ( !ExtractNode(ctx, childino, desc) ) + return false; + } + } + return true; +} + +static bool ExtractFile(ioctx_t* ctx, uint32_t ino, Ref file) +{ + size_t filesize; + const uint8_t* data = Open(ino, &filesize); + if ( !data ) + return false; + if ( file->truncate(ctx, filesize) != 0 ) + return false; + size_t sofar = 0; + while ( sofar < filesize ) + { + ssize_t numbytes = file->write(ctx, data + sofar, filesize - sofar); + if ( numbytes <= 0 ) + return false; + sofar += numbytes; + } + return true; +} + +static bool ExtractNode(ioctx_t* ctx, uint32_t ino, Ref node) +{ + const initrd_inode_t* inode = GetInode(ino); + if ( !inode ) + return false; + if ( node->chmod(ctx, InitRDModeToHost(inode->mode)) < 0 ) + return false; + if ( node->chown(ctx, inode->uid, inode->gid) < 0 ) + return false; + // TODO: utimes. + if ( INITRD_S_ISDIR(inode->mode) ) + if ( !ExtractDir(ctx, ino, node) ) + return false; + if ( INITRD_S_ISREG(inode->mode) ) + if ( !ExtractFile(ctx, ino, node) ) + return false; + return true; +} + +bool ExtractInto(Ref desc) +{ + ioctx_t ctx; SetupKernelIOCtx(&ctx); + return ExtractNode(&ctx, sb->root, desc); +} + +bool ExtractFromPhysicalInto(addr_t physaddr, size_t size, Ref desc) +{ + Init(physaddr, size); + return ExtractInto(desc); +} + } // namespace InitRD } // namespace Sortix diff --git a/sortix/initrd.h b/sortix/initrd.h index 104d1c36..8ee8ee73 100644 --- a/sortix/initrd.h +++ b/sortix/initrd.h @@ -22,21 +22,22 @@ *******************************************************************************/ -#ifndef SORTIX_INITRD_KERNEL_H -#define SORTIX_INITRD_KERNEL_H +#ifndef SORTIX_INITRD_H +#define SORTIX_INITRD_H + +#include namespace Sortix { + +class Descriptor; + namespace InitRD { -void Init(addr_t phys, size_t size); -uint32_t Root(); -bool Stat(uint32_t inode, struct stat* st); -uint8_t* Open(uint32_t inode, size_t* size); -uint32_t Traverse(uint32_t inode, const char* name); -const char* GetFilename(uint32_t dir, size_t index); -size_t GetNumFiles(uint32_t dir); +bool ExtractInto(Ref desc); +bool ExtractFromPhysicalInto(addr_t physaddr, size_t size, Ref desc); } // namespace InitRD + } // namespace Sortix #endif diff --git a/sortix/inode.cpp b/sortix/inode.cpp new file mode 100644 index 00000000..95c31d6f --- /dev/null +++ b/sortix/inode.cpp @@ -0,0 +1,270 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + inode.cpp + Interfaces and utility classes for implementing inodes. + +*******************************************************************************/ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace Sortix { + +AbstractInode::AbstractInode() +{ + metalock = KTHREAD_MUTEX_INITIALIZER; + inode_type = INODE_TYPE_UNKNOWN; + stat_mode = 0; + stat_nlink = 0; + stat_uid = 0; + stat_gid = 0; + stat_size = 0; + stat_atime = 0; + stat_mtime = 0; + stat_ctime = 0; + /* TODO: stat_atim, stat_mtim, stat_ctim */ + stat_blksize = 0; + stat_blocks = 0; +} + +AbstractInode::~AbstractInode() +{ +} + +void AbstractInode::linked() +{ + InterlockedIncrement(&stat_nlink); +} + +void AbstractInode::unlinked() +{ + InterlockedDecrement(&stat_nlink); +} + +int AbstractInode::sync(ioctx_t* /*ctx*/) +{ + return 0; +} + +int AbstractInode::stat(ioctx_t* ctx, struct stat* st) +{ + struct stat retst; + ScopedLock lock(&metalock); + memset(&retst, 0, sizeof(retst)); + retst.st_dev = dev; + retst.st_ino = ino; + retst.st_mode = stat_mode; + retst.st_nlink = (nlink_t) stat_nlink; + retst.st_uid = stat_uid; + retst.st_gid = stat_gid; + retst.st_size = stat_size; + // TODO: Keep track of time. + retst.st_blksize = stat_blksize; + retst.st_blocks = stat_size / 512; + if ( !ctx->copy_to_dest(st, &retst, sizeof(retst)) ) + return -1; + return 0; +} + +int AbstractInode::chmod(ioctx_t* /*ctx*/, mode_t mode) +{ + ScopedLock lock(&metalock); + stat_mode = (mode & S_SETABLE) | this->type; + return 0; +} + +int AbstractInode::chown(ioctx_t* /*ctx*/, uid_t owner, gid_t group) +{ + ScopedLock lock(&metalock); + stat_uid = owner; + stat_gid= group; + return 0; +} + +int AbstractInode::truncate(ioctx_t* /*ctx*/, off_t /*length*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EISDIR, -1; + return errno = EINVAL, -1; +} + +off_t AbstractInode::lseek(ioctx_t* /*ctx*/, off_t /*offset*/, int /*whence*/) +{ + if ( inode_type == INODE_TYPE_STREAM || inode_type == INODE_TYPE_TTY ) + return errno = ESPIPE, -1; + return errno = EBADF, -1; +} + +ssize_t AbstractInode::read(ioctx_t* /*ctx*/, uint8_t* /*buf*/, + size_t /*count*/) +{ + return errno = EBADF, -1; +} + +ssize_t AbstractInode::pread(ioctx_t* /*ctx*/, uint8_t* /*buf*/, + size_t /*count*/, off_t /*off*/) +{ + if ( inode_type == INODE_TYPE_STREAM || inode_type == INODE_TYPE_TTY ) + return errno = ESPIPE, -1; + return errno = EBADF, -1; +} + +ssize_t AbstractInode::write(ioctx_t* /*ctx*/, const uint8_t* /*buf*/, + size_t /*count*/) +{ + return errno = EBADF, -1; +} + +ssize_t AbstractInode::pwrite(ioctx_t* /*ctx*/, const uint8_t* /*buf*/, + size_t /*count*/, off_t /*off*/) +{ + if ( inode_type == INODE_TYPE_STREAM || inode_type == INODE_TYPE_TTY ) + return errno = ESPIPE, -1; + return errno = EBADF, -1; +} + +int AbstractInode::utimes(ioctx_t* /*ctx*/, const struct timeval /*times*/[2]) +{ + // TODO: Implement this! + return 0; +} + +int AbstractInode::isatty(ioctx_t* /*ctx*/) +{ + if ( inode_type == INODE_TYPE_TTY ) + return 1; + return errno = ENOTTY, 0; +} + +ssize_t AbstractInode::readdirents(ioctx_t* /*ctx*/, + struct kernel_dirent* /*dirent*/, + size_t /*size*/, off_t /*start*/, + size_t /*maxcount*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + +Ref AbstractInode::open(ioctx_t* /*ctx*/, const char* /*filename*/, + int /*flags*/, mode_t /*mode*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, Ref(NULL); + return errno = ENOTDIR, Ref(NULL); +} + +int AbstractInode::mkdir(ioctx_t* /*ctx*/, const char* /*filename*/, + mode_t /*mode*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + +int AbstractInode::link(ioctx_t* /*ctx*/, const char* /*filename*/, + Ref /*node*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + +int AbstractInode::link_raw(ioctx_t* /*ctx*/, const char* /*filename*/, + Ref /*node*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + +int AbstractInode::unlink(ioctx_t* /*ctx*/, const char* /*filename*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + +int AbstractInode::unlink_raw(ioctx_t* /*ctx*/, const char* /*filename*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + +int AbstractInode::rmdir(ioctx_t* /*ctx*/, const char* /*filename*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + +int AbstractInode::rmdir_me(ioctx_t* /*ctx*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + +int AbstractInode::symlink(ioctx_t* /*ctx*/, const char* /*oldname*/, + const char* /*filename*/) +{ + if ( inode_type == INODE_TYPE_DIR ) + return errno = EBADF, -1; + return errno = ENOTDIR, -1; +} + +ssize_t AbstractInode::readlink(ioctx_t* /*ctx*/, char* /*buf*/, + size_t /*bufsiz*/) +{ + return errno = EINVAL, -1; +} + +int AbstractInode::tcgetwinsize(ioctx_t* /*ctx*/, struct winsize* /*ws*/) +{ + if ( inode_type == INODE_TYPE_TTY ) + return errno = EBADF, -1; + return errno = ENOTTY, -1; +} + +int AbstractInode::settermmode(ioctx_t* /*ctx*/, unsigned /*mode*/) +{ + if ( inode_type == INODE_TYPE_TTY ) + return errno = EBADF, -1; + return errno = ENOTTY, -1; +} + +int AbstractInode::gettermmode(ioctx_t* /*ctx*/, unsigned* /*mode*/) +{ + if ( inode_type == INODE_TYPE_TTY ) + return errno = EBADF, -1; + return errno = ENOTTY, -1; +} + +} // namespace Sortix diff --git a/sortix/io.cpp b/sortix/io.cpp index c2b99433..973dcd4a 100644 --- a/sortix/io.cpp +++ b/sortix/io.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. This file is part of Sortix. @@ -23,178 +23,364 @@ *******************************************************************************/ #include +#include +#include +#include +#include +#include #include #include + #include +#include +#include +#include +#include #include #include "thread.h" #include "process.h" -#include "device.h" -#include "stream.h" #include "syscall.h" #include "io.h" -namespace Sortix +namespace Sortix { +namespace IO { + +static Ref PrepareLookup(const char** path, int dirfd = AT_FDCWD) { - namespace IO - { -#ifdef GOT_FAKE_KTHREAD - struct SysWrite_t - { - union { size_t align1; int fd; }; - union { size_t align2; const uint8_t* buffer; }; - union { size_t align3; size_t count; }; - }; - - STATIC_ASSERT(sizeof(SysWrite_t) <= sizeof(Thread::scstate)); -#endif - - ssize_t SysWrite(int fd, const uint8_t* buffer, size_t count) - { - // TODO: Check that buffer is a valid user-space buffer. - if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - if ( !dev->IsType(Device::STREAM) ) { errno = EBADF; return -1; } - DevStream* stream = (DevStream*) dev; - if ( !stream->IsWritable() ) { errno = EBADF; return -1; } -#ifdef GOT_FAKE_KTHREAD - ssize_t written = stream->Write(buffer, count); - if ( 0 <= written ) { return written; } - if ( errno != EBLOCKING ) { return -1; } - - // The stream will resume our system call once progress has been - // made. Our request is certainly not forgotten. - - // Resume the system call with these parameters. - Thread* thread = CurrentThread(); - thread->scfunc = (void*) SysWrite; - SysWrite_t* state = (SysWrite_t*) thread->scstate; - state->fd = fd; - state->buffer = buffer; - state->count = count; - thread->scsize = sizeof(SysWrite_t); - - // Now go do something else. - Syscall::Incomplete(); - return 0; -#else - return stream->Write(buffer, count); -#endif - } - - // TODO: Not implemented yet due to stupid internal kernel design. - ssize_t SysPWrite(int fd, const uint8_t* buffer, size_t count, off_t off) - { - errno = ENOSYS; - return -1; - } - -#ifdef GOT_FAKE_KTHREAD - struct SysRead_t - { - union { size_t align1; int fd; }; - union { size_t align2; uint8_t* buffer; }; - union { size_t align3; size_t count; }; - }; - - STATIC_ASSERT(sizeof(SysRead_t) <= sizeof(Thread::scstate)); -#endif - - ssize_t SysRead(int fd, uint8_t* buffer, size_t count) - { - // TODO: Check that buffer is a valid user-space buffer. - if ( SSIZE_MAX < count ) { count = SSIZE_MAX; } - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - if ( !dev->IsType(Device::STREAM) ) { errno = EBADF; return -1; } - DevStream* stream = (DevStream*) dev; - if ( !stream->IsReadable() ) { errno = EBADF; return -1;} -#ifdef GOT_FAKE_KTHREAD - ssize_t bytesread = stream->Read(buffer, count); - if ( 0 <= bytesread ) { return bytesread; } - if ( errno != EBLOCKING ) { return -1; } - - // The stream will resume our system call once progress has been - // made. Our request is certainly not forgotten. - - // Resume the system call with these parameters. - Thread* thread = CurrentThread(); - thread->scfunc = (void*) SysRead; - SysRead_t* state = (SysRead_t*) thread->scstate; - state->fd = fd; - state->buffer = buffer; - state->count = count; - thread->scsize = sizeof(SysRead_t); - - // Now go do something else. - Syscall::Incomplete(); - return 0; -#else - return stream->Read(buffer, count); -#endif - } - - // TODO: Not implemented yet due to stupid internal kernel design. - ssize_t SysPRead(int fd, uint8_t* buffer, size_t count, off_t off) - { - errno = ENOSYS; - return -1; - } - - int SysSeek(int fd, off_t* offset, int whence) - { - // TODO: Validate that offset is a legal user-space off_t! - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; *offset = -1; return -1; } - if ( !dev->IsType(Device::BUFFER) ) { errno = EBADF; *offset = -1; return -1; } - DevBuffer* buffer = (DevBuffer*) dev; - off_t origin; - switch ( whence ) - { - case SEEK_SET: origin = 0; break; - case SEEK_CUR: origin = buffer->Position(); break; - case SEEK_END: origin = buffer->Size(); break; - default: errno = EINVAL; *offset = -1; return -1; - } - off_t newposition = origin + *offset; - if ( newposition < 0 ) { errno = EINVAL; *offset = -1; return -1; } - if ( !buffer->Seek(newposition) ) { *offset = -1; return -1; } - *offset = buffer->Position(); - return 0; - } - - int SysClose(int fd) - { - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - process->descriptors.Free(fd); - return 0; - } - - int SysDup(int fd) - { - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - const char* path = process->descriptors.GetPath(fd); - char* pathcopy = path ? String::Clone(path) : NULL; - return process->descriptors.Allocate(dev, pathcopy); - } - - void Init() - { - Syscall::Register(SYSCALL_WRITE, (void*) SysWrite); - Syscall::Register(SYSCALL_PWRITE, (void*) SysPWrite); - Syscall::Register(SYSCALL_READ, (void*) SysRead); - Syscall::Register(SYSCALL_PREAD, (void*) SysPRead); - Syscall::Register(SYSCALL_CLOSE, (void*) SysClose); - Syscall::Register(SYSCALL_DUP, (void*) SysDup); - Syscall::Register(SYSCALL_SEEK, (void*) SysSeek); - } - } + if ( (*path)[0] == '/' ) + return CurrentProcess()->GetRoot(); + if ( dirfd == AT_FDCWD ) + return CurrentProcess()->GetCWD(); + return CurrentProcess()->GetDescriptor(dirfd); } + +static ssize_t sys_write(int fd, const void* buffer, size_t count) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + ssize_t ret = desc->write(&ctx, (const uint8_t*) buffer, count); + return ret; +} + +static ssize_t sys_pwrite(int fd, const void* buffer, size_t count, off_t off) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + ssize_t ret = desc->pwrite(&ctx, (const uint8_t*) buffer, count, off); + return ret; +} + +static ssize_t sys_read(int fd, void* buffer, size_t count) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + ssize_t ret = desc->read(&ctx, (uint8_t*) buffer, count); + return ret; +} + +static ssize_t sys_pread(int fd, void* buffer, size_t count, off_t off) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + ssize_t ret = desc->pread(&ctx, (uint8_t*) buffer, count, off); + return ret; +} + +static off_t sys_seek(int fd, off_t offset, int whence) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + off_t ret = desc->lseek(&ctx, offset, whence); + return ret; +} + +static int sys_close(int fd) +{ + ioctx_t ctx; SetupUserIOCtx(&ctx); + Ref dtable = CurrentProcess()->GetDTable(); + Ref desc = dtable->FreeKeep(fd); + dtable.Reset(); + if ( !desc ) + return -1; + return desc->sync(&ctx); +} + +static int sys_dup(int fd) +{ + Ref dtable = CurrentProcess()->GetDTable(); + Ref desc = dtable->Get(fd); + return dtable->Allocate(desc, 0); +} + +// TODO: If this function fails the file may still have been created. Does a +// standard prohibit this and is that the wrong thing? +static int sys_openat(int dirfd, const char* path, int flags, mode_t mode) +{ + char* pathcopy = GetStringFromUser(path); + if ( !pathcopy ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + int fdflags = 0; + if ( flags & O_CLOEXEC ) fdflags |= FD_CLOEXEC; + if ( flags & O_CLOFORK ) fdflags |= FD_CLOFORK; + flags &= ~(O_CLOEXEC | O_CLOFORK); + const char* relpath = pathcopy; + Ref from = PrepareLookup(&relpath, dirfd); + if ( !from ) { delete[] pathcopy; return -1; } + Ref desc = from->open(&ctx, relpath, flags, mode); + from.Reset(); + delete[] pathcopy; + if ( !desc ) + return -1; + Ref dtable = CurrentProcess()->GetDTable(); + return dtable->Allocate(desc, fdflags); +} + +static int sys_open(const char* path, int flags, mode_t mode) +{ + return sys_openat(AT_FDCWD, path, flags, mode); +} + +// TODO: This is a hack! Stat the file in some manner and check permissions. +static int sys_access(const char* path, int /*mode*/) +{ + char* pathcopy = GetStringFromUser(path); + if ( !pathcopy ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + const char* relpath = pathcopy; + Ref from = PrepareLookup(&relpath); + Ref desc = from->open(&ctx, relpath, O_RDONLY); + delete[] pathcopy; + return desc ? 0 : -1; +} + +static int sys_unlink(const char* path) +{ + char* pathcopy = GetStringFromUser(path); + if ( !pathcopy ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + const char* relpath = pathcopy; + Ref from = PrepareLookup(&relpath); + int ret = from->unlink(&ctx, relpath); + delete[] pathcopy; + return ret; +} + +// TODO: unlinkat + +static int sys_mkdir(const char* path, mode_t mode) +{ + char* pathcopy = GetStringFromUser(path); + if ( !pathcopy ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + const char* relpath = pathcopy; + Ref from = PrepareLookup(&relpath); + int ret = from->mkdir(&ctx, relpath, mode); + delete[] pathcopy; + return ret; +} + +// TODO: mkdirat + +static int sys_rmdir(const char* path) +{ + char* pathcopy = GetStringFromUser(path); + if ( !pathcopy ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + const char* relpath = pathcopy; + Ref from = PrepareLookup(&relpath); + int ret = from->rmdir(&ctx, relpath); + delete[] pathcopy; + return ret; +} + +// TODO: unlinkat(AT_REMOVEDIR) + +static int sys_truncate(const char* path, off_t length) +{ + char* pathcopy = GetStringFromUser(path); + if ( !pathcopy ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + const char* relpath = pathcopy; + Ref from = PrepareLookup(&relpath); + Ref desc = from->open(&ctx, relpath, O_WRONLY); + delete[] pathcopy; + if ( !desc ) + return -1; + return desc->truncate(&ctx, length); +} + +static int sys_ftruncate(int fd, off_t length) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->truncate(&ctx, length); +} + +static int sys_fstatat(int dirfd, const char* path, struct stat* st, int /*flags*/) +{ + char* pathcopy = GetStringFromUser(path); + if ( !pathcopy ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + const char* relpath = pathcopy; + Ref from = PrepareLookup(&relpath, dirfd); + if ( !from ) { delete[] pathcopy; return -1; } + Ref desc = from->open(&ctx, relpath, O_RDONLY); + delete[] pathcopy; + if ( !desc ) + return -1; + return desc->stat(&ctx, st); +} + +static int sys_stat(const char* path, struct stat* st) +{ + return sys_fstatat(AT_FDCWD, path, st, 0); +} + +static int sys_fstat(int fd, struct stat* st) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->stat(&ctx, st); +} + +static int sys_fcntl(int fd, int cmd, unsigned long arg) +{ + Ref dtable = CurrentProcess()->GetDTable(); + int ret = -1; + switch ( cmd ) + { + case F_SETFD: + ret = dtable->SetFlags(fd, (int) arg) ? 0 : -1; + break; + case F_GETFD: + ret = dtable->GetFlags(fd); + break; + case F_SETFL: + case F_GETFL: + errno = ENOSYS; + break; + default: + errno = EINVAL; + break; + } + return ret; +} + +static ssize_t sys_readdirents(int fd, kernel_dirent* dirent, size_t size/*, + size_t maxcount*/) +{ + if ( size < sizeof(kernel_dirent) ) { errno = EINVAL; return -1; } + if ( SSIZE_MAX < size ) + size = SSIZE_MAX; + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->readdirents(&ctx, dirent, size, 1 /*maxcount*/); +} + +static int sys_chdir(const char* path) +{ + char* pathcopy = GetStringFromUser(path); + if ( !pathcopy ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + const char* relpath = pathcopy; + Ref from = PrepareLookup(&relpath); + Ref desc = from->open(&ctx, relpath, O_RDONLY | O_DIRECTORY); + from.Reset(); + delete[] pathcopy; + if ( !desc ) + return -1; + CurrentProcess()->SetCWD(desc); + return 0; +} + +static int sys_settermmode(int fd, unsigned mode) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->settermmode(&ctx, mode); +} + +static int sys_gettermmode(int fd, unsigned* mode) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->gettermmode(&ctx, mode); +} + +static int sys_isatty(int fd) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return 0; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->isatty(&ctx); +} + +static int sys_tcgetwinsize(int fd, struct winsize* ws) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->tcgetwinsize(&ctx, ws); +} + +void Init() +{ + Syscall::Register(SYSCALL_ACCESS, (void*) sys_access); + Syscall::Register(SYSCALL_CHDIR, (void*) sys_chdir); + Syscall::Register(SYSCALL_CLOSE, (void*) sys_close); + Syscall::Register(SYSCALL_DUP, (void*) sys_dup); + Syscall::Register(SYSCALL_FCNTL, (void*) sys_fcntl); + Syscall::Register(SYSCALL_FSTATAT, (void*) sys_fstatat); + Syscall::Register(SYSCALL_FSTAT, (void*) sys_fstat); + Syscall::Register(SYSCALL_FTRUNCATE, (void*) sys_ftruncate); + Syscall::Register(SYSCALL_GETTERMMODE, (void*) sys_gettermmode); + Syscall::Register(SYSCALL_ISATTY, (void*) sys_isatty); + Syscall::Register(SYSCALL_MKDIR, (void*) sys_mkdir); + Syscall::Register(SYSCALL_OPENAT, (void*) sys_openat); + Syscall::Register(SYSCALL_OPEN, (void*) sys_open); + Syscall::Register(SYSCALL_PREAD, (void*) sys_pread); + Syscall::Register(SYSCALL_PWRITE, (void*) sys_pwrite); + Syscall::Register(SYSCALL_READDIRENTS, (void*) sys_readdirents); + Syscall::Register(SYSCALL_READ, (void*) sys_read); + Syscall::Register(SYSCALL_RMDIR, (void*) sys_rmdir); + Syscall::Register(SYSCALL_SEEK, (void*) sys_seek); + Syscall::Register(SYSCALL_SETTERMMODE, (void*) sys_settermmode); + Syscall::Register(SYSCALL_STAT, (void*) sys_stat); + Syscall::Register(SYSCALL_TCGETWINSIZE, (void*) sys_tcgetwinsize); + Syscall::Register(SYSCALL_TRUNCATE, (void*) sys_truncate); + Syscall::Register(SYSCALL_UNLINK, (void*) sys_unlink); + Syscall::Register(SYSCALL_WRITE, (void*) sys_write); +} + +} // namespace IO +} // namespace Sortix diff --git a/sortix/io.h b/sortix/io.h index cfd15bb5..fbb0c375 100644 --- a/sortix/io.h +++ b/sortix/io.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -25,12 +25,12 @@ #ifndef SORTIX_IO_H #define SORTIX_IO_H -namespace Sortix -{ - namespace IO - { - void Init(); - } -} +namespace Sortix { +namespace IO { + +void Init(); + +} // namespace IO +} // namespace Sortix #endif diff --git a/sortix/keyboard.cpp b/sortix/ioctx.cpp similarity index 55% rename from sortix/keyboard.cpp rename to sortix/ioctx.cpp index 86c23f7d..408e1377 100644 --- a/sortix/keyboard.cpp +++ b/sortix/ioctx.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. This file is part of Sortix. @@ -17,32 +17,32 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - keyboard.cpp - An interface to keyboards. + ioctx.cpp + The context for io operations: who made it, how should data be copied, etc. *******************************************************************************/ #include -#include "interrupt.h" -#include "syscall.h" -#include "keyboard.h" -#include "kb/ps2.h" -#include "kb/layout/us.h" -#include "logterminal.h" +#include +#include +#include "process.h" -namespace Sortix +namespace Sortix { + +void SetupUserIOCtx(ioctx_t* ctx) { - DevTerminal* tty; - - void Keyboard::Init() - { - Keyboard* keyboard = new PS2Keyboard(0x60, Interrupt::IRQ1); - if ( !keyboard ) { Panic("Could not allocate PS2 Keyboard driver"); } - - KeyboardLayout* kblayout = new KBLayoutUS; - if ( !kblayout ) { Panic("Could not allocate keyboard layout driver"); } - - tty = new LogTerminal(keyboard, kblayout); - if ( !tty ) { Panic("Could not allocate a simple terminal"); } - } + ctx->uid = ctx->auth_uid = CurrentProcess()->uid; + ctx->gid = ctx->auth_gid = CurrentProcess()->gid; + ctx->copy_to_dest = CopyToUser; + ctx->copy_from_src = CopyFromUser; } + +void SetupKernelIOCtx(ioctx_t* ctx) +{ + ctx->uid = ctx->auth_uid = CurrentProcess()->uid; + ctx->gid = ctx->auth_gid = CurrentProcess()->gid; + ctx->copy_to_dest = CopyToKernel; + ctx->copy_from_src = CopyFromKernel; +} + +} // namespace Sortix diff --git a/sortix/kb/layout/us.cpp b/sortix/kb/layout/us.cpp index 95141673..0889fd19 100644 --- a/sortix/kb/layout/us.cpp +++ b/sortix/kb/layout/us.cpp @@ -23,7 +23,7 @@ *******************************************************************************/ #include -#include "../../keyboard.h" +#include #include #include "us.h" diff --git a/sortix/kb/layout/us.h b/sortix/kb/layout/us.h index 0abf5b50..16868f9c 100644 --- a/sortix/kb/layout/us.h +++ b/sortix/kb/layout/us.h @@ -25,7 +25,7 @@ #ifndef SORTIX_KB_LAYOUT_US_H #define SORTIX_KB_LAYOUT_US_H -#include "../../keyboard.h" +#include namespace Sortix { diff --git a/sortix/kb/ps2.cpp b/sortix/kb/ps2.cpp index 8590bbaa..1a5c305a 100644 --- a/sortix/kb/ps2.cpp +++ b/sortix/kb/ps2.cpp @@ -23,11 +23,11 @@ *******************************************************************************/ #include +#include +#include #include #include #include "../interrupt.h" -#include "../keyboard.h" -#include #include "ps2.h" namespace Sortix @@ -87,14 +87,10 @@ namespace Sortix void PS2Keyboard::OnInterrupt(CPU::InterruptRegisters* /*regs*/) { uint8_t scancode = PopScancode(); -#ifdef GOT_ACTUAL_KTHREAD PS2KeyboardWork work; work.kb = this; work.scancode = scancode; Interrupt::ScheduleWork(PS2Keyboard__InterruptWork, &work, sizeof(work)); -#else - InterruptWork(scancode); -#endif } void PS2Keyboard::InterruptWork(uint8_t scancode) diff --git a/sortix/kb/ps2.h b/sortix/kb/ps2.h index 1ee3f975..b3684729 100644 --- a/sortix/kb/ps2.h +++ b/sortix/kb/ps2.h @@ -26,7 +26,7 @@ #define SORTIX_KB_PS2_H #include -#include "../keyboard.h" +#include namespace Sortix { diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index a6bee105..95af26b8 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. This file is part of Sortix. @@ -33,15 +33,25 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include #include "kernelinfo.h" #include "x86-family/gdt.h" #include "x86-family/float.h" #include "time.h" -#include "keyboard.h" #include "multiboot.h" #include "thread.h" #include "process.h" @@ -51,8 +61,8 @@ #include "ata.h" #include "com.h" #include "uart.h" +#include "logterminal.h" #include "vgatextbuffer.h" -#include "terminal.h" #include "serialterminal.h" #include "textterminal.h" #include "elf.h" @@ -62,12 +72,11 @@ #include "sound.h" #include "io.h" #include "pipe.h" -#include "filesystem.h" -#include "mount.h" -#include "directory.h" #include "interrupt.h" #include "dispmsg.h" -#include "fs/devfs.h" +#include "fs/kram.h" +#include "kb/ps2.h" +#include "kb/layout/us.h" // Keep the stack size aligned with $CPU/base.s const size_t STACK_SIZE = 64*1024; @@ -121,10 +130,18 @@ static size_t TextTermHeight(void* user) return ((TextTerminal*) user)->Height(); } +addr_t initrd; +size_t initrdsize; +Ref textbufhandle; + extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) { (void) magic; + // + // Stage 1. Initialization of Early Environment. + // + // Initialize system calls. Syscall::Init(); @@ -137,10 +154,11 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) const size_t VGA_HEIGHT = 25; static uint16_t vga_attr_buffer[VGA_WIDTH*VGA_HEIGHT]; VGATextBuffer textbuf(VGAFB, vga_attr_buffer, VGA_WIDTH, VGA_HEIGHT); - TextBufferHandle textbufhandle(NULL, false, &textbuf, false); + TextBufferHandle textbufhandlestack(NULL, false, &textbuf, false); + textbufhandle = Ref(&textbufhandlestack); // Setup a text terminal instance. - TextTerminal textterm(&textbufhandle); + TextTerminal textterm(textbufhandle); // Register the text terminal as the kernel log and initialize it. Log::Init(PrintToTextTerminal, TextTermWidth, TextTermHeight, &textterm); @@ -182,8 +200,8 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) "multiboot compliant?"); } - addr_t initrd = 0; - size_t initrdsize = 0; + initrd = 0; + initrdsize = 0; uint32_t* modules = (uint32_t*) (addr_t) bootinfo->mods_addr; for ( uint32_t i = 0; i < bootinfo->mods_count; i++ ) @@ -209,26 +227,12 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // Initialize the kernel heap. _init_heap(); - // Initialize the interrupt worker. + // Initialize the interrupt worker (before scheduling is enabled). Interrupt::InitWorker(); - // Initialize the list of kernel devices. - DeviceFS::Init(); - - // Initialize the COM ports. - COM::Init(); - - // Initialize the keyboard. - Keyboard::Init(); - - // Initialize the terminal. - Terminal::Init(); - - // Initialize the VGA driver. - VGA::Init(); - - // Initialize the sound driver. - Sound::Init(); + // + // Stage 2. Transition to Multithreaded Environment + // // Initialize the process system. Process::Init(); @@ -236,47 +240,11 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // Initialize the thread system. Thread::Init(); - // Initialize the IO system. - IO::Init(); - - // Initialize the pipe system. - Pipe::Init(); - - // Initialize the filesystem system. - FileSystem::Init(); - - // Initialize the directory system. - Directory::Init(); - - // Initialize the mount system. - Mount::Init(); - - // Initialize the scheduler. - Scheduler::Init(); - // Initialize Unix Signals. Signal::Init(); - // Initialize the worker thread data structures. - Worker::Init(); - - // Initialize the kernel information query syscall. - Info::Init(); - - // Set up the initial ram disk. - InitRD::Init(initrd, initrdsize); - - // Initialize the Video Driver framework. - Video::Init(&textbufhandle); - - // Search for PCI devices and load their drivers. - PCI::Init(); - - // Initialize ATA devices. - ATA::Init(); - - // Initialize the BGA driver. - BGA::Init(); + // Initialize the scheduler. + Scheduler::Init(); // Initialize the Display Message framework. DisplayMessage::Init(); @@ -294,6 +262,7 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // _nothing_ else to run on this CPU. Thread* idlethread = new Thread; idlethread->process = system; + idlethread->addrspace = idlethread->process->addrspace; idlethread->kernelstackpos = (addr_t) stack; idlethread->kernelstacksize = STACK_SIZE; idlethread->kernelstackmalloced = false; @@ -302,8 +271,9 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) Scheduler::SetIdleThread(idlethread); // Let's create a regular kernel thread that can decide what happens next. - // Note that we don't do the work here: should it block, then there is - // nothing to run. Therefore we must become the system idle thread. + // Note that we don't do the work here: if all other threads are not running + // and this thread isn't runnable, then there is nothing to run. Therefore + // we must become the system idle thread. RunKernelThread(BootThread, NULL); // Set up such that floating point registers are lazily switched. @@ -319,27 +289,130 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) static void SystemIdleThread(void* /*user*/) { // Alright, we are now the system idle thread. If there is nothing to do, - // then we are run. Note that we must never do any real work here. + // then we are run. Note that we must never do any real work here as the + // idle thread must always be runnable. while(true); } static void BootThread(void* /*user*/) { + // + // Stage 3. Spawning Kernel Worker Threads. + // + // Hello, threaded world! You can now regard the kernel as a multi-threaded // process with super-root access to the system. Before we boot the full // system we need to start some worker threads. - // Let's create the interrupt worker thread that executes additional work // requested by interrupt handlers, where such work isn't safe. Thread* interruptworker = RunKernelThread(Interrupt::WorkerThread, NULL); if ( !interruptworker ) Panic("Could not create interrupt worker"); + // Initialize the worker thread data structures. + Worker::Init(); + // Create a general purpose worker thread. Thread* workerthread = RunKernelThread(Worker::Thread, NULL); if ( !workerthread ) Panic("Unable to create general purpose worker thread"); + // + // Stage 4. Initialize the Filesystem + // + + Ref dtable(new DescriptorTable()); + if ( !dtable ) + Panic("Unable to allocate descriptor table"); + Ref mtable(new MountTable()); + if ( !mtable ) + Panic("Unable to allocate mount table."); + CurrentProcess()->BootstrapTables(dtable, mtable); + + // Let's begin preparing the filesystem. + // TODO: Setup the right device id for the KRAMFS dir? + Ref iroot(new KRAMFS::Dir((dev_t) 0, (ino_t) 0, 0, 0, 0755)); + if ( !iroot ) + Panic("Unable to allocate root inode."); + ioctx_t ctx; SetupKernelIOCtx(&ctx); + Ref vroot(new Vnode(iroot, Ref(NULL), 0, 0)); + if ( !vroot ) + Panic("Unable to allocate root vnode."); + Ref droot(new Descriptor(vroot, 0)); + if ( !droot ) + Panic("Unable to allocate root descriptor."); + CurrentProcess()->BootstrapDirectories(droot); + + // Initialize the root directory. + if ( iroot->link_raw(&ctx, ".", iroot) != 0 ) + Panic("Unable to link /. to /"); + if ( iroot->link_raw(&ctx, "..", iroot) != 0 ) + Panic("Unable to link /.. to /"); + + // Install the initrd into our fresh RAM filesystem. + if ( !InitRD::ExtractFromPhysicalInto(initrd, initrdsize, droot) ) + Panic("Unable to extract initrd into RAM root filesystem."); + // TODO: It's safe to free the original initrd now. + + // Get a descriptor for the /dev directory so we can populate it. + if ( droot->mkdir(&ctx, "dev", 0775) != 0 && errno != EEXIST ) + Panic("Unable to create RAM filesystem /dev directory."); + Ref slashdev = droot->open(&ctx, "dev", O_RDONLY | O_DIRECTORY); + if ( !slashdev ) + Panic("Unable to create descriptor for RAM filesystem /dev directory."); + + // + // Stage 5. Loading and Initializing Core Drivers. + // + + // Initialize the keyboard. + Keyboard* keyboard = new PS2Keyboard(0x60, Interrupt::IRQ1); + if ( !keyboard ) + Panic("Could not allocate PS2 Keyboard driver"); + KeyboardLayout* kblayout = new KBLayoutUS; + if ( !kblayout ) + Panic("Could not allocate keyboard layout driver"); + + // Register the kernel terminal as /dev/tty. + Ref tty(new LogTerminal(slashdev->dev, 0666, 0, 0, keyboard, kblayout)); + if ( !tty ) + Panic("Could not allocate a kernel terminal"); + if ( LinkInodeInDir(&ctx, slashdev, "tty", tty) != 0 ) + Panic("Unable to link /dev/tty to kernel terminal."); + + // Initialize the COM ports. + COM::Init("/dev", slashdev); + + // Initialize the VGA driver. + VGA::Init("/dev", slashdev); + + // Initialize the sound driver. + Sound::Init(); + + // Initialize the IO system. + IO::Init(); + + // Initialize the pipe system. + Pipe::Init(); + + // Initialize the kernel information query syscall. + Info::Init(); + + // Initialize the Video Driver framework. + Video::Init(textbufhandle); + + // Search for PCI devices and load their drivers. + PCI::Init(); + + // Initialize ATA devices. + ATA::Init("/dev", slashdev); + + // Initialize the BGA driver. + BGA::Init(); + + // + // Stage 6. Executing Hosted Environment ("User-Space") + // // Finally, let's transfer control to a new kernel process that will // eventually run user-space code known as the operating system. addr_t initaddrspace = Memory::Fork(); @@ -350,11 +423,18 @@ static void BootThread(void* /*user*/) CurrentProcess()->AddChildProcess(init); + // TODO: Why don't we fork from pid=0 and this is done for us? + // TODO: Fork dtable and mtable, don't share them! + init->BootstrapTables(dtable, mtable); + dtable.Reset(); + mtable.Reset(); + init->BootstrapDirectories(droot); init->addrspace = initaddrspace; Scheduler::SetInitProcess(init); Thread* initthread = RunKernelThread(init, InitThread, NULL); - if ( !initthread ) { Panic("Coul not create init thread"); } + if ( !initthread ) + Panic("Could not create init thread"); // Wait until init init is done and then shut down the computer. int status; @@ -373,6 +453,14 @@ static void BootThread(void* /*user*/) } } +#if defined(PLATFORM_X86) + #define CPUTYPE_STR "i486-sortix" +#elif defined(PLATFORM_X64) + #define CPUTYPE_STR "x86_64-sortix" +#else + #error No cputype environmental variable provided here. +#endif + static void InitThread(void* /*user*/) { // We are the init process's first thread. Let's load the init program from @@ -382,12 +470,36 @@ static void InitThread(void* /*user*/) Thread* thread = CurrentThread(); Process* process = CurrentProcess(); - uint32_t inode = InitRD::Traverse(InitRD::Root(), "init"); - if ( !inode ) { Panic("InitRD did not contain an 'init' program."); } + const char* initpath = "/" CPUTYPE_STR "/bin/init"; - size_t programsize; - uint8_t* program = InitRD::Open(inode, &programsize); - if ( !program ) { Panic("InitRD did not contain an 'init' program."); } + ioctx_t ctx; SetupKernelIOCtx(&ctx); + Ref root = CurrentProcess()->GetRoot(); + Ref init = root->open(&ctx, initpath, O_EXEC); + if ( !init ) + PanicF("Could not open %s in early kernel RAM filesystem:\n%s", + initpath, strerror(errno)); + struct stat st; + if ( init->stat(&ctx, &st) ) + PanicF("Could not stat '%s' in initrd.", initpath); + assert(0 <= st.st_size); + if ( (uintmax_t) SIZE_MAX < (uintmax_t) st.st_size ) + PanicF("%s is bigger than SIZE_MAX.", initpath); + size_t programsize = st.st_size; + uint8_t* program = new uint8_t[programsize]; + if ( !program ) + PanicF("Unable to allocate 0x%zx bytes needed for %s.", programsize, initpath); + size_t sofar = 0; + while ( sofar < programsize ) + { + ssize_t numbytes = init->read(&ctx, program+sofar, programsize-sofar); + if ( !numbytes ) + PanicF("Premature EOF when reading %s.", initpath); + if ( numbytes < 0 ) + PanicF("IO error when reading %s.", initpath); + sofar += numbytes; + } + + init.Reset(); const size_t DEFAULT_STACK_SIZE = 64UL * 1024UL; @@ -399,32 +511,23 @@ static void InitThread(void* /*user*/) int prot = PROT_FORK | PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE; if ( !Memory::MapRange(stackpos, stacksize, prot) ) - { Panic("Could not allocate init stack memory"); - } thread->stackpos = stackpos; thread->stacksize = stacksize; int argc = 1; const char* argv[] = { "init", NULL }; -#if defined(PLATFORM_X86) - const char* cputype = "cputype=i486-sortix"; -#elif defined(PLATFORM_X64) - const char* cputype = "cputype=x86_64-sortix"; -#else - #warning No cputype environmental variable provided here. - const char* cputype = "cputype=unknown-sortix"; -#endif + const char* cputype = "cputype=" CPUTYPE_STR; int envc = 1; const char* envp[] = { cputype, NULL }; CPU::InterruptRegisters regs; - if ( process->Execute("init", program, programsize, argc, argv, envc, envp, - ®s) ) - { - Panic("Unable to execute init program"); - } + if ( process->Execute(initpath, program, programsize, argc, argv, envc, + envp, ®s) ) + PanicF("Unable to execute %s.", initpath); + + delete[] program; // Now become the init process and the operation system shall run. CPU::LoadRegisters(®s); diff --git a/sortix/logterminal.cpp b/sortix/logterminal.cpp index 47e32576..02cd0a9d 100644 --- a/sortix/logterminal.cpp +++ b/sortix/logterminal.cpp @@ -23,303 +23,294 @@ *******************************************************************************/ #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include #include "utf8.h" -#include "keyboard.h" #include "process.h" #include "scheduler.h" -#include "terminal.h" #include "logterminal.h" -namespace Sortix +namespace Sortix { + +const unsigned SUPPORTED_MODES = TERMMODE_KBKEY + | TERMMODE_UNICODE + | TERMMODE_SIGNAL + | TERMMODE_UTF8 + | TERMMODE_LINEBUFFER + | TERMMODE_ECHO + | TERMMODE_NONBLOCK; + +LogTerminal::LogTerminal(dev_t dev, mode_t mode, uid_t owner, gid_t group, + Keyboard* keyboard, KeyboardLayout* kblayout) { - const unsigned SUPPORTED_MODES = TERMMODE_KBKEY - | TERMMODE_UNICODE - | TERMMODE_SIGNAL - | TERMMODE_UTF8 - | TERMMODE_LINEBUFFER - | TERMMODE_ECHO - | TERMMODE_NONBLOCK; - - LogTerminal::LogTerminal(Keyboard* keyboard, KeyboardLayout* kblayout) - { - this->keyboard = keyboard; - this->kblayout = kblayout; - this->partiallywritten = 0; - this->control = false; - this->mode = TERMMODE_UNICODE - | TERMMODE_SIGNAL - | TERMMODE_UTF8 - | TERMMODE_LINEBUFFER - | TERMMODE_ECHO; - this->termlock = KTHREAD_MUTEX_INITIALIZER; - this->datacond = KTHREAD_COND_INITIALIZER; - this->numwaiting = 0; - this->numeofs = 0; - keyboard->SetOwner(this, NULL); - } - - LogTerminal::~LogTerminal() - { - delete keyboard; - delete kblayout; - } - - bool LogTerminal::SetMode(unsigned newmode) - { - ScopedLock lock(&termlock); - unsigned oldmode = mode; - if ( oldmode & ~SUPPORTED_MODES ) { errno = ENOSYS; return false; } - bool oldutf8 = mode & TERMMODE_UTF8; - bool newutf8 = newmode & TERMMODE_UTF8; - if ( oldutf8 ^ newutf8 ) { partiallywritten = 0; } - mode = newmode; - if ( !(newmode & TERMMODE_LINEBUFFER) ) { CommitLineBuffer(); } - partiallywritten = 0; - return true; - } - - bool LogTerminal::SetWidth(unsigned width) - { - ScopedLock lock(&termlock); - if ( width != GetWidth() ) { errno = ENOTSUP; return false; } - return true; - } - - bool LogTerminal::SetHeight(unsigned height) - { - ScopedLock lock(&termlock); - if ( height != GetHeight() ) { errno = ENOTSUP; return false; } - return true; - } - - unsigned LogTerminal::GetMode() const - { - ScopedLock lock(&termlock); - return mode; - } - - unsigned LogTerminal::GetWidth() const - { - return (unsigned) Log::Width(); - } - - unsigned LogTerminal::GetHeight() const - { - return (unsigned) Log::Height(); - } - - void LogTerminal::OnKeystroke(Keyboard* kb, void* /*user*/) - { - while ( kb->HasPending() ) - { - ProcessKeystroke(kb->Read()); - } - } - - void LogTerminal::ProcessKeystroke(int kbkey) - { - if ( !kbkey ) { return; } - - ScopedLock lock(&termlock); - - if ( kbkey == KBKEY_LCTRL ) { control = true; } - if ( kbkey == -KBKEY_LCTRL ) { control = false; } - if ( mode & TERMMODE_SIGNAL && control && kbkey == KBKEY_C ) - { - while ( linebuffer.CanBackspace() ) - linebuffer.Backspace(); - pid_t pid = Process::HackGetForegroundProcess(); - Process* process = Process::Get(pid); - if ( process ) - process->DeliverSignal(SIGINT); - return; - } - if ( mode & TERMMODE_SIGNAL && control && kbkey == KBKEY_D ) - { - if ( !linebuffer.CanPop() ) - { - numeofs++; -#ifdef GOT_ACTUAL_KTHREAD - if ( numwaiting ) - kthread_cond_broadcast(&datacond); -#else - queuecommitevent.Signal(); -#endif - } - return; - } - - - uint32_t unikbkey = KBKEY_ENCODE(kbkey); - QueueUnicode(unikbkey); - uint32_t unicode = kblayout->Translate(kbkey); - if ( 0 < kbkey ) { QueueUnicode(unicode); } - } - - void LogTerminal::QueueUnicode(uint32_t unicode) - { - if ( !unicode ) { return; } - - int kbkey = KBKEY_DECODE(unicode); - int abskbkey = (kbkey < 0) ? -kbkey : kbkey; - bool wasenter = kbkey == KBKEY_ENTER || unicode == '\n'; - bool kbkeymode = mode & TERMMODE_KBKEY; - bool unicodemode = mode && TERMMODE_UNICODE; - bool linemode = mode & TERMMODE_LINEBUFFER; - bool echomode = mode & TERMMODE_ECHO; - - if ( linemode && abskbkey == KBKEY_BKSPC ) { return; } - while ( linemode && unicode == '\b' ) - { - if ( !linebuffer.CanBackspace() ) { return; } - uint32_t delchar = linebuffer.Backspace(); - bool waskbkey = KBKEY_DECODE(delchar); - bool wasuni = !waskbkey; - if ( waskbkey && !kbkeymode ) { continue; } - if ( wasuni && !unicodemode ) { continue; } - if ( !echomode ) { return; } - if ( wasuni ) { Log::Print("\b"); } - return; - } - - if ( !linebuffer.Push(unicode) ) - { - Log::PrintF("Warning: LogTerminal driver dropping keystroke due " - "to insufficient buffer space\n"); - return; - } - - // TODO: Could it be the right thing to print the unicode character even - // if it is unprintable (it's an encoded keystroke)? - if ( !KBKEY_DECODE(unicode) && echomode ) - { - char utf8buf[6]; - unsigned numbytes = UTF8::Encode(unicode, utf8buf); - Log::PrintData(utf8buf, numbytes); - } - - bool commit = !linemode || wasenter; - if ( commit ) { CommitLineBuffer(); } - } - - void LogTerminal::CommitLineBuffer() - { - linebuffer.Commit(); -#ifdef GOT_ACTUAL_KTHREAD - if ( numwaiting ) - kthread_cond_broadcast(&datacond); -#else - queuecommitevent.Signal(); -#endif - } - - ssize_t LogTerminal::Read(uint8_t* dest, size_t count) - { - ScopedLockSignal lock(&termlock); - if ( !lock.IsAcquired() ) { errno = EINTR; return -1; } - size_t sofar = 0; - size_t left = count; -#ifdef GOT_ACTUAL_KTHREAD - bool blocking = !(mode & TERMMODE_NONBLOCK); - while ( left && !linebuffer.CanPop() && blocking && !numeofs ) - { - numwaiting++; - bool abort = !kthread_cond_wait_signal(&datacond, &termlock); - numwaiting--; - if ( abort ) { errno = EINTR; return -1; } - } - if ( left && !linebuffer.CanPop() && !blocking && !numeofs ) - { - errno = EWOULDBLOCK; - return -1; - } -#endif - if ( numeofs ) - { - numeofs--; - return 0; - } - while ( left && linebuffer.CanPop() ) - { - uint32_t codepoint = linebuffer.Peek(); - int kbkey = KBKEY_DECODE(codepoint); - - bool ignore = false; - if ( !(mode & TERMMODE_KBKEY) && kbkey ) { ignore = true; } - if ( !(mode & TERMMODE_UNICODE) && !kbkey ) { ignore = true; } - if ( ignore ) { linebuffer.Pop(); continue; } - - uint8_t* buf; - size_t bufsize; - uint8_t codepointbuf[4]; - char utf8buf[6]; - - if ( mode & TERMMODE_UTF8 ) - { - unsigned numbytes = UTF8::Encode(codepoint, utf8buf); - if ( !numbytes ) - { - Log::PrintF("Warning: logterminal driver dropping invalid " - "codepoint 0x%x\n", codepoint); - } - buf = (uint8_t*) utf8buf; - bufsize = numbytes; - } - else - { - codepointbuf[0] = (codepoint >> 24U) & 0xFFU; - codepointbuf[1] = (codepoint >> 16U) & 0xFFU; - codepointbuf[2] = (codepoint >> 8U) & 0xFFU; - codepointbuf[3] = (codepoint >> 0U) & 0xFFU; - buf = (uint8_t*) &codepointbuf; - bufsize = sizeof(codepointbuf); - // TODO: Whoops, the above is big-endian and the user programs - // expect the data to be in the host endian. That's bad. For now - // just send the data in host endian, but this will break when - // terminals are accessed over the network. - buf = (uint8_t*) &codepoint; - } - if ( bufsize < partiallywritten ) { partiallywritten = bufsize; } - buf += partiallywritten; - bufsize -= partiallywritten; - if ( sofar && left < bufsize ) { return sofar; } - size_t amount = left < bufsize ? left : bufsize; - memcpy(dest + sofar, buf, amount); - partiallywritten = (amount < bufsize) ? partiallywritten + amount : 0; - left -= amount; - sofar += amount; - if ( !partiallywritten ) { linebuffer.Pop(); } - } - -#ifdef GOT_FAKE_KTHREAD - // Block if no data were ready. - if ( !sofar ) - { - if ( mode & TERMMODE_NONBLOCK ) { errno = EWOULDBLOCK; } - else { queuecommitevent.Register(); errno = EBLOCKING; } - return -1; - } -#endif - return sofar; - } - - ssize_t LogTerminal::Write(const uint8_t* src, size_t count) - { - Log::PrintData(src, count); - return count; - } - - bool LogTerminal::IsReadable() - { - return true; - } - - bool LogTerminal::IsWritable() - { - return true; - } + inode_type = INODE_TYPE_TTY; + this->dev = dev; + this->ino = (ino_t) this; + this->type = S_IFCHR; + this->stat_mode = (mode & S_SETABLE) | this->type; + this->stat_uid = owner; + this->stat_gid = group; + this->keyboard = keyboard; + this->kblayout = kblayout; + this->partiallywritten = 0; + this->control = false; + this->termmode = TERMMODE_UNICODE + | TERMMODE_SIGNAL + | TERMMODE_UTF8 + | TERMMODE_LINEBUFFER + | TERMMODE_ECHO; + this->termlock = KTHREAD_MUTEX_INITIALIZER; + this->datacond = KTHREAD_COND_INITIALIZER; + this->numwaiting = 0; + this->numeofs = 0; + keyboard->SetOwner(this, NULL); } + +LogTerminal::~LogTerminal() +{ + delete keyboard; + delete kblayout; +} + +int LogTerminal::settermmode(ioctx_t* /*ctx*/, unsigned newtermmode) +{ + if ( newtermmode & ~SUPPORTED_MODES ) { errno = EINVAL; return -1; } + ScopedLock lock(&termlock); + unsigned oldtermmode = termmode; + bool oldutf8 = oldtermmode & TERMMODE_UTF8; + bool newutf8 = newtermmode & TERMMODE_UTF8; + if ( oldutf8 != newutf8 ) + partiallywritten = 0; + termmode = newtermmode; + if ( (!newtermmode & TERMMODE_LINEBUFFER) ) + CommitLineBuffer(); + partiallywritten = 0; + return 0; +} + +int LogTerminal::gettermmode(ioctx_t* ctx, unsigned* mode) +{ + ScopedLock lock(&termlock); + if ( !ctx->copy_to_dest(mode, &termmode, sizeof(termmode)) ) + return -1; + return 0; +} + +int LogTerminal::tcgetwinsize(ioctx_t* ctx, struct winsize* ws) +{ + struct winsize retws; + memset(&retws, 0, sizeof(retws)); + retws.ws_col = Log::Width(); + retws.ws_row = Log::Height(); + if ( !ctx->copy_to_dest(ws, &retws, sizeof(retws)) ) + return -1; + return 0; +} + +int LogTerminal::sync(ioctx_t* /*ctx*/) +{ + return 0; // Not needed. +} + +void LogTerminal::OnKeystroke(Keyboard* kb, void* /*user*/) +{ + while ( kb->HasPending() ) + ProcessKeystroke(kb->Read()); +} + +void LogTerminal::ProcessKeystroke(int kbkey) +{ + if ( !kbkey ) + return; + + ScopedLock lock(&termlock); + + if ( kbkey == KBKEY_LCTRL ) { control = true; } + if ( kbkey == -KBKEY_LCTRL ) { control = false; } + if ( termmode & TERMMODE_SIGNAL && control && kbkey == KBKEY_C ) + { + while ( linebuffer.CanBackspace() ) + linebuffer.Backspace(); + pid_t pid = Process::HackGetForegroundProcess(); + Process* process = Process::Get(pid); + if ( process ) + process->DeliverSignal(SIGINT); + return; + } + if ( termmode & TERMMODE_SIGNAL && control && kbkey == KBKEY_D ) + { + if ( !linebuffer.CanPop() ) + { + numeofs++; + if ( numwaiting ) + kthread_cond_broadcast(&datacond); + } + return; + } + + uint32_t unikbkey = KBKEY_ENCODE(kbkey); + QueueUnicode(unikbkey); + uint32_t unicode = kblayout->Translate(kbkey); + if ( 0 < kbkey ) + QueueUnicode(unicode); +} + +void LogTerminal::QueueUnicode(uint32_t unicode) +{ + if ( !unicode ) + return; + + int kbkey = KBKEY_DECODE(unicode); + int abskbkey = (kbkey < 0) ? -kbkey : kbkey; + bool wasenter = kbkey == KBKEY_ENTER || unicode == '\n'; + bool kbkeymode = termmode & TERMMODE_KBKEY; + bool unicodemode = termmode && TERMMODE_UNICODE; + bool linemode = termmode & TERMMODE_LINEBUFFER; + bool echomode = termmode & TERMMODE_ECHO; + + if ( linemode && abskbkey == KBKEY_BKSPC ) { return; } + while ( linemode && unicode == '\b' ) + { + if ( !linebuffer.CanBackspace() ) { return; } + uint32_t delchar = linebuffer.Backspace(); + bool waskbkey = KBKEY_DECODE(delchar); + bool wasuni = !waskbkey; + if ( waskbkey && !kbkeymode ) { continue; } + if ( wasuni && !unicodemode ) { continue; } + if ( !echomode ) { return; } + if ( wasuni ) { Log::Print("\b"); } + return; + } + + if ( !linebuffer.Push(unicode) ) + { + Log::PrintF("Warning: LogTerminal driver dropping keystroke due " + "to insufficient buffer space\n"); + return; + } + + // TODO: Could it be the right thing to print the unicode character even + // if it is unprintable (it's an encoded keystroke)? + if ( !KBKEY_DECODE(unicode) && echomode ) + { + char utf8buf[6]; + unsigned numbytes = UTF8::Encode(unicode, utf8buf); + Log::PrintData(utf8buf, numbytes); + } + + bool commit = !linemode || wasenter; + if ( commit ) + CommitLineBuffer(); +} + +void LogTerminal::CommitLineBuffer() +{ + linebuffer.Commit(); + if ( numwaiting ) + kthread_cond_broadcast(&datacond); +} + +ssize_t LogTerminal::read(ioctx_t* ctx, uint8_t* userbuf, size_t count) +{ + ScopedLockSignal lock(&termlock); + if ( !lock.IsAcquired() ) { errno = EINTR; return -1; } + size_t sofar = 0; + size_t left = count; + bool blocking = !(termmode & TERMMODE_NONBLOCK); + while ( left && !linebuffer.CanPop() && blocking && !numeofs ) + { + numwaiting++; + bool abort = !kthread_cond_wait_signal(&datacond, &termlock); + numwaiting--; + if ( abort ) { errno = EINTR; return -1; } + } + if ( left && !linebuffer.CanPop() && !blocking && !numeofs ) + { + errno = EWOULDBLOCK; + return -1; + } + if ( numeofs ) + { + numeofs--; + return 0; + } + while ( left && linebuffer.CanPop() ) + { + uint32_t codepoint = linebuffer.Peek(); + int kbkey = KBKEY_DECODE(codepoint); + + bool ignore = false; + if ( !(termmode & TERMMODE_KBKEY) && kbkey ) { ignore = true; } + if ( !(termmode & TERMMODE_UNICODE) && !kbkey ) { ignore = true; } + if ( ignore ) { linebuffer.Pop(); continue; } + + uint8_t* buf; + size_t bufsize; + uint8_t codepointbuf[4]; + char utf8buf[6]; + + if ( termmode & TERMMODE_UTF8 ) + { + unsigned numbytes = UTF8::Encode(codepoint, utf8buf); + if ( !numbytes ) + { + Log::PrintF("Warning: logterminal driver dropping invalid " + "codepoint 0x%x\n", codepoint); + } + buf = (uint8_t*) utf8buf; + bufsize = numbytes; + } + else + { + codepointbuf[0] = (codepoint >> 24U) & 0xFFU; + codepointbuf[1] = (codepoint >> 16U) & 0xFFU; + codepointbuf[2] = (codepoint >> 8U) & 0xFFU; + codepointbuf[3] = (codepoint >> 0U) & 0xFFU; + buf = (uint8_t*) &codepointbuf; + bufsize = sizeof(codepointbuf); + // TODO: Whoops, the above is big-endian and the user programs + // expect the data to be in the host endian. That's bad. For now + // just send the data in host endian, but this will break when + // terminals are accessed over the network. + buf = (uint8_t*) &codepoint; + } + if ( bufsize < partiallywritten ) { partiallywritten = bufsize; } + buf += partiallywritten; + bufsize -= partiallywritten; + if ( sofar && left < bufsize ) { return sofar; } + size_t amount = left < bufsize ? left : bufsize; + ctx->copy_to_dest(userbuf + sofar, buf, amount); + partiallywritten = (amount < bufsize) ? partiallywritten + amount : 0; + left -= amount; + sofar += amount; + if ( !partiallywritten ) { linebuffer.Pop(); } + } + + return sofar; +} + +ssize_t LogTerminal::write(ioctx_t* ctx, const uint8_t* buf, size_t count) +{ + // TODO: Add support for ioctx to the kernel log. + const size_t BUFFER_SIZE = 64UL; + if ( BUFFER_SIZE < count ) + count = BUFFER_SIZE; + char buffer[BUFFER_SIZE]; + if ( !ctx->copy_from_src(buffer, buf, count) ) + return -1; + Log::PrintData(buf, count); + return count; +} + +} // namespace Sortix diff --git a/sortix/logterminal.h b/sortix/logterminal.h index a3e1ffc9..34850bc0 100644 --- a/sortix/logterminal.h +++ b/sortix/logterminal.h @@ -26,60 +26,49 @@ #define SORTIX_LOGTERMINAL_H #include -#ifdef GOT_FAKE_KTHREAD -#include "event.h" -#endif -#include "stream.h" -#include "terminal.h" -#include "keyboard.h" +#include +#include #include "linebuffer.h" -namespace Sortix +namespace Sortix { + +class LogTerminal : public AbstractInode, public KeyboardOwner { - class LogTerminal : public DevTerminal, public KeyboardOwner - { - public: - LogTerminal(Keyboard* keyboard, KeyboardLayout* kblayout); - virtual ~LogTerminal(); +public: + LogTerminal(dev_t dev, mode_t mode, uid_t owner, gid_t group, + Keyboard* keyboard, KeyboardLayout* kblayout); + virtual ~LogTerminal(); - public: - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); +public: + virtual int sync(ioctx_t* ctx); + virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); + virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); + virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws); + virtual int settermmode(ioctx_t* ctx, unsigned termmode); + virtual int gettermmode(ioctx_t* ctx, unsigned* termmode); - public: - virtual bool SetMode(unsigned mode); - virtual bool SetWidth(unsigned width); - virtual bool SetHeight(unsigned height); - virtual unsigned GetMode() const; - virtual unsigned GetWidth() const; - virtual unsigned GetHeight() const; +public: + virtual void OnKeystroke(Keyboard* keyboard, void* user); - public: - virtual void OnKeystroke(Keyboard* keyboard, void* user); +private: + void ProcessKeystroke(int kbkey); + void QueueUnicode(uint32_t unicode); + void CommitLineBuffer(); - private: - void ProcessKeystroke(int kbkey); - void QueueUnicode(uint32_t unicode); - void CommitLineBuffer(); +private: + mutable kthread_mutex_t termlock; + kthread_cond_t datacond; + size_t numwaiting; + size_t numeofs; + Keyboard* keyboard; + KeyboardLayout* kblayout; + LineBuffer linebuffer; + size_t partiallywritten; + unsigned termmode; + bool control; - private: - mutable kthread_mutex_t termlock; - kthread_cond_t datacond; - size_t numwaiting; - size_t numeofs; - Keyboard* keyboard; - KeyboardLayout* kblayout; - LineBuffer linebuffer; -#ifdef GOT_FAKE_KTHREAD - Event queuecommitevent; -#endif - size_t partiallywritten; - unsigned mode; - bool control; +}; - }; -} +} // namespace Sortix #endif diff --git a/sortix/mount.cpp b/sortix/mount.cpp deleted file mode 100644 index 0ff453f6..00000000 --- a/sortix/mount.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - mount.cpp - Handles system wide mount points and initialization of new file systems. - -*******************************************************************************/ - -#include -#include -#include -#include -#include "mount.h" -#include "fs/ramfs.h" -#include "fs/initfs.h" -#include "fs/devfs.h" - -namespace Sortix -{ - class MountPoint - { - public: - MountPoint(DevFileSystem* fs, char* path); - ~MountPoint(); - - public: - MountPoint* prev; - MountPoint* next; - - public: - DevFileSystem* fs; - char* path; - - }; - - MountPoint::MountPoint(DevFileSystem* fs, char* path) - { - this->path = path; - this->fs = fs; - this->fs->Refer(); - prev = NULL; - next = NULL; - } - - MountPoint::~MountPoint() - { - fs->Unref(); - delete[] path; - } - - namespace Mount - { - MountPoint* root; - - bool MatchesMountPath(const char* path, const char* mount) - { - size_t mountlen = strlen(mount); - if ( !String::StartsWith(path, mount) ) { return false; } - int c = path[mountlen]; - return c == '/' || c == '\0'; - } - - DevFileSystem* WhichFileSystem(const char* path, size_t* pathoffset) - { - DevFileSystem* result = NULL; - *pathoffset = 0; - - for ( MountPoint* tmp = root; tmp; tmp = tmp->next ) - { - if ( MatchesMountPath(path, tmp->path) ) - { - result = tmp->fs; - *pathoffset = strlen(tmp->path); - } - } - - return result; - } - - bool Register(DevFileSystem* fs, const char* path) - { - char* newpath = String::Clone(path); - if ( !newpath ) { return false; } - - MountPoint* mp = new MountPoint(fs, newpath); - if ( !mp ) { delete[] newpath; return false; } - - if ( !root ) { root = mp; return true; } - - if ( strcmp(path, root->path) < 0 ) - { - mp->next = root; - root->prev = mp; - root = mp; - return false; - } - - for ( MountPoint* tmp = root; tmp; tmp = tmp->next ) - { - if ( tmp->next == NULL || strcmp(path, tmp->next->path) < 0 ) - { - mp->next = tmp->next; - tmp->next = mp; - return true; - } - } - - return false; // Shouldn't happen. - } - - void Init() - { - root = NULL; - - DevFileSystem* rootfs = new DevRAMFS(); - if ( !rootfs || !Register(rootfs, "") ) { Panic("Unable to allocate rootfs"); } - DevFileSystem* initfs = new DevInitFS(); - if ( !initfs || !Register(initfs, "/bin") ) { Panic("Unable to allocate initfs"); } - DevFileSystem* devfs = new DevDevFS(); - if ( !devfs || !Register(devfs, "/dev") ) { Panic("Unable to allocate devfs"); } - } - } -} diff --git a/sortix/mtable.cpp b/sortix/mtable.cpp new file mode 100644 index 00000000..d137651c --- /dev/null +++ b/sortix/mtable.cpp @@ -0,0 +1,85 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + mtable.cpp + Class to keep track of mount points. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +namespace Sortix { + +MountTable::MountTable() +{ + mtablelock = KTHREAD_MUTEX_INITIALIZER; + mounts = NULL; + nummounts = 0; + mountsalloced = 0; +} + +MountTable::~MountTable() +{ + delete[] mounts; +} + +Ref MountTable::Fork() +{ + ScopedLock lock(&mtablelock); + Ref clone(new MountTable); + if ( !clone ) + return Ref(NULL); + clone->mounts = new mountpoint_t[nummounts]; + if ( !clone->mounts ) + return Ref(NULL); + clone->nummounts = nummounts; + clone->mountsalloced = nummounts; + for ( size_t i = 0; i < nummounts; i++ ) + clone->mounts[i] = mounts[i]; + return clone; +} + +bool MountTable::AddMount(ino_t ino, dev_t dev, Ref inode) +{ + ScopedLock lock(&mtablelock); + if ( nummounts == mountsalloced ) + { + size_t newalloced = mountsalloced ? 2UL * mountsalloced : 4UL; + mountpoint_t* newmounts = new mountpoint_t[newalloced]; + if ( !newmounts ) + return false; + for ( size_t i = 0; i < nummounts; i++ ) + newmounts[i] = mounts[i]; + delete[] mounts; + mounts = newmounts; + mountsalloced = newalloced; + } + mountpoint_t* mp = mounts + nummounts++; + mp->ino = ino; + mp->dev = dev; + mp->inode = inode; + return true; +} + +} // namespace Sortix diff --git a/sortix/pipe.cpp b/sortix/pipe.cpp index aa725c3f..6f6ed74e 100644 --- a/sortix/pipe.cpp +++ b/sortix/pipe.cpp @@ -24,340 +24,270 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include #include #include -#ifdef GOT_FAKE_KTHREAD -#include "event.h" -#endif #include "signal.h" #include "thread.h" #include "process.h" #include "syscall.h" #include "pipe.h" -namespace Sortix +namespace Sortix { + +class PipeChannel { - class DevPipeStorage : public DevStream +public: + PipeChannel(uint8_t* buffer, size_t buffersize); + ~PipeChannel(); + void StartReading(); + void StartWriting(); + void CloseReading(); + void CloseWriting(); + void PerhapsShutdown(); + ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); + ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); + +private: + kthread_mutex_t pipelock; + kthread_cond_t readcond; + kthread_cond_t writecond; + uint8_t* buffer; + size_t bufferoffset; + size_t bufferused; + size_t buffersize; + bool anyreading; + bool anywriting; + +}; + +PipeChannel::PipeChannel(uint8_t* buffer, size_t buffersize) +{ + pipelock = KTHREAD_MUTEX_INITIALIZER; + readcond = KTHREAD_COND_INITIALIZER; + writecond = KTHREAD_COND_INITIALIZER; + this->buffer = buffer; + this->buffersize = buffersize; + bufferoffset = bufferused = 0; + anyreading = anywriting = false; +} + +PipeChannel::~PipeChannel() +{ + delete[] buffer; +} + +void PipeChannel::StartReading() +{ + ScopedLock lock(&pipelock); + assert(!anyreading); + anyreading = true; +} + +void PipeChannel::StartWriting() +{ + ScopedLock lock(&pipelock); + assert(!anywriting); + anywriting = true; +} + +void PipeChannel::CloseReading() +{ + anyreading = false; + kthread_cond_broadcast(&writecond); + PerhapsShutdown(); +} + +void PipeChannel::CloseWriting() +{ + anywriting = false; + kthread_cond_broadcast(&readcond); + PerhapsShutdown(); +} + +void PipeChannel::PerhapsShutdown() +{ + kthread_mutex_lock(&pipelock); + bool deleteme = !anyreading & !anywriting; + kthread_mutex_unlock(&pipelock); + if ( deleteme ) + delete this; +} + +ssize_t PipeChannel::read(ioctx_t* ctx, uint8_t* buf, size_t count) +{ + ScopedLockSignal lock(&pipelock); + if ( !lock.IsAcquired() ) { errno = EINTR; return -1; } + while ( anywriting && !bufferused ) { - public: - typedef Device BaseClass; - - public: - DevPipeStorage(uint8_t* buffer, size_t buffersize); - ~DevPipeStorage(); - - private: - uint8_t* buffer; - size_t buffersize; - size_t bufferoffset; - size_t bufferused; -#ifdef GOT_FAKE_KTHREAD - Event readevent; - Event writeevent; -#endif - bool anyreading; - bool anywriting; - kthread_mutex_t pipelock; - kthread_cond_t readcond; - kthread_cond_t writecond; - - public: - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - - public: - void NotReading(); - void NotWriting(); - - }; - - DevPipeStorage::DevPipeStorage(uint8_t* buffer, size_t buffersize) - { - this->buffer = buffer; - this->buffersize = buffersize; - this->bufferoffset = 0; - this->bufferused = 0; - this->anyreading = true; - this->anywriting = true; - this->pipelock = KTHREAD_MUTEX_INITIALIZER; - this->readcond = KTHREAD_COND_INITIALIZER; - this->writecond = KTHREAD_COND_INITIALIZER; - } - - DevPipeStorage::~DevPipeStorage() - { - delete[] buffer; - } - - bool DevPipeStorage::IsReadable() { return true; } - bool DevPipeStorage::IsWritable() { return true; } - - ssize_t DevPipeStorage::Read(uint8_t* dest, size_t count) - { - if ( count == 0 ) { return 0; } -#ifdef GOT_ACTUAL_KTHREAD - ScopedLockSignal lock(&pipelock); - if ( !lock.IsAcquired() ) { errno = EINTR; return -1; } - while ( anywriting && !bufferused ) + if ( !kthread_cond_wait_signal(&readcond, &pipelock) ) { - if ( !kthread_cond_wait_signal(&readcond, &pipelock) ) - { - errno = EINTR; - return -1; - } - } - if ( !bufferused && !anywriting ) { return 0; } - if ( bufferused < count ) { count = bufferused; } - size_t amount = count; - size_t linear = buffersize - bufferoffset; - if ( linear < amount ) { amount = linear; } - assert(amount); - memcpy(dest, buffer + bufferoffset, amount); - bufferoffset = (bufferoffset + amount) % buffersize; - bufferused -= amount; - kthread_cond_broadcast(&writecond); - return amount; -#else - if ( bufferused ) - { - if ( bufferused < count ) { count = bufferused; } - size_t amount = count; - size_t linear = buffersize - bufferoffset; - if ( linear < amount ) { amount = linear; } - assert(amount); - memcpy(dest, buffer + bufferoffset, amount); - bufferoffset = (bufferoffset + amount) % buffersize; - bufferused -= amount; - writeevent.Signal(); - return amount; - } - - if ( !anywriting ) { return 0; } - - errno = EBLOCKING; - readevent.Register(); - return -1; -#endif - } - - ssize_t DevPipeStorage::Write(const uint8_t* src, size_t count) - { - if ( count == 0 ) { return 0; } -#ifdef GOT_ACTUAL_KTHREAD - ScopedLockSignal lock(&pipelock); - if ( !lock.IsAcquired() ) { errno = EINTR; return -1; } - while ( anyreading && bufferused == buffersize ) - { - if ( !kthread_cond_wait_signal(&writecond, &pipelock) ) - { - errno = EINTR; - return -1; - } - } - if ( !anyreading ) - { - CurrentThread()->DeliverSignal(SIGPIPE); - errno = EPIPE; + errno = EINTR; return -1; } - if ( buffersize - bufferused < count ) { count = buffersize - bufferused; } - size_t writeoffset = (bufferoffset + bufferused) % buffersize; - size_t amount = count; - size_t linear = buffersize - writeoffset; - if ( linear < amount ) { amount = linear; } - assert(amount); - memcpy(buffer + writeoffset, src, amount); - bufferused += amount; - kthread_cond_broadcast(&readcond); - return amount; -#else - if ( bufferused < buffersize ) - { - if ( buffersize - bufferused < count ) { count = buffersize - bufferused; } - size_t writeoffset = (bufferoffset + bufferused) % buffersize; - size_t amount = count; - size_t linear = buffersize - writeoffset; - if ( linear < amount ) { amount = linear; } - assert(amount); - memcpy(buffer + writeoffset, src, amount); - bufferused += amount; - readevent.Signal(); - return amount; - } - - errno = EBLOCKING; - writeevent.Register(); - return -1; -#endif - } - - void DevPipeStorage::NotReading() - { - ScopedLock lock(&pipelock); - anyreading = false; - kthread_cond_broadcast(&readcond); - } - - void DevPipeStorage::NotWriting() - { - ScopedLock lock(&pipelock); - anywriting = false; - kthread_cond_broadcast(&writecond); - } - - class DevPipeReading : public DevStream - { - public: - typedef Device BaseClass; - - public: - DevPipeReading(DevStream* stream); - ~DevPipeReading(); - - private: - DevStream* stream; - - public: - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - - }; - - DevPipeReading::DevPipeReading(DevStream* stream) - { - stream->Refer(); - this->stream = stream; - } - - DevPipeReading::~DevPipeReading() - { - ((DevPipeStorage*) stream)->NotReading(); - stream->Unref(); - } - - ssize_t DevPipeReading::Read(uint8_t* dest, size_t count) - { - return stream->Read(dest, count); - } - - ssize_t DevPipeReading::Write(const uint8_t* /*src*/, size_t /*count*/) - { - errno = EBADF; - return -1; - } - - bool DevPipeReading::IsReadable() - { - return true; - } - - bool DevPipeReading::IsWritable() - { - return false; - } - - class DevPipeWriting : public DevStream - { - public: - typedef Device BaseClass; - - public: - DevPipeWriting(DevStream* stream); - ~DevPipeWriting(); - - private: - DevStream* stream; - - public: - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - - }; - - DevPipeWriting::DevPipeWriting(DevStream* stream) - { - stream->Refer(); - this->stream = stream; - } - - DevPipeWriting::~DevPipeWriting() - { - ((DevPipeStorage*) stream)->NotWriting(); - stream->Unref(); - } - - ssize_t DevPipeWriting::Read(uint8_t* /*dest*/, size_t /*count*/) - { - errno = EBADF; - return -1; - } - - ssize_t DevPipeWriting::Write(const uint8_t* src, size_t count) - { - return stream->Write(src, count); - } - - bool DevPipeWriting::IsReadable() - { - return false; - } - - bool DevPipeWriting::IsWritable() - { - return true; - } - - namespace Pipe - { - const size_t BUFFER_SIZE = 4096UL; - - int SysPipe(int pipefd[2]) - { - // TODO: Validate that pipefd is a valid user-space array! - - size_t buffersize = BUFFER_SIZE; - uint8_t* buffer = new uint8_t[buffersize]; - if ( !buffer ) { return -1; /* TODO: ENOMEM */ } - - // Transfer ownership of the buffer to the storage device. - DevStream* storage = new DevPipeStorage(buffer, buffersize); - if ( !storage ) { delete[] buffer; return -1; /* TODO: ENOMEM */ } - - DevStream* reading = new DevPipeReading(storage); - if ( !reading ) { delete storage; return -1; /* TODO: ENOMEM */ } - - DevStream* writing = new DevPipeWriting(storage); - if ( !writing ) { delete reading; return -1; /* TODO: ENOMEM */ } - - Process* process = CurrentProcess(); - int readfd = process->descriptors.Allocate(reading, NULL); - int writefd = process->descriptors.Allocate(writing, NULL); - - if ( readfd < 0 || writefd < 0 ) - { - if ( 0 <= readfd ) { process->descriptors.Free(readfd); } else { delete reading; } - if ( 0 <= writefd ) { process->descriptors.Free(writefd); } else { delete writing; } - - return -1; /* TODO: ENOMEM/EMFILE/ENFILE */ - } - - pipefd[0] = readfd; - pipefd[1] = writefd; - - return 0; - } - - void Init() - { - Syscall::Register(SYSCALL_PIPE, (void*) SysPipe); - } } + if ( !bufferused && !anywriting ) { return 0; } + if ( bufferused < count ) { count = bufferused; } + size_t amount = count; + size_t linear = buffersize - bufferoffset; + if ( linear < amount ) { amount = linear; } + assert(amount); + ctx->copy_to_dest(buf, buffer + bufferoffset, amount); + bufferoffset = (bufferoffset + amount) % buffersize; + bufferused -= amount; + kthread_cond_broadcast(&writecond); + return amount; } + +ssize_t PipeChannel::write(ioctx_t* ctx, const uint8_t* buf, size_t count) +{ + ScopedLockSignal lock(&pipelock); + if ( !lock.IsAcquired() ) { errno = EINTR; return -1; } + while ( anyreading && bufferused == buffersize ) + { + if ( !kthread_cond_wait_signal(&writecond, &pipelock) ) + { + errno = EINTR; + return -1; + } + } + if ( !anyreading ) + { + CurrentThread()->DeliverSignal(SIGPIPE); + errno = EPIPE; + return -1; + } + if ( buffersize - bufferused < count ) { count = buffersize - bufferused; } + size_t writeoffset = (bufferoffset + bufferused) % buffersize; + size_t amount = count; + size_t linear = buffersize - writeoffset; + if ( linear < amount ) { amount = linear; } + assert(amount); + ctx->copy_from_src(buffer + writeoffset, buf, amount); + bufferused += amount; + kthread_cond_broadcast(&readcond); + return amount; +} + +class PipeEndpoint : public AbstractInode +{ +public: + PipeEndpoint(dev_t dev, uid_t owner, gid_t group, mode_t mode, + PipeChannel* channel, bool reading); + ~PipeEndpoint(); + virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); + virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); + +private: + kthread_mutex_t pipelock; + PipeChannel* channel; + bool reading; + +}; + +PipeEndpoint::PipeEndpoint(dev_t dev, uid_t owner, gid_t group, mode_t mode, + PipeChannel* channel, bool reading) +{ + inode_type = INODE_TYPE_STREAM; + this->dev = dev; + this->ino = (ino_t) this; + this->channel = channel; + this->reading = reading; + if ( reading ) + channel->StartReading(); + else + channel->StartWriting(); + pipelock = KTHREAD_MUTEX_INITIALIZER; + this->stat_uid = owner; + this->stat_gid = group; + this->type = S_IFCHR; + this->stat_mode = (mode & S_SETABLE) | this->type; +} + +PipeEndpoint::~PipeEndpoint() +{ + if ( reading ) + channel->CloseReading(); + else + channel->CloseWriting(); +} + +ssize_t PipeEndpoint::read(ioctx_t* ctx, uint8_t* buf, size_t count) +{ + if ( !reading ) { errno = EBADF; return -1; } + return channel->read(ctx, buf, count); +} + +ssize_t PipeEndpoint::write(ioctx_t* ctx, const uint8_t* buf, size_t count) +{ + if ( reading ) { errno = EBADF; return -1; } + return channel->write(ctx, buf, count); +} + +namespace Pipe { + +const size_t BUFFER_SIZE = 4096UL; + +static int sys_pipe(int pipefd[2]) +{ + Process* process = CurrentProcess(); + uid_t uid = process->uid; + uid_t gid = process->gid; + mode_t mode = 0600; + + size_t buffersize = BUFFER_SIZE; + uint8_t* buffer = new uint8_t[buffersize]; + if ( !buffer ) return -1; + + PipeChannel* channel = new PipeChannel(buffer, buffersize); + if ( !channel ) { delete[] buffer; return -1; } + + Ref recv_inode(new PipeEndpoint(0, uid, gid, mode, channel, true)); + if ( !recv_inode ) { delete channel; return -1; } + Ref send_inode(new PipeEndpoint(0, uid, gid, mode, channel, false)); + if ( !send_inode ) return -1; + + Ref recv_vnode(new Vnode(recv_inode, Ref(NULL), 0, 0)); + Ref send_vnode(new Vnode(send_inode, Ref(NULL), 0, 0)); + if ( !recv_vnode || !send_vnode ) return -1; + + Ref recv_desc(new Descriptor(recv_vnode, 0)); + Ref send_desc(new Descriptor(send_vnode, 0)); + if ( !recv_desc || !send_desc ) return -1; + + Ref dtable = process->GetDTable(); + + int recv_index, send_index; + if ( 0 <= (recv_index = dtable->Allocate(recv_desc, 0)) ) + { + if ( 0 <= (send_index = dtable->Allocate(send_desc, 0)) ) + { + int ret[2] = { recv_index, send_index }; + if ( CopyToUser(pipefd, ret, sizeof(ret)) ) + return 0; + + dtable->Free(send_index); + } + dtable->Free(recv_index); + } + + return -1; +} + +void Init() +{ + Syscall::Register(SYSCALL_PIPE, (void*) sys_pipe); +} + +} // namespace Pipe +} // namespace Sortix diff --git a/sortix/pipe.h b/sortix/pipe.h index faf5e173..0b34226e 100644 --- a/sortix/pipe.h +++ b/sortix/pipe.h @@ -22,18 +22,19 @@ *******************************************************************************/ - #ifndef SORTIX_PIPE_H #define SORTIX_PIPE_H -#include "stream.h" +namespace Sortix { -namespace Sortix -{ - namespace Pipe - { - void Init(); - } -} +class Inode; + +namespace Pipe { + +void Init(); +bool CreatePipes(Inode* pipes[2]); + +} // namespace Pipe +} // namespace Sortix #endif diff --git a/sortix/process.cpp b/sortix/process.cpp index 79eb6f6c..3672139f 100644 --- a/sortix/process.cpp +++ b/sortix/process.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. This file is part of Sortix. @@ -24,11 +24,19 @@ #include #include +#include +#include +#include +#include +#include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -38,10 +46,6 @@ #include #include "thread.h" #include "process.h" -#include "device.h" -#include "stream.h" -#include "filesystem.h" -#include "directory.h" #include "scheduler.h" #include "initrd.h" #include "elf.h" @@ -113,7 +117,7 @@ namespace Sortix nozombify = false; firstthread = NULL; threadlock = KTHREAD_MUTEX_INITIALIZER; - workingdir = NULL; + ptrlock = KTHREAD_MUTEX_INITIALIZER; mmapfrom = 0x80000000UL; exitstatus = -1; pid = AllocatePID(); @@ -126,9 +130,30 @@ namespace Sortix assert(!firstchild); assert(!addrspace); assert(!segments); + assert(!dtable); + assert(!mtable); + assert(!cwd); + assert(!root); Remove(this); - delete[] workingdir; + } + + void Process::BootstrapTables(Ref dtable, Ref mtable) + { + ScopedLock lock(&ptrlock); + assert(!this->dtable); + assert(!this->mtable); + this->dtable = dtable; + this->mtable = mtable; + } + + void Process::BootstrapDirectories(Ref root) + { + ScopedLock lock(&ptrlock); + assert(!this->root); + assert(!this->cwd); + this->root = root; + this->cwd = root; } void Process__OnLastThreadExit(void* user); @@ -205,7 +230,11 @@ namespace Sortix addr_t prevaddrspace = curthread->SwitchAddressSpace(addrspace); ResetAddressSpace(); - descriptors.Reset(); + + if ( dtable ) dtable.Reset(); + if ( cwd ) cwd.Reset(); + if ( root ) root.Reset(); + if ( mtable ) mtable.Reset(); // Destroy the address space and safely switch to the replacement // address space before things get dangerous. @@ -436,6 +465,48 @@ namespace Sortix firstchild = child; } + Ref Process::GetMTable() + { + ScopedLock lock(&ptrlock); + assert(mtable); + return mtable; + } + + Ref Process::GetDTable() + { + ScopedLock lock(&ptrlock); + assert(dtable); + return dtable; + } + + Ref Process::GetRoot() + { + ScopedLock lock(&ptrlock); + assert(root); + return root; + } + + Ref Process::GetCWD() + { + ScopedLock lock(&ptrlock); + assert(cwd); + return cwd; + } + + void Process::SetCWD(Ref newcwd) + { + ScopedLock lock(&ptrlock); + assert(newcwd); + cwd = newcwd; + } + + Ref Process::GetDescriptor(int fd) + { + ScopedLock lock(&ptrlock); + assert(dtable); + return dtable->Get(fd); + } + Process* Process::Fork() { assert(CurrentProcess() == this); @@ -475,16 +546,30 @@ namespace Sortix // Remember the relation to the child process. AddChildProcess(clone); - bool failure = false; - - if ( !descriptors.Fork(&clone->descriptors) ) - failure = true; - + // Initialize everything that is safe and can't fail. clone->mmapfrom = mmapfrom; - clone->workingdir = NULL; - if ( workingdir && !(clone->workingdir = String::Clone(workingdir)) ) + kthread_mutex_lock(&ptrlock); + clone->root = root; + clone->cwd = cwd; + kthread_mutex_unlock(&ptrlock); + + // Initialize things that can fail and abort if needed. + bool failure = false; + + kthread_mutex_lock(&ptrlock); + if ( !(clone->dtable = dtable->Fork()) ) failure = true; + //if ( !(clone->mtable = mtable->Fork()) ) + // failure = true; + clone->mtable = mtable; + kthread_mutex_unlock(&ptrlock); + + if ( pid == 1) + assert(dtable->Get(1)); + + if ( pid == 1) + assert(clone->dtable->Get(1)); // If the proces creation failed, ask the process to commit suicide and // not become a zombie, as we don't wait for it to exit. It will clean @@ -560,27 +645,20 @@ namespace Sortix stackpos = envppos - envpsize; - descriptors.OnExecute(); + dtable->OnExecute(); ExecuteCPU(argc, stackargv, envc, stackenvp, stackpos, entry, regs); return 0; } - DevBuffer* OpenProgramImage(const char* progname, const char* wd, const char* path) + // TODO. This is a hack. Please remove this when execve is moved to another + // file/class, it doesn't belong here, it's a program loader ffs! + Ref Process::Open(ioctx_t* ctx, const char* path, int flags, mode_t mode) { - (void) wd; - (void) path; - char* abs = Directory::MakeAbsolute("/", progname); - if ( !abs ) { errno = ENOMEM; return NULL; } - - // TODO: Use O_EXEC here! - Device* dev = FileSystem::Open(abs, O_RDONLY, 0); - delete[] abs; - - if ( !dev ) { return NULL; } - if ( !dev->IsType(Device::BUFFER) ) { errno = EACCES; dev->Unref(); return NULL; } - return (DevBuffer*) dev; + // TODO: Locking the root/cwd pointers. How should that be arranged? + Ref dir = path[0] == '/' ? root : cwd; + return dir->open(ctx, path, flags, mode); } int SysExecVE(const char* _filename, char* const _argv[], char* const _envp[]) @@ -590,8 +668,9 @@ namespace Sortix int envc; char** argv; char** envp; - DevBuffer* dev; - uintmax_t needed; + ioctx_t ctx; + Ref desc; + struct stat st; size_t sofar; size_t count; uint8_t* buffer; @@ -627,24 +706,25 @@ namespace Sortix if ( !envp[i] ) { goto cleanup_envp; } } - dev = OpenProgramImage(filename, process->workingdir, "/bin"); - if ( !dev ) { goto cleanup_envp; } + SetupKernelIOCtx(&ctx); - dev->Refer(); // TODO: Rules of GC may change soon. - needed = dev->Size(); - if ( SIZE_MAX < needed ) { errno = ENOMEM; goto cleanup_dev; } + // TODO: Somehow mark the executable as busy and don't permit writes? + desc = process->Open(&ctx, filename, O_RDONLY, 0); + if ( !desc ) { goto cleanup_envp; } - if ( !dev->IsReadable() ) { errno = EBADF; goto cleanup_dev; } + if ( desc->stat(&ctx, &st) ) { goto cleanup_desc; } + if ( st.st_size < 0 ) { errno = EINVAL; goto cleanup_desc; } + if ( SIZE_MAX < (uintmax_t) st.st_size ) { errno = ERANGE; goto cleanup_desc; } - count = needed; + count = (size_t) st.st_size; buffer = new uint8_t[count]; - if ( !buffer ) { goto cleanup_dev; } + if ( !buffer ) { goto cleanup_desc; } sofar = 0; while ( sofar < count ) { - ssize_t bytesread = dev->Read(buffer + sofar, count - sofar); + ssize_t bytesread = desc->read(&ctx, buffer + sofar, count - sofar); if ( bytesread < 0 ) { goto cleanup_buffer; } - if ( bytesread == 0 ) { errno = EEOF; return -1; } + if ( bytesread == 0 ) { errno = EEOF; goto cleanup_buffer; } sofar += bytesread; } @@ -653,8 +733,8 @@ namespace Sortix cleanup_buffer: delete[] buffer; - cleanup_dev: - dev->Unref(); + cleanup_desc: + desc.Reset(); cleanup_envp: for ( int i = 0; i < envc; i++) { delete[] envp[i]; } delete[] envp; diff --git a/sortix/process.h b/sortix/process.h index 9af66a6a..d0977bd3 100644 --- a/sortix/process.h +++ b/sortix/process.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. This file is part of Sortix. @@ -25,16 +25,21 @@ #ifndef SORTIX_PROCESS_H #define SORTIX_PROCESS_H -#include "descriptors.h" #include "cpu.h" #include +#include #include namespace Sortix { class Thread; class Process; + class Descriptor; + class DescriptorTable; + class MountTable; struct ProcessSegment; + struct ioctx_struct; + typedef struct ioctx_struct ioctx_t; const int SEG_NONE = 0; const int SEG_TEXT = 1; @@ -76,8 +81,28 @@ namespace Sortix public: addr_t addrspace; - char* workingdir; pid_t pid; + uid_t uid; + gid_t gid; + + private: + kthread_mutex_t ptrlock; + Ref root; + Ref cwd; + Ref mtable; + Ref dtable; + + public: + void BootstrapTables(Ref dtable, Ref mtable); + void BootstrapDirectories(Ref root); + Ref GetMTable(); + Ref GetDTable(); + Ref GetRoot(); + Ref GetCWD(); + Ref GetDescriptor(int fd); + // TODO: This should be removed, don't call it. + Ref Open(ioctx_t* ctx, const char* path, int flags, mode_t mode = 0); + void SetCWD(Ref newcwd); private: // A process may only access its parent if parentlock is locked. A process @@ -102,7 +127,6 @@ namespace Sortix kthread_mutex_t threadlock; public: - DescriptorTable descriptors; ProcessSegment* segments; public: diff --git a/sortix/refcount.cpp b/sortix/refcount.cpp index 94289881..87b4c1b6 100644 --- a/sortix/refcount.cpp +++ b/sortix/refcount.cpp @@ -29,32 +29,38 @@ namespace Sortix { -Refcounted::Refcounted() +Refcountable::Refcountable() { reflock = KTHREAD_MUTEX_INITIALIZER; - refcount = 1; + refcount = 0; + being_deleted = false; } -Refcounted::~Refcounted() +Refcountable::~Refcountable() { // It's OK to be deleted if our refcount is 1, it won't mess with any // other owners that might need us. assert(refcount <= 1); } -void Refcounted::Refer() +void Refcountable::Refer_Renamed() { ScopedLock lock(&reflock); refcount++; } -void Refcounted::Unref() +void Refcountable::Unref_Renamed() { + assert(!being_deleted); kthread_mutex_lock(&reflock); - bool deleteme = !--refcount; + assert(refcount); + bool deleteme = !refcount || !--refcount; kthread_mutex_unlock(&reflock); if ( deleteme ) + { + being_deleted = true; delete this; + } } } // namespace Sortix diff --git a/sortix/scheduler.cpp b/sortix/scheduler.cpp index 52c3c37f..81b9f0a2 100644 --- a/sortix/scheduler.cpp +++ b/sortix/scheduler.cpp @@ -39,6 +39,9 @@ namespace Sortix { namespace Scheduler { +const uint32_t SCHED_MAGIC = 0x1234567; + +volatile unsigned long premagic; static Thread* currentthread; } // namespace Scheduler Thread* CurrentThread() { return Scheduler::currentthread; } @@ -51,6 +54,7 @@ Thread* idlethread; Thread* firstrunnablethread; Thread* firstsleepingthread; Process* initprocess; +volatile unsigned long postmagic; static inline void SetCurrentThread(Thread* newcurrentthread) { @@ -71,6 +75,8 @@ static Thread* PopNextThread() static Thread* ValidatedPopNextThread() { + assert(premagic == SCHED_MAGIC); + assert(postmagic == SCHED_MAGIC); Thread* nextthread = PopNextThread(); if ( !nextthread ) { Panic("Had no thread to switch to."); } if ( nextthread->terminated ) @@ -120,9 +126,13 @@ static void DoActualSwitch(CPU::InterruptRegisters* regs) void Switch(CPU::InterruptRegisters* regs) { + assert(premagic == SCHED_MAGIC); + assert(postmagic == SCHED_MAGIC); DoActualSwitch(regs); if ( regs->signal_pending && regs->InUserspace() ) Signal::Dispatch(regs); + assert(premagic == SCHED_MAGIC); + assert(postmagic == SCHED_MAGIC); } const bool DEBUG_BEGINCTXSWITCH = false; @@ -271,6 +281,8 @@ extern "C" void thread_exit_handler(); void Init() { + premagic = postmagic = SCHED_MAGIC; + // We use a dummy so that the first context switch won't crash when the // current thread is accessed. This lets us avoid checking whether it is // NULL (which it only will be once), which gives simpler code. diff --git a/sortix/serialterminal.cpp b/sortix/serialterminal.cpp index 6c08d566..12b3f74e 100644 --- a/sortix/serialterminal.cpp +++ b/sortix/serialterminal.cpp @@ -23,9 +23,9 @@ *******************************************************************************/ #include -#include +#include +#include #include "vga.h" -#include "keyboard.h" #include "uart.h" #include "serialterminal.h" #include "scheduler.h" diff --git a/sortix/stream.h b/sortix/stream.h deleted file mode 100644 index d67d811a..00000000 --- a/sortix/stream.h +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - stream.h - Various device types that provides a sequence of bytes. - -*******************************************************************************/ - -#ifndef SORTIX_STREAM_H -#define SORTIX_STREAM_H - -#include "device.h" - -namespace Sortix -{ - class DevStream : public Device - { - public: - typedef Device BaseClass; - - public: - virtual bool IsType(unsigned type) const { return type == Device::STREAM; } - - public: - virtual ssize_t Read(uint8_t* dest, size_t count) = 0; - virtual ssize_t Write(const uint8_t* src, size_t count) = 0; - virtual bool IsReadable() = 0; - virtual bool IsWritable() = 0; - - }; - - class DevBuffer : public DevStream - { - public: - typedef DevStream BaseClass; - - public: - virtual bool IsType(unsigned type) const - { - return type == Device::BUFFER || BaseClass::IsType(type); - } - - public: - virtual size_t BlockSize() = 0; - virtual uintmax_t Size() = 0; - virtual uintmax_t Position() = 0; - virtual bool Seek(uintmax_t position) = 0; - virtual bool Resize(uintmax_t size) = 0; - - }; -} - -#endif diff --git a/sortix/terminal.cpp b/sortix/terminal.cpp deleted file mode 100644 index cf141ebe..00000000 --- a/sortix/terminal.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2012. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - terminal.cpp - Provides an interface to terminals for user-space. - -*******************************************************************************/ - -#include -#include -#include -#include "syscall.h" -#include "process.h" -#include "terminal.h" - -namespace Sortix -{ - int SysSetTermMode(int fd, unsigned mode) - { - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - if ( !dev->IsType(Device::TERMINAL) ) { errno = ENOTTY; return -1; } - DevTerminal* term = (DevTerminal*) dev; - return term->SetMode(mode) ? 0 : -1; - } - - int SysGetTermMode(int fd, unsigned* mode) - { - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - if ( !dev->IsType(Device::TERMINAL) ) { errno = ENOTTY; return -1; } - DevTerminal* term = (DevTerminal*) dev; - // TODO: Check that mode is a valid user-space pointer. - *mode = term->GetMode(); - return 0; - } - - int SysIsATTY(int fd) - { - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; return 0; } - if ( !dev->IsType(Device::TERMINAL) ) { errno = ENOTTY; return 0; } - return 1; - } - - int SysTCGetWinSize(int fd, struct winsize* ws) - { - Process* process = CurrentProcess(); - Device* dev = process->descriptors.Get(fd); - if ( !dev ) { errno = EBADF; return -1; } - if ( !dev->IsType(Device::TERMINAL) ) { errno = ENOTTY; return -1; } - DevTerminal* term = (DevTerminal*) dev; - struct winsize ret; - ret.ws_col = term->GetWidth(); - ret.ws_row = term->GetHeight(); - ret.ws_xpixel = 0; // Not supported by DevTerminal interface. - ret.ws_ypixel = 0; // Not supported by DevTerminal interface. - // TODO: Check that ws is a valid user-space pointer. - *ws = ret; - return 0; - } - - void Terminal::Init() - { - Syscall::Register(SYSCALL_SETTERMMODE, (void*) SysSetTermMode); - Syscall::Register(SYSCALL_GETTERMMODE, (void*) SysGetTermMode); - Syscall::Register(SYSCALL_ISATTY, (void*) SysIsATTY); - Syscall::Register(SYSCALL_TCGETWINSIZE, (void*) SysTCGetWinSize); - } -} diff --git a/sortix/terminal.h b/sortix/terminal.h deleted file mode 100644 index 25cdfb1c..00000000 --- a/sortix/terminal.h +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2012. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - terminal.h - Reads data from an input source (such as a keyboard), optionally does line- - buffering, and redirects data to an output device (such as the VGA). - -*******************************************************************************/ - -#ifndef SORTIX_TERMINAL_H -#define SORTIX_TERMINAL_H - -#include "device.h" -#include "stream.h" -#include - -namespace Sortix -{ - class DevTerminal : public DevStream - { - public: - typedef DevStream BaseClass; - - public: - virtual bool IsType(unsigned type) const - { - return type == Device::TERMINAL || BaseClass::IsType(type); - } - - public: - virtual bool SetMode(unsigned mode) = 0; - virtual bool SetWidth(unsigned width) = 0; - virtual bool SetHeight(unsigned height) = 0; - virtual unsigned GetMode() const = 0; - virtual unsigned GetWidth() const = 0; - virtual unsigned GetHeight() const = 0; - - }; - - namespace Terminal - { - void Init(); - } -} -#endif diff --git a/sortix/textterminal.cpp b/sortix/textterminal.cpp index fdc34216..2d6128d8 100644 --- a/sortix/textterminal.cpp +++ b/sortix/textterminal.cpp @@ -33,9 +33,9 @@ namespace Sortix { const uint16_t DEFAULT_COLOR = COLOR8_LIGHT_GREY << 0U | COLOR8_BLACK << 4U; const uint16_t ATTR_CHAR = 1U << 0U; -TextTerminal::TextTerminal(TextBufferHandle* textbufhandle) +TextTerminal::TextTerminal(Ref textbufhandle) { - this->textbufhandle = textbufhandle; textbufhandle->Refer(); + this->textbufhandle = textbufhandle; this->termlock = KTHREAD_MUTEX_INITIALIZER; Reset(); } diff --git a/sortix/textterminal.h b/sortix/textterminal.h index 2d03c2b3..30c66cee 100644 --- a/sortix/textterminal.h +++ b/sortix/textterminal.h @@ -26,6 +26,7 @@ #define SORTIX_TEXTTERMINAL_H #include +#include namespace Sortix { @@ -34,7 +35,7 @@ class TextBufferHandle; class TextTerminal //: public Printable ? { public: - TextTerminal(TextBufferHandle* textbufhandle); + TextTerminal(Ref textbufhandle); ~TextTerminal(); size_t Print(const char* string, size_t stringlen); size_t Width() const; @@ -52,7 +53,7 @@ private: void Reset(); private: - mutable TextBufferHandle* textbufhandle; + mutable Ref textbufhandle; mutable kthread_mutex_t termlock; uint8_t vgacolor; unsigned column; diff --git a/sortix/vga.cpp b/sortix/vga.cpp index 6155992d..2b1acc9a 100644 --- a/sortix/vga.cpp +++ b/sortix/vga.cpp @@ -23,15 +23,19 @@ *******************************************************************************/ #include +#include +#include +#include +#include +#include +#include #include #include #include "fs/util.h" -#include "fs/devfs.h" #include "vga.h" #include "scheduler.h" #include "syscall.h" #include "process.h" -#include "serialterminal.h" #define TEST_VGAFONT 0 @@ -119,7 +123,7 @@ const uint8_t* GetFont() return vgafont; } -void Init() +void Init(const char* devpath, Ref slashdev) { vgafontsize = VGA_FONT_NUMCHARS * VGA_FONT_CHARSIZE; if ( !(vgafont = new uint8_t[vgafontsize]) ) @@ -129,12 +133,25 @@ void Init() PrintFontChar(vgafont, 'A'); PrintFontChar(vgafont, 'S'); #endif - DevMemoryBuffer* vgamembuf = new DevMemoryBuffer(vgafont, vgafontsize, - false, false); - if ( !vgamembuf ) - Panic("Unable to allocate vga font filesystem object"); - if ( !DeviceFS::RegisterDevice("vgafont", vgamembuf) ) - Panic("Unable to register vga font filesystem object"); + + ioctx_t ctx; SetupKernelIOCtx(&ctx); + + // Setup the vgafont device. + Ref vgafontnode(new UtilMemoryBuffer(slashdev->dev, (ino_t) 0, 0, 0, + 0660, vgafont, vgafontsize, + false, false)); + if ( !vgafontnode ) + PanicF("Unable to allocate %s/vgafont inode.", devpath); + if ( LinkInodeInDir(&ctx, slashdev, "vgafont", vgafontnode) != 0 ) + PanicF("Unable to link %s/vgafont to vga font.", devpath); + + // Setup the vga device. + Ref vganode(new UtilMemoryBuffer(slashdev->dev, (ino_t) 0, 0, 0, + 0660, VGA, VGA_SIZE, true, false)); + if ( !vganode ) + PanicF("Unable to allocate %s/vga inode.", devpath); + if ( LinkInodeInDir(&ctx, slashdev, "vga", vganode) != 0 ) + PanicF("Unable to link %s/vga to vga.", devpath); } // Changes the position of the hardware cursor. @@ -153,82 +170,4 @@ void SetCursor(unsigned x, unsigned y) } } // namespace VGA - -DevVGA::DevVGA() -{ - offset = 0; -} - -DevVGA::~DevVGA() -{ -#ifdef PLATFORM_SERIAL - // TODO: HACK: This is a hack that is unrelated to this file. - // This is a hack to make the cursor a proper color after the vga buffer - // has been radically modified. The best solution would be for the VGA - // to ANSI Escape Codes converter to keep track of colors and restoring - // them, but this will do for now. - Log::PrintF("\e[m"); -#endif -} - -ssize_t DevVGA::Read(uint8_t* dest, size_t count) -{ - if ( VGA::VGA_SIZE - offset < count ) { count = VGA::VGA_SIZE - offset; } - memcpy(dest, VGA::VGA + offset, count); - offset += count; - return count; -} - -ssize_t DevVGA::Write(const uint8_t* src, size_t count) -{ - if ( offset == VGA::VGA_SIZE && count ) { errno = ENOSPC; return -1; } - if ( VGA::VGA_SIZE - offset < count ) { count = VGA::VGA_SIZE - offset; } - memcpy(VGA::VGA + offset, src, count); - offset = (offset + count) % VGA::VGA_SIZE; - VGA::SetCursor(VGA::WIDTH, VGA::HEIGHT-1); -#ifdef PLATFORM_SERIAL - SerialTerminal::OnVGAModified(); -#endif - return count; -} - -bool DevVGA::IsReadable() -{ - return true; -} - -bool DevVGA::IsWritable() -{ - return true; -} - -size_t DevVGA::BlockSize() -{ - return 1; -} - -uintmax_t DevVGA::Size() -{ - return VGA::VGA_SIZE; -} - -uintmax_t DevVGA::Position() -{ - return offset; -} - -bool DevVGA::Seek(uintmax_t position) -{ - if ( VGA::VGA_SIZE < position ) { errno = EINVAL; return false; } - offset = position; - return true; -} - -bool DevVGA::Resize(uintmax_t size) -{ - if ( size == VGA::VGA_SIZE ) { return false; } - errno = ENOSPC; - return false; -} - } // namespace Sortix diff --git a/sortix/vga.h b/sortix/vga.h index 3e1f4008..dd044e07 100644 --- a/sortix/vga.h +++ b/sortix/vga.h @@ -25,11 +25,12 @@ #ifndef SORTIX_VGA_H #define SORTIX_VGA_H -#include "device.h" -#include "stream.h" +#include namespace Sortix { +class Descriptor; + const size_t VGA_FONT_WIDTH = 8UL; const size_t VGA_FONT_HEIGHT = 16UL; const size_t VGA_FONT_NUMCHARS = 256UL; @@ -37,39 +38,12 @@ const size_t VGA_FONT_CHARSIZE = VGA_FONT_WIDTH * VGA_FONT_HEIGHT / 8UL; namespace VGA { -void Init(); +void Init(const char* devpath, Ref slashdev); void SetCursor(unsigned x, unsigned y); const uint8_t* GetFont(); } // namespace VGA -// TODO: This class shouldn't be exposed publicly; it is used in a hack in the -// /dev filesystem. However, vga.cpp should register /dev/vga instead. -class DevVGA : public DevBuffer -{ -public: - typedef DevBuffer BaseClass; - -public: - DevVGA(); - virtual ~DevVGA(); - -private: - size_t offset; - -public: - virtual ssize_t Read(uint8_t* dest, size_t count); - virtual ssize_t Write(const uint8_t* src, size_t count); - virtual bool IsReadable(); - virtual bool IsWritable(); - virtual size_t BlockSize(); - virtual uintmax_t Size(); - virtual uintmax_t Position(); - virtual bool Seek(uintmax_t position); - virtual bool Resize(uintmax_t size); - -}; - } // namespace Sortix #endif diff --git a/sortix/video.cpp b/sortix/video.cpp index c68a383b..0e37eecf 100644 --- a/sortix/video.cpp +++ b/sortix/video.cpp @@ -105,9 +105,9 @@ size_t currentdrvid; bool newdrivers; kthread_mutex_t videolock; -TextBufferHandle* textbufhandle; +Ref textbufhandle; -void Init(TextBufferHandle* thetextbufhandle) +void Init(Ref thetextbufhandle) { videolock = KTHREAD_MUTEX_INITIALIZER; textbufhandle = thetextbufhandle; diff --git a/sortix/vnode.cpp b/sortix/vnode.cpp new file mode 100644 index 00000000..24549b29 --- /dev/null +++ b/sortix/vnode.cpp @@ -0,0 +1,216 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + vnode.cpp + Nodes in the virtual filesystem. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "process.h" + +namespace Sortix { + +static Ref LookupMount(Ref inode) +{ + Ref mtable = CurrentProcess()->GetMTable(); + ScopedLock lock(&mtable->mtablelock); + for ( size_t i = 0; i < mtable->nummounts; i++ ) + { + mountpoint_t* mp = mtable->mounts + i; + if ( mp->ino != inode->ino || mp->dev != inode->dev ) + continue; + return mp->inode; + } + return Ref(NULL); +} + +Vnode::Vnode(Ref inode, Ref mountedat, ino_t rootino, dev_t rootdev) +{ + for ( Ref tmp = mountedat; tmp; tmp = tmp->mountedat ) + assert(tmp != this); + this->inode = inode; + this->mountedat = mountedat; + this->rootino = rootino; + this->rootdev = rootdev; + this->ino = inode->ino; + this->dev = inode->dev; + this->type = inode->type; +} + +Vnode::~Vnode() +{ +} + +Ref Vnode::open(ioctx_t* ctx, const char* filename, int flags, mode_t mode) +{ + // Handle transition across filesystem mount points. + bool isroot = inode->ino == rootino && inode->dev == rootdev; + bool dotdot = strcmp(filename, "..") == 0; + if ( isroot && dotdot && mountedat ) + return mountedat; + + // Move within the current filesystem. + Ref retinode = inode->open(ctx, filename, flags, mode); + if ( !retinode ) { return Ref(NULL); } + Ref retmountedat = mountedat; + ino_t retrootino = rootino; + dev_t retrootdev = rootdev; + + // Check whether we moved into a filesystem mount point. + Ref mounted = LookupMount(retinode); + if ( mounted ) + { + retinode = mounted; + retmountedat = Ref(this); + retrootino = mounted->ino; + retrootdev = mounted->dev; + } + + return Ref(new Vnode(retinode, retmountedat, retrootino, retrootdev)); +} + +int Vnode::sync(ioctx_t* ctx) +{ + return inode->sync(ctx); +} + +int Vnode::stat(ioctx_t* ctx, struct stat* st) +{ + return inode->stat(ctx, st); +} + +int Vnode::chmod(ioctx_t* ctx, mode_t mode) +{ + return inode->chmod(ctx, mode); +} + +int Vnode::chown(ioctx_t* ctx, uid_t owner, gid_t group) +{ + return inode->chown(ctx, owner, group); +} + +int Vnode::truncate(ioctx_t* ctx, off_t length) +{ + return inode->truncate(ctx, length); +} + +off_t Vnode::lseek(ioctx_t* ctx, off_t offset, int whence) +{ + return inode->lseek(ctx, offset, whence); +} + +ssize_t Vnode::read(ioctx_t* ctx, uint8_t* buf, size_t count) +{ + return inode->read(ctx, buf, count); +} + +ssize_t Vnode::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off) +{ + return inode->pread(ctx, buf, count, off); +} + +ssize_t Vnode::write(ioctx_t* ctx, const uint8_t* buf, size_t count) +{ + return inode->write(ctx, buf, count); +} + +ssize_t Vnode::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off) +{ + return inode->pwrite(ctx, buf, count, off); +} + +int Vnode::utimes(ioctx_t* ctx, const struct timeval times[2]) +{ + return inode->utimes(ctx, times); +} + +int Vnode::isatty(ioctx_t* ctx) +{ + return inode->isatty(ctx); +} + +ssize_t Vnode::readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, + size_t size, off_t start, size_t count) +{ + return inode->readdirents(ctx, dirent, size, start, count); +} + +int Vnode::mkdir(ioctx_t* ctx, const char* filename, mode_t mode) +{ + return inode->mkdir(ctx, filename, mode); +} + +int Vnode::unlink(ioctx_t* ctx, const char* filename) +{ + return inode->unlink(ctx, filename); +} + +int Vnode::rmdir(ioctx_t* ctx, const char* filename) +{ + return inode->rmdir(ctx, filename); +} + +int Vnode::link(ioctx_t* ctx, const char* filename, Ref node) +{ + if ( node->inode->dev != inode->dev ) { errno = EXDEV; return -1; } + return inode->link(ctx, filename, node->inode); +} + +int Vnode::symlink(ioctx_t* ctx, const char* oldname, const char* filename) +{ + return inode->symlink(ctx, oldname, filename); +} + +ssize_t Vnode::readlink(ioctx_t* ctx, char* buf, size_t bufsiz) +{ + return inode->readlink(ctx, buf, bufsiz); +} + +int Vnode::fsbind(ioctx_t* /*ctx*/, Vnode* /*node*/, int /*flags*/) +{ + // TODO: Support binding in namespaces. + errno = ENOSYS; + return -1; +} + +int Vnode::tcgetwinsize(ioctx_t* ctx, struct winsize* ws) +{ + return inode->tcgetwinsize(ctx, ws); +} + +int Vnode::settermmode(ioctx_t* ctx, unsigned mode) +{ + return inode->settermmode(ctx, mode); +} + +int Vnode::gettermmode(ioctx_t* ctx, unsigned* mode) +{ + return inode->gettermmode(ctx, mode); +} + +} // namespace Sortix diff --git a/utils/cat.cpp b/utils/cat.cpp index 1c96eb62..08bbfb9b 100644 --- a/utils/cat.cpp +++ b/utils/cat.cpp @@ -43,7 +43,7 @@ int docat(const char* inputname, int fd) } if ( (ssize_t) writeall(1, buffer, bytesread) < bytesread ) { - error(0, errno, "write: %s", inputname); + error(0, errno, "write: ", inputname); return 1; } } while ( true ); diff --git a/utils/cp.cpp b/utils/cp.cpp index be428f5e..28b3355e 100644 --- a/utils/cp.cpp +++ b/utils/cp.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2013. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -20,6 +20,8 @@ *******************************************************************************/ +#include + #include #include #include @@ -40,24 +42,26 @@ int main(int argc, char* argv[]) const char* frompath = argv[1]; const char* topath = argv[2]; - char tobuffer[256]; int fromfd = open(frompath, O_RDONLY); if ( fromfd < 0 ) { error(1, errno, "%s", frompath); return 1; } int tofd = open(topath, O_WRONLY | O_TRUNC | O_CREAT, 0777); if ( tofd < 0 ) - { - if ( errno == EISDIR ) - { - strcpy(tobuffer, topath); - if ( tobuffer[strlen(tobuffer)-1] != '/' ) { strcat(tobuffer, "/"); } - strcat(tobuffer, basename(frompath)); - topath = tobuffer; - tofd = open(topath, O_WRONLY | O_TRUNC | O_CREAT, 0777); - } + error(1, errno, "%s", topath); - if ( tofd < 0 ) { error(1, errno, "%s", topath); return 1; } + struct stat st; + if ( fstat(tofd, &st) ) + error(1, errno, "stat: %s", topath); + + if ( S_ISDIR(st.st_mode) ) + { + int dirfd = tofd; + const char* name = basename(frompath); + tofd = openat(dirfd, name, O_WRONLY | O_TRUNC | O_CREAT, 0777); + close(dirfd); + if ( tofd < 0 ) + error(1, errno, "%s/%s", topath, name); } while ( true ) @@ -67,7 +71,7 @@ int main(int argc, char* argv[]) ssize_t bytesread = read(fromfd, buffer, BUFFER_SIZE); if ( bytesread < 0 ) { error(1, errno, "read: %s", frompath); return 1; } if ( bytesread == 0 ) { return 0; } - if ( (ssize_t) writeall(tofd, buffer, bytesread) < bytesread ) + if ( writeall(tofd, buffer, bytesread) < (size_t) bytesread ) { error(1, errno, "write: %s", topath); return 1; diff --git a/utils/init.cpp b/utils/init.cpp index 52be1eef..187b7243 100644 --- a/utils/init.cpp +++ b/utils/init.cpp @@ -20,7 +20,10 @@ *******************************************************************************/ +#include +#include #include + #include #include #include @@ -77,5 +80,8 @@ int main(int /*argc*/, char* /*argv*/[]) // we are running. setenv("objtype", getenv("cputype"), 0); + // Make sure that we have a /tmp directory. + mkdir("/tmp", 01777); + return runsystem(); }