Refactor kernel VFS.

Note: This is an incompatible ABI change.
This commit is contained in:
Jonas 'Sortie' Termansen 2012-08-08 00:19:44 +02:00
parent 9634a0c3d2
commit 1444683ea8
92 changed files with 4494 additions and 4472 deletions

View File

@ -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

View File

@ -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 \

View File

@ -26,6 +26,14 @@
#include <string.h>
#include <unistd.h>
#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; }

View File

@ -22,6 +22,7 @@
*******************************************************************************/
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
@ -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; }

33
libc/fstatat.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
fstatat.cpp
Retrieves status of an open file.
*******************************************************************************/
#include <sys/stat.h>
#include <sys/syscall.h>
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);
}

View File

@ -22,12 +22,116 @@
*******************************************************************************/
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
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;
}

View File

@ -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

View File

@ -26,21 +26,16 @@
#define _SYS_READDIRENTS_H 1
#include <features.h>
#include <sys/types.h>
#include <stddef.h>
#include <stdint.h>
#include <sortix/dirent.h>
__BEGIN_DECLS
@include(size_t.h)
// Keep this up to date with <sortix/directory.h>
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

View File

@ -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 <sortix/stat.h>
__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

View File

@ -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);

View File

@ -25,10 +25,9 @@
#include <sys/syscall.h>
#include <unistd.h>
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);
}

View File

@ -26,14 +26,11 @@
#include <errno.h>
#include <unistd.h>
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);

View File

@ -25,9 +25,9 @@
#include <sys/readdirents.h>
#include <sys/syscall.h>
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);
}

View File

@ -26,14 +26,11 @@
#include <errno.h>
#include <unistd.h>
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);

View File

@ -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=\

View File

@ -23,12 +23,18 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/interlock.h>
#include <sortix/kernel/kthread.h>
#include "cpu.h"
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/stat.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#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<Descriptor> 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<ATANode> 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<Descriptor> 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<Descriptor> 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)

View File

@ -26,9 +26,11 @@
#define SORTIX_ATA_H
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
namespace Sortix {
class Descriptor;
class ATABus;
class ATADrive;
@ -83,7 +85,7 @@ private:
namespace ATA {
void Init();
void Init(const char* devpath, Ref<Descriptor> slashdev);
ATABus* CreateBus(uint16_t portoffset, uint16_t altport);
} // namespace ATA

View File

@ -24,13 +24,16 @@
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/interlock.h>
#include <sortix/stat.h>
#include <errno.h>
#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<DevCOMPort> 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<Descriptor> 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<DevCOMPort>(); continue; }
comdevices[i] = Ref<DevCOMPort>
(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);

View File

@ -26,12 +26,16 @@
#define SORTIX_COM_H
namespace Sortix {
class Descriptor;
namespace COM {
void EarlyInit();
void Init();
void Init(const char* devpath, Ref<Descriptor> slashdev);
} // namespace COM
} // namespace Sortix
#endif

65
sortix/copy.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
copy.h
The context for io operations: who made it, how should data be copied, etc.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/string.h>
#include <string.h>
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

419
sortix/descriptor.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
descriptor.cpp
A file descriptor.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/vnode.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/fsfunc.h>
#include <sortix/kernel/string.h>
#include <sortix/kernel/copy.h> // DEBUG
#include <sortix/dirent.h>
#include <sortix/fcntl.h>
#include <sortix/seek.h>
#include <sortix/stat.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include "process.h"
namespace Sortix {
bool LinkInodeInDir(ioctx_t* ctx, Ref<Descriptor> dir, const char* name,
Ref<Inode> inode)
{
Ref<Vnode> vnode(new Vnode(inode, Ref<Vnode>(), 0, 0));
if ( !vnode ) return false;
Ref<Descriptor> desc(new Descriptor(Ref<Vnode>(vnode), 0));
if ( !desc ) return false;
return dir->link(ctx, name, desc) != 0;
}
Ref<Descriptor> OpenDirContainingPath(ioctx_t* ctx, Ref<Descriptor> from,
const char* path, char** finalp)
{
if ( !path[0] ) { errno = EINVAL; return Ref<Descriptor>(); }
char* dirpath;
char* final;
if ( !SplitFinalElem(path, &dirpath, &final) )
return Ref<Descriptor>();
// 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<Descriptor> ret = from->open(ctx, dirpath, O_RDONLY | O_DIRECTORY, 0);
delete[] dirpath;
if ( !ret ) { delete[] final; return Ref<Descriptor>(); }
*finalp = final;
return ret;
}
// TODO: Add security checks.
Descriptor::Descriptor(Ref<Vnode> 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> Descriptor::Fork()
{
Ref<Descriptor> ret(new Descriptor(vnode, dflags));
if ( !ret )
return Ref<Descriptor>();
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> Descriptor::open(ioctx_t* ctx, const char* filename, int flags,
mode_t mode)
{
if ( !filename[0] )
return errno = ENOENT, Ref<Descriptor>();
// 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<Descriptor>();
Ref<Descriptor> desc(this);
while ( filename[0] )
{
if ( filename[0] == '/' )
{
if ( !S_ISDIR(desc->type) )
return errno = ENOTDIR, Ref<Descriptor>();
filename++;
continue;
}
size_t slashpos = strcspn(filename, "/");
bool lastelem = filename[slashpos] == '\0';
char* elem = String::Substring(filename, 0, slashpos);
if ( !elem )
return Ref<Descriptor>();
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<Descriptor> next = desc->open_elem(ctx, elem, open_flags, open_mode);
delete[] elem;
if ( !next )
return Ref<Descriptor>();
desc = next;
filename += slashpos;
}
if ( flags & O_DIRECTORY && !S_ISDIR(desc->type) )
return errno = ENOTDIR, Ref<Descriptor>();
// 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> 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<Vnode> retvnode = vnode->open(ctx, filename, next_flags, mode);
if ( !retvnode )
return Ref<Descriptor>();
Ref<Descriptor> ret(new Descriptor(retvnode, flags & O_APPEND));
if ( !ret )
return Ref<Descriptor>();
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<Descriptor> dir = OpenDirContainingPath(ctx, Ref<Descriptor>(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<Descriptor> node)
{
char* final;
Ref<Descriptor> dir = OpenDirContainingPath(ctx, Ref<Descriptor>(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<Descriptor> dir = OpenDirContainingPath(ctx, Ref<Descriptor>(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<Descriptor> dir = OpenDirContainingPath(ctx, Ref<Descriptor>(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<Descriptor> dir = OpenDirContainingPath(ctx, Ref<Descriptor>(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

View File

@ -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 <http://www.gnu.org/licenses/>.
descriptors.cpp
Handles file descriptors, socket descriptors, and whatnot for each process.
********************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/string.h>
#include <assert.h>
#include <string.h>
#include "descriptors.h"
#include "device.h"
#include <sortix/fcntl.h>
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
directory.cpp
Allows access to stored sequences of bytes in an orderly fashion.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/string.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <sys/readdirents.h>
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

198
sortix/dtable.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
dtable.cpp
Table of file descriptors.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/dtable.h>
#include <sortix/fcntl.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#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> DescriptorTable::Fork()
{
ScopedLock lock(&dtablelock);
Ref<DescriptorTable> ret(new DescriptorTable);
if ( !ret ) { return Ref<DescriptorTable>(NULL); }
ret->entries = new dtableent_t[numentries];
if ( !ret->entries ) { return Ref<DescriptorTable>(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<Descriptor> DescriptorTable::Get(int index)
{
ScopedLock lock(&dtablelock);
if ( !IsGoodEntry(index) ) { errno = EBADF; return Ref<Descriptor>(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<Descriptor> 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<Descriptor> DescriptorTable::FreeKeep(int index)
{
ScopedLock lock(&dtablelock);
if ( !IsGoodEntry(index) ) { errno = EBADF; return Ref<Descriptor>(NULL); }
Ref<Descriptor> 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

View File

@ -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 <http://www.gnu.org/licenses/>.
filesystem.cpp
Allows access to stored sequences of bytes in an orderly fashion.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/string.h>
#include <errno.h>
#include <string.h>
#include "syscall.h"
#include "process.h"
#include "filesystem.h"
#include "directory.h"
#include "mount.h"
#include <sortix/stat.h>
#include <sortix/fcntl.h>
#include <sortix/unistd.h>
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
fs/devfs.cpp
Provides access to various block, character, and other kinds of devices.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/string.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <sortix/kernel/sortedlist.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
fs/initfs.cpp
Provides access to the initial ramdisk.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/string.h>
#include <errno.h>
#include <string.h>
#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;
}
}

397
sortix/fs/kram.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
fs/kram.cpp
Kernel RAM filesystem.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/interlock.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/fsfunc.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/string.h>
#include <sortix/dirent.h>
#include <sortix/fcntl.h>
#include <sortix/stat.h>
#include <sortix/seek.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#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> 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> 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<Inode> Dir::open(ioctx_t* ctx, const char* filename, int flags, mode_t mode)
{
ScopedLock lock(&dirlock);
if ( shutdown ) { errno = ENOENT; return Ref<Inode>(NULL); }
size_t childindex = FindChild(filename);
if ( childindex != SIZE_MAX )
{
if ( flags & O_EXCL ) { errno = EEXIST; return Ref<Inode>(NULL); }
return children[childindex].inode;
}
if ( !(flags & O_CREAT) )
return errno = ENOENT, Ref<Inode>(NULL);
Ref<File> file(new File(dev, 0, ctx->uid, ctx->gid, mode));
if ( !file )
return Ref<Inode>(NULL);
if ( !AddChild(filename, file) )
return Ref<Inode>(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> 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<Dir>(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<Inode> 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<Inode> 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

103
sortix/fs/kram.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
fs/kram.h
Kernel RAM filesystem.
*******************************************************************************/
#ifndef SORTIX_FS_KRAM_H
#define SORTIX_FS_KRAM_H
#include <sortix/kernel/inode.h>
#include <sortix/kernel/kthread.h>
namespace Sortix {
namespace KRAMFS {
struct DirEntry
{
Ref<Inode> 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<Inode> 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<Inode> node);
virtual int link_raw(ioctx_t* ctx, const char* filename, Ref<Inode> 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> 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

View File

@ -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 <http://www.gnu.org/licenses/>.
fs/ramfs.cpp
A filesystem stored entirely in RAM.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/string.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#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<DevRAMFSFile*>(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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
fs/ramfs.h
A filesystem stored entirely in RAM.
*******************************************************************************/
#ifndef SORTIX_FS_RAMFS_H
#define SORTIX_FS_RAMFS_H
#include <sortix/kernel/sortedlist.h>
#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<DevRAMFSFile*>* 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

View File

@ -23,193 +23,93 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/interlock.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/inode.h>
#include <sortix/stat.h>
#include <sortix/seek.h>
#include <errno.h>
#include <string.h>
#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

View File

@ -25,79 +25,31 @@
#ifndef SORTIX_FS_UTIL_H
#define SORTIX_FS_UTIL_H
#include "../stream.h"
#include <sortix/kernel/inode.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
fs/videofs.cpp
Provides filesystem access to the video framework.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/video.h>
#include <sortix/kernel/string.h>
#include <errno.h>
#include <string.h>
#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

81
sortix/fsfunc.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
fsfunc.cpp
Filesystem related utility functions.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/fsfunc.h>
#include <sortix/kernel/string.h>
#include <sortix/dirent.h>
#include <sortix/stat.h>
#include <string.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/dirent.h
Format of directory entries.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_DIRENT_H
#define INCLUDE_SORTIX_DIRENT_H
#include <features.h>
__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

View File

@ -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

View File

@ -22,8 +22,8 @@
*******************************************************************************/
#ifndef SORTIX_INITRD_H
#define SORTIX_INITRD_H
#ifndef INCLUDE_SORTIX_INITRD_H
#define INCLUDE_SORTIX_INITRD_H
#include <features.h>

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <sortix/kernel/sortedlist.h>
#include "../filesystem.h"
#include <stddef.h>
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

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/kernel/descriptor.h
A file descriptor.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_KERNEL_DESCRIPTOR_H
#define INCLUDE_SORTIX_KERNEL_DESCRIPTOR_H
#include <sys/types.h>
#include <stdint.h>
#include <sortix/timespec.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
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> vnode, int dflags);
virtual ~Descriptor();
Ref<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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> vnode;
kthread_mutex_t curofflock;
bool seekable;
bool checked_seekable;
off_t curoff;
int dflags;
};
bool LinkInodeInDir(ioctx_t* ctx, Ref<Descriptor> dir, const char* name,
Ref<Inode> inode);
Ref<Descriptor> OpenDirContainingPath(ioctx_t* ctx, Ref<Descriptor> from,
const char* path, char** finalp);
} // namespace Sortix
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/kernel/dtable.h
Table of file descriptors.
*******************************************************************************/
#ifndef SORTIX_DTABLE_H
#define SORTIX_DTABLE_H
#include <sortix/kernel/refcount.h>
namespace Sortix {
class Descriptor;
typedef struct dtableent_struct
{
Ref<Descriptor> desc;
int flags;
} dtableent_t;
class DescriptorTable : public Refcountable
{
public:
DescriptorTable();
~DescriptorTable();
Ref<DescriptorTable> Fork();
Ref<Descriptor> Get(int index);
int Allocate(Ref<Descriptor> desc, int flags);
int Copy(int from, int to);
void Free(int index);
Ref<Descriptor> 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

View File

@ -17,29 +17,24 @@
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/kernel/inode.h
Interfaces and utility classes for implementing inodes.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_KERNEL_INODE_H
#define INCLUDE_SORTIX_KERNEL_INODE_H
#include <sys/types.h>
#include <stddef.h>
#include <stdint.h>
#include <sortix/timespec.h>
#include <sortix/kernel/refcount.h>
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<Inode> 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<Inode> node) = 0;
virtual int link_raw(ioctx_t* ctx, const char* filename, Ref<Inode> 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<Inode> 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<Inode> node);
virtual int link_raw(ioctx_t* ctx, const char* filename, Ref<Inode> 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

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <string.h>
#include "device.h"
#ifndef SORTIX_IOCTX_H
#define SORTIX_IOCTX_H
namespace Sortix
#include <sys/types.h>
#include <stdint.h>
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

View File

@ -17,45 +17,46 @@
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
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

View File

@ -27,8 +27,6 @@
#include <sortix/signal.h>
#define GOT_ACTUAL_KTHREAD
namespace Sortix {
extern "C" {

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
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> inode;
ino_t ino;
dev_t dev;
} mountpoint_t;
public:
Device();
virtual ~Device();
class MountTable : public Refcountable
{
public:
MountTable();
~MountTable();
Ref<MountTable> Fork();
bool AddMount(ino_t ino, dev_t dev, Ref<Inode> 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

View File

@ -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 T> class Ref
{
public:
constexpr Ref() : obj(NULL) { }
explicit Ref(T* obj) : obj(obj) { if ( obj ) obj->Refer_Renamed(); }
template <class U>
explicit Ref(U* obj) : obj(obj) { if ( obj ) obj->Refer_Renamed(); }
Ref(const Ref<T>& r) : obj(r.Get()) { if ( obj ) obj->Refer_Renamed(); }
template <class U>
Ref(const Ref<U>& 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 <class U>
Ref operator=(const Ref<U> 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 <class U> bool operator==(const Ref<U>& other)
{
return (*this).Get() == other.Get();
}
template <class U> bool operator==(const U* const& other)
{
return (*this).Get() == other;
}
bool operator!=(const Ref& other)
{
return !((*this) == other);
}
template <class U> bool operator!=(const Ref<U>& other)
{
return !((*this) == other);
}
template <class U> 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

View File

@ -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

View File

@ -25,6 +25,8 @@
#ifndef SORTIX_VIDEO_H
#define SORTIX_VIDEO_H
#include <sortix/kernel/refcount.h>
namespace Sortix {
class TextBuffer;
@ -51,7 +53,7 @@ public:
namespace Video {
void Init(TextBufferHandle* textbufhandle);
void Init(Ref<TextBufferHandle> textbufhandle);
bool RegisterDriver(const char* name, VideoDriver* driver);
char* GetCurrentMode();
char* GetDriverName(size_t index);

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/kernel/vnode.h
Nodes in the virtual filesystem.
*******************************************************************************/
#ifndef SORTIX_VNODE_H
#define SORTIX_VNODE_H
#include <sortix/kernel/refcount.h>
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> inode, Ref<Vnode> 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<Vnode> 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<Vnode> 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> inode;
Ref<Vnode> mountedat;
ino_t rootino;
dev_t rootdev;
};
} // namespace Sortix
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <features.h>
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

View File

@ -27,6 +27,7 @@
#define SORTIX_STAT_H
#include <features.h>
#include <sys/types.h>
__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)

View File

@ -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

View File

@ -23,12 +23,18 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/vnode.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/crc32.h>
#include <sortix/kernel/string.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/fsfunc.h>
#include <sortix/fcntl.h>
#include <sortix/initrd.h>
#include <sortix/stat.h>
#include <sortix/mman.h>
#include <sortix/initrd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#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<Descriptor> node);
static bool ExtractFile(ioctx_t* ctx, uint32_t ino, Ref<Descriptor> file);
static bool ExtractNode(ioctx_t* ctx, uint32_t ino, Ref<Descriptor> node);
static bool ExtractDir(ioctx_t* ctx, uint32_t ino, Ref<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> desc)
{
ioctx_t ctx; SetupKernelIOCtx(&ctx);
return ExtractNode(&ctx, sb->root, desc);
}
bool ExtractFromPhysicalInto(addr_t physaddr, size_t size, Ref<Descriptor> desc)
{
Init(physaddr, size);
return ExtractInto(desc);
}
} // namespace InitRD
} // namespace Sortix

View File

@ -22,21 +22,22 @@
*******************************************************************************/
#ifndef SORTIX_INITRD_KERNEL_H
#define SORTIX_INITRD_KERNEL_H
#ifndef SORTIX_INITRD_H
#define SORTIX_INITRD_H
#include <sortix/kernel/refcount.h>
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<Descriptor> desc);
bool ExtractFromPhysicalInto(addr_t physaddr, size_t size, Ref<Descriptor> desc);
} // namespace InitRD
} // namespace Sortix
#endif

270
sortix/inode.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
inode.cpp
Interfaces and utility classes for implementing inodes.
*******************************************************************************/
#include <errno.h>
#include <string.h>
#include <sortix/stat.h>
#include <sortix/kernel/platform.h>
#include <sortix/kernel/interlock.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/ioctx.h>
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<Inode> AbstractInode::open(ioctx_t* /*ctx*/, const char* /*filename*/,
int /*flags*/, mode_t /*mode*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, Ref<Inode>(NULL);
return errno = ENOTDIR, Ref<Inode>(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<Inode> /*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<Inode> /*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

View File

@ -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 <sortix/kernel/platform.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/dtable.h>
#include <sortix/kernel/string.h>
#include <sortix/kernel/kthread.h>
#include <sortix/seek.h>
#include <sortix/dirent.h>
#include <sortix/fcntl.h>
#include <sortix/stat.h>
#include <assert.h>
#include <errno.h>
#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<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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<DescriptorTable> dtable = CurrentProcess()->GetDTable();
Ref<Descriptor> desc = dtable->FreeKeep(fd);
dtable.Reset();
if ( !desc )
return -1;
return desc->sync(&ctx);
}
static int sys_dup(int fd)
{
Ref<DescriptorTable> dtable = CurrentProcess()->GetDTable();
Ref<Descriptor> 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<Descriptor> from = PrepareLookup(&relpath, dirfd);
if ( !from ) { delete[] pathcopy; return -1; }
Ref<Descriptor> desc = from->open(&ctx, relpath, flags, mode);
from.Reset();
delete[] pathcopy;
if ( !desc )
return -1;
Ref<DescriptorTable> 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<Descriptor> from = PrepareLookup(&relpath);
Ref<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> from = PrepareLookup(&relpath);
Ref<Descriptor> 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<Descriptor> 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<Descriptor> from = PrepareLookup(&relpath, dirfd);
if ( !from ) { delete[] pathcopy; return -1; }
Ref<Descriptor> 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<Descriptor> 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<DescriptorTable> 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<Descriptor> 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<Descriptor> from = PrepareLookup(&relpath);
Ref<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
keyboard.cpp
An interface to keyboards.
ioctx.cpp
The context for io operations: who made it, how should data be copied, etc.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include "interrupt.h"
#include "syscall.h"
#include "keyboard.h"
#include "kb/ps2.h"
#include "kb/layout/us.h"
#include "logterminal.h"
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/copy.h>
#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

View File

@ -23,7 +23,7 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include "../../keyboard.h"
#include <sortix/kernel/keyboard.h>
#include <sortix/keycodes.h>
#include "us.h"

View File

@ -25,7 +25,7 @@
#ifndef SORTIX_KB_LAYOUT_US_H
#define SORTIX_KB_LAYOUT_US_H
#include "../../keyboard.h"
#include <sortix/kernel/keyboard.h>
namespace Sortix
{

View File

@ -23,11 +23,11 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/keyboard.h>
#include <sortix/keycodes.h>
#include <assert.h>
#include <string.h>
#include "../interrupt.h"
#include "../keyboard.h"
#include <sortix/keycodes.h>
#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)

View File

@ -26,7 +26,7 @@
#define SORTIX_KB_PS2_H
#include <sortix/kernel/kthread.h>
#include "../keyboard.h"
#include <sortix/kernel/keyboard.h>
namespace Sortix
{

View File

@ -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 <sortix/kernel/pci.h>
#include <sortix/kernel/worker.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/vnode.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/dtable.h>
#include <sortix/kernel/mtable.h>
#include <sortix/kernel/keyboard.h>
#include <sortix/fcntl.h>
#include <sortix/stat.h>
#include <sortix/mman.h>
#include <sortix/wait.h>
#include <assert.h>
#include <errno.h>
#include <malloc.h>
#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<TextBufferHandle> 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<TextBufferHandle>(&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<DescriptorTable> dtable(new DescriptorTable());
if ( !dtable )
Panic("Unable to allocate descriptor table");
Ref<MountTable> 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<Inode> 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<Vnode> vroot(new Vnode(iroot, Ref<Vnode>(NULL), 0, 0));
if ( !vroot )
Panic("Unable to allocate root vnode.");
Ref<Descriptor> 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<Descriptor> 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<Inode> 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<Descriptor> root = CurrentProcess()->GetRoot();
Ref<Descriptor> 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,
&regs) )
{
Panic("Unable to execute init program");
}
if ( process->Execute(initpath, program, programsize, argc, argv, envc,
envp, &regs) )
PanicF("Unable to execute %s.", initpath);
delete[] program;
// Now become the init process and the operation system shall run.
CPU::LoadRegisters(&regs);

View File

@ -23,303 +23,294 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/interlock.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/keyboard.h>
#include <sortix/termmode.h>
#include <sortix/termios.h>
#include <sortix/keycodes.h>
#include <sortix/signal.h>
#include <sortix/stat.h>
#include <errno.h>
#include <string.h>
#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

View File

@ -26,60 +26,49 @@
#define SORTIX_LOGTERMINAL_H
#include <sortix/kernel/kthread.h>
#ifdef GOT_FAKE_KTHREAD
#include "event.h"
#endif
#include "stream.h"
#include "terminal.h"
#include "keyboard.h"
#include <sortix/kernel/inode.h>
#include <sortix/kernel/keyboard.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
mount.cpp
Handles system wide mount points and initialization of new file systems.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/panic.h>
#include <sortix/kernel/string.h>
#include <string.h>
#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"); }
}
}
}

85
sortix/mtable.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
mtable.cpp
Class to keep track of mount points.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/mtable.h>
#include <string.h>
namespace Sortix {
MountTable::MountTable()
{
mtablelock = KTHREAD_MUTEX_INITIALIZER;
mounts = NULL;
nummounts = 0;
mountsalloced = 0;
}
MountTable::~MountTable()
{
delete[] mounts;
}
Ref<MountTable> MountTable::Fork()
{
ScopedLock lock(&mtablelock);
Ref<MountTable> clone(new MountTable);
if ( !clone )
return Ref<MountTable>(NULL);
clone->mounts = new mountpoint_t[nummounts];
if ( !clone->mounts )
return Ref<MountTable>(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> 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

View File

@ -24,340 +24,270 @@
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/interlock.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/vnode.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/dtable.h>
#include <sortix/signal.h>
#include <sortix/stat.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#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<Inode> recv_inode(new PipeEndpoint(0, uid, gid, mode, channel, true));
if ( !recv_inode ) { delete channel; return -1; }
Ref<Inode> send_inode(new PipeEndpoint(0, uid, gid, mode, channel, false));
if ( !send_inode ) return -1;
Ref<Vnode> recv_vnode(new Vnode(recv_inode, Ref<Vnode>(NULL), 0, 0));
Ref<Vnode> send_vnode(new Vnode(send_inode, Ref<Vnode>(NULL), 0, 0));
if ( !recv_vnode || !send_vnode ) return -1;
Ref<Descriptor> recv_desc(new Descriptor(recv_vnode, 0));
Ref<Descriptor> send_desc(new Descriptor(send_vnode, 0));
if ( !recv_desc || !send_desc ) return -1;
Ref<DescriptorTable> 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

View File

@ -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

View File

@ -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 <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/dtable.h>
#include <sortix/kernel/mtable.h>
#include <sortix/kernel/worker.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/string.h>
#include <sortix/signal.h>
#include <sortix/unistd.h>
#include <sortix/fcntl.h>
#include <sortix/stat.h>
#include <sortix/fork.h>
#include <sortix/mman.h>
#include <sortix/wait.h>
@ -38,10 +46,6 @@
#include <string.h>
#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<DescriptorTable> dtable, Ref<MountTable> mtable)
{
ScopedLock lock(&ptrlock);
assert(!this->dtable);
assert(!this->mtable);
this->dtable = dtable;
this->mtable = mtable;
}
void Process::BootstrapDirectories(Ref<Descriptor> 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<MountTable> Process::GetMTable()
{
ScopedLock lock(&ptrlock);
assert(mtable);
return mtable;
}
Ref<DescriptorTable> Process::GetDTable()
{
ScopedLock lock(&ptrlock);
assert(dtable);
return dtable;
}
Ref<Descriptor> Process::GetRoot()
{
ScopedLock lock(&ptrlock);
assert(root);
return root;
}
Ref<Descriptor> Process::GetCWD()
{
ScopedLock lock(&ptrlock);
assert(cwd);
return cwd;
}
void Process::SetCWD(Ref<Descriptor> newcwd)
{
ScopedLock lock(&ptrlock);
assert(newcwd);
cwd = newcwd;
}
Ref<Descriptor> 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<Descriptor> 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<Descriptor> 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<Descriptor> 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;

View File

@ -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 <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/fork.h>
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<Descriptor> root;
Ref<Descriptor> cwd;
Ref<MountTable> mtable;
Ref<DescriptorTable> dtable;
public:
void BootstrapTables(Ref<DescriptorTable> dtable, Ref<MountTable> mtable);
void BootstrapDirectories(Ref<Descriptor> root);
Ref<MountTable> GetMTable();
Ref<DescriptorTable> GetDTable();
Ref<Descriptor> GetRoot();
Ref<Descriptor> GetCWD();
Ref<Descriptor> GetDescriptor(int fd);
// TODO: This should be removed, don't call it.
Ref<Descriptor> Open(ioctx_t* ctx, const char* path, int flags, mode_t mode = 0);
void SetCWD(Ref<Descriptor> 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:

View File

@ -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

View File

@ -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.

View File

@ -23,9 +23,9 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/log.h>
#include <sortix/kernel/keyboard.h>
#include <string.h>
#include "vga.h"
#include "keyboard.h"
#include "uart.h"
#include "serialterminal.h"
#include "scheduler.h"

View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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 <http://www.gnu.org/licenses/>.
terminal.cpp
Provides an interface to terminals for user-space.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/termios.h>
#include <errno.h>
#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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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 <sortix/termmode.h>
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

View File

@ -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<TextBufferHandle> textbufhandle)
{
this->textbufhandle = textbufhandle; textbufhandle->Refer();
this->textbufhandle = textbufhandle;
this->termlock = KTHREAD_MUTEX_INITIALIZER;
Reset();
}

View File

@ -26,6 +26,7 @@
#define SORTIX_TEXTTERMINAL_H
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
namespace Sortix {
@ -34,7 +35,7 @@ class TextBufferHandle;
class TextTerminal //: public Printable ?
{
public:
TextTerminal(TextBufferHandle* textbufhandle);
TextTerminal(Ref<TextBufferHandle> 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<TextBufferHandle> textbufhandle;
mutable kthread_mutex_t termlock;
uint8_t vgacolor;
unsigned column;

View File

@ -23,15 +23,19 @@
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/interlock.h>
#include <errno.h>
#include <string.h>
#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<Descriptor> 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<Inode> 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<Inode> 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

View File

@ -25,11 +25,12 @@
#ifndef SORTIX_VGA_H
#define SORTIX_VGA_H
#include "device.h"
#include "stream.h"
#include <sortix/kernel/refcount.h>
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<Descriptor> 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

View File

@ -105,9 +105,9 @@ size_t currentdrvid;
bool newdrivers;
kthread_mutex_t videolock;
TextBufferHandle* textbufhandle;
Ref<TextBufferHandle> textbufhandle;
void Init(TextBufferHandle* thetextbufhandle)
void Init(Ref<TextBufferHandle> thetextbufhandle)
{
videolock = KTHREAD_MUTEX_INITIALIZER;
textbufhandle = thetextbufhandle;

216
sortix/vnode.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
vnode.cpp
Nodes in the virtual filesystem.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/vnode.h>
#include <sortix/kernel/mtable.h>
#include <sortix/mount.h>
#include <assert.h>
#include <errno.h>
#include "process.h"
namespace Sortix {
static Ref<Inode> LookupMount(Ref<Inode> inode)
{
Ref<MountTable> 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<Inode>(NULL);
}
Vnode::Vnode(Ref<Inode> inode, Ref<Vnode> mountedat, ino_t rootino, dev_t rootdev)
{
for ( Ref<Vnode> 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> 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<Inode> retinode = inode->open(ctx, filename, flags, mode);
if ( !retinode ) { return Ref<Vnode>(NULL); }
Ref<Vnode> retmountedat = mountedat;
ino_t retrootino = rootino;
dev_t retrootdev = rootdev;
// Check whether we moved into a filesystem mount point.
Ref<Inode> mounted = LookupMount(retinode);
if ( mounted )
{
retinode = mounted;
retmountedat = Ref<Vnode>(this);
retrootino = mounted->ino;
retrootdev = mounted->dev;
}
return Ref<Vnode>(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<Vnode> 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

View File

@ -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: <stdout>", inputname);
return 1;
}
} while ( true );

View File

@ -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 <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
@ -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;

View File

@ -20,7 +20,10 @@
*******************************************************************************/
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -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();
}