diff --git a/libc/Makefile b/libc/Makefile index 68a88226..b8d67ff5 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -151,6 +151,14 @@ fdio.o \ fileno.o \ fork.o \ fpipe.o \ +fsm_bootstraprootfd.o \ +fsm_closechannel.o \ +fsm_closeserver.o \ +fsm_fsbind.o \ +fsm_listen.o \ +fsm_mkserver.o \ +fsm_recv.o \ +fsm_send.o \ fstatat.o \ fstat.o \ ftruncate.o \ diff --git a/libc/fsm_bootstraprootfd.cpp b/libc/fsm_bootstraprootfd.cpp new file mode 100644 index 00000000..55c7c9b1 --- /dev/null +++ b/libc/fsm_bootstraprootfd.cpp @@ -0,0 +1,38 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 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 . + + fsm_bootstraprootfd.cpp + Creates a root file descriptor associated with a user-space filesystem. + +*******************************************************************************/ + +#include +#include +#include +#include + +#include + +extern "C" int fsm_bootstraprootfd(int server, ino_t ino, int open_flags, + mode_t mode) +{ + char name[sizeof(uintmax_t)*3]; + snprintf(name, sizeof(name), "%ju", (uintmax_t) ino); + return openat(server, name, open_flags, mode); +} diff --git a/libc/fsm_closechannel.cpp b/libc/fsm_closechannel.cpp new file mode 100644 index 00000000..35ccf79d --- /dev/null +++ b/libc/fsm_closechannel.cpp @@ -0,0 +1,32 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 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 . + + fsm_closechannel.cpp + Closes a user-space filesystem communication channel. + +*******************************************************************************/ + +#include + +#include + +extern "C" int fsm_closechannel(int /*server*/, int channel) +{ + return close(channel); +} diff --git a/libc/fsm_closeserver.cpp b/libc/fsm_closeserver.cpp new file mode 100644 index 00000000..65a57823 --- /dev/null +++ b/libc/fsm_closeserver.cpp @@ -0,0 +1,32 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 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 . + + fsm_closeserver.cpp + Destroys a user-space filesystem server. + +*******************************************************************************/ + +#include + +#include + +extern "C" int fsm_closeserver(int server) +{ + return close(server); +} diff --git a/libc/fsm_fsbind.cpp b/libc/fsm_fsbind.cpp new file mode 100644 index 00000000..08b262d4 --- /dev/null +++ b/libc/fsm_fsbind.cpp @@ -0,0 +1,36 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 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 . + + fsm_fsbind.cpp + Binds a user-space filesystem inode at a mount point. + +*******************************************************************************/ + +#include + +#include + +#include + +DEFN_SYSCALL3(int, sys_fsm_fsbind, SYSCALL_FSM_FSBIND, int, int, int); + +extern "C" int fsm_fsbind(int rootfd, int mountpoint, int flags) +{ + return sys_fsm_fsbind(rootfd, mountpoint, flags); +} diff --git a/libc/fsm_listen.cpp b/libc/fsm_listen.cpp new file mode 100644 index 00000000..f478ec44 --- /dev/null +++ b/libc/fsm_listen.cpp @@ -0,0 +1,32 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 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 . + + fsm_listen.cpp + Listens for a new channel on a given filesystem server. + +*******************************************************************************/ + +#include + +#include + +extern "C" int fsm_listen(int server) +{ + return openat(server, "listen", O_RDWR); +} diff --git a/libc/fsm_mkserver.cpp b/libc/fsm_mkserver.cpp new file mode 100644 index 00000000..119019f4 --- /dev/null +++ b/libc/fsm_mkserver.cpp @@ -0,0 +1,32 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 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 . + + fsm_mkserver.cpp + Creates a user-space filesystem server. + +*******************************************************************************/ + +#include + +#include + +extern "C" int fsm_mkserver() +{ + return open("/dev/fs/new", O_RDWR | O_CREAT, 0666); +} diff --git a/libc/fsm_recv.cpp b/libc/fsm_recv.cpp new file mode 100644 index 00000000..bed75939 --- /dev/null +++ b/libc/fsm_recv.cpp @@ -0,0 +1,32 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 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 . + + fsm_recv.cpp + Reads a message from a filesystem communication channel. + +*******************************************************************************/ + +#include + +#include + +extern "C" ssize_t fsm_recv(int /*server*/, int channel, void* ptr, size_t count) +{ + return read(channel, ptr, count); +} diff --git a/libc/fsm_send.cpp b/libc/fsm_send.cpp new file mode 100644 index 00000000..830600ed --- /dev/null +++ b/libc/fsm_send.cpp @@ -0,0 +1,33 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 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 . + + fsm_send.cpp + Send a message to a filesystem communication channel. + +*******************************************************************************/ + +#include + +#include + +extern "C" ssize_t fsm_send(int /*server*/, int channel, const void* ptr, + size_t count) +{ + return write(channel, ptr, count); +} diff --git a/libc/include/fsmarshall-msg.h b/libc/include/fsmarshall-msg.h new file mode 100644 index 00000000..711ca0d1 --- /dev/null +++ b/libc/include/fsmarshall-msg.h @@ -0,0 +1,310 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + 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 . + + fsmarshall-msg.h + User-space filesystem API. + +*******************************************************************************/ + +#ifndef INCLUDE_FSMARSHALL_MSG_H +#define INCLUDE_FSMARSHALL_MSG_H + +#include + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct fsm_msg_header +{ + size_t msgtype; + size_t msgsize; +}; + +#define FSM_RESP_ERROR 0 +struct fsm_resp_error +{ + int errnum; +}; + +#define FSM_RESP_SUCCESS 1 +struct fsm_resp_success +{ +}; + +#define FSM_REQ_ABORT 2 +struct fsm_req_abort +{ +}; + +#define FSM_RESP_ABORT 3 +struct fsm_resp_abort +{ +}; + +#define FSM_REQ_SYNC 4 +struct fsm_req_sync +{ + ino_t ino; +}; + +#define FSM_REQ_STAT 5 +struct fsm_req_stat +{ + ino_t ino; +}; + +#define FSM_RESP_STAT 6 +struct fsm_resp_stat +{ + struct stat st; +}; + +#define FSM_REQ_CHMOD 7 +struct fsm_req_chmod +{ + ino_t ino; + mode_t mode; +}; + +#define FSM_REQ_CHOWN 8 +struct fsm_req_chown +{ + ino_t ino; + uid_t uid; + gid_t gid; +}; + +#define FSM_REQ_TRUNCATE 9 +struct fsm_req_truncate +{ + ino_t ino; + off_t size; +}; + +#define FSM_REQ_LSEEK 10 +struct fsm_req_lseek +{ + ino_t ino; + off_t offset; + int whence; +}; + +#define FSM_RESP_LSEEK 11 +struct fsm_resp_lseek +{ + off_t offset; +}; + +#define FSM_REQ_READ 12 +struct fsm_req_read +{ + ino_t ino; + size_t count; +}; + +#define FSM_REQ_PREAD 13 +struct fsm_req_pread +{ + ino_t ino; + off_t offset; + size_t count; +}; + +#define FSM_RESP_READ 14 +struct fsm_resp_read +{ + size_t count; + //uint8_t data[count]; +}; + +#define FSM_REQ_WRITE 15 +struct fsm_req_write +{ + ino_t ino; + size_t count; + //uint8_t data[count]; +}; + +#define FSM_REQ_PWRITE 16 +struct fsm_req_pwrite +{ + ino_t ino; + off_t offset; + size_t count; + //uint8_t data[count]; +}; + +#define FSM_RESP_WRITE 17 +struct fsm_resp_write +{ + size_t count; +}; + +#define FSM_REQ_UTIMES 18 +struct fsm_req_utimes +{ + ino_t ino; + struct timeval times[2]; +}; + +#define FSM_REQ_ISATTY 19 +struct fsm_req_isatty +{ + ino_t ino; +}; + +#define FSM_REQ_READDIRENTS 20 +struct fsm_req_readdirents +{ + ino_t ino; + off_t rec_num; +}; + +#define FSM_RESP_READDIRENTS 21 +struct fsm_resp_readdirents +{ + ino_t ino; + unsigned char type; + size_t namelen; + //char name[namelen]; +}; + +#define FSM_REQ_OPEN 22 +struct fsm_req_open +{ + ino_t dirino; + int flags; + mode_t mode; + size_t namelen; + //char name[namelen]; +}; + +#define FSM_RESP_OPEN 23 +struct fsm_resp_open +{ + ino_t ino; + mode_t type; +}; + +#define FSM_REQ_MKDIR 24 +struct fsm_req_mkdir +{ + ino_t dirino; + mode_t mode; + size_t namelen; + //char name[namelen]; +}; + +#define FSM_RESP_MKDIR 25 +struct fsm_resp_mkdir +{ + ino_t ino; +}; + +#define FSM_REQ_LINK 26 +struct fsm_req_link +{ + ino_t dirino; + ino_t linkino; + size_t namelen; + //char name[namelen]; +}; + +#define FSM_REQ_SYMLINK 27 +struct fsm_req_symlink +{ + ino_t dirino; + size_t targetlen; + size_t namelen; + //char target[targetlen]; + //char name[namelen]; +}; + +#define FSM_REQ_READLINK 28 +struct fsm_req_readlink +{ + ino_t ino; +}; + +#define FSM_RESP_READLINK 29 +struct fsm_resp_readlink +{ + size_t targetlen; + //char target[targetlen]; +}; + +#define FSM_REQ_TCGETWINSIZE 30 +struct fsm_req_tcgetwinsize +{ + ino_t ino; +}; + +#define FSM_RESP_TCGETWINSIZE 31 +struct fsm_resp_tcgetwinsize +{ + struct winsize size; +}; + +#define FSM_REQ_SETTERMMODE 32 +struct fsm_req_settermmode +{ + ino_t ino; + unsigned int termmode; +}; + +#define FSM_REQ_GETTERMMODE 33 +struct fsm_req_gettermmode +{ + ino_t ino; +}; + +#define FSM_RESP_GETTERMMODE 34 +struct fsm_resp_gettermmode +{ + unsigned int termmode; +}; + +#define FSM_REQ_UNLINK 35 +struct fsm_req_unlink +{ + ino_t dirino; + size_t namelen; + //char name[namelen]; +}; + +#define FSM_REQ_RMDIR 36 +struct fsm_req_rmdir +{ + ino_t dirino; + size_t namelen; + //char name[namelen]; +}; + +#define FSM_MSG_NUM 37 + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif diff --git a/libc/include/fsmarshall.h b/libc/include/fsmarshall.h new file mode 100644 index 00000000..3bc2cfcc --- /dev/null +++ b/libc/include/fsmarshall.h @@ -0,0 +1,53 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + 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 . + + fsmarshall.h + User-space filesystem API. + +*******************************************************************************/ + +#ifndef INCLUDE_FSMARSHALL_H +#define INCLUDE_FSMARSHALL_H + +#include + +#include +#include +#include + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +int fsm_mkserver(); +int fsm_closeserver(int server); +int fsm_bootstraprootfd(int server, ino_t ino, int open_flags, mode_t mode); +int fsm_fsbind(int rootfd, int mountpoint, int flags); +int fsm_listen(int server); +int fsm_closechannel(int server, int channel); +ssize_t fsm_recv(int server, int channel, void* ptr, size_t count); +ssize_t fsm_send(int server, int channel, const void* ptr, size_t count); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif diff --git a/sortix/Makefile b/sortix/Makefile index 8132f66f..466198a6 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -84,6 +84,7 @@ dtable.o \ elf.o \ fsfunc.o \ fs/kram.o \ +fs/user.o \ fs/util.o \ initrd.o \ inode.o \ diff --git a/sortix/fs/user.cpp b/sortix/fs/user.cpp new file mode 100644 index 00000000..b7eabccf --- /dev/null +++ b/sortix/fs/user.cpp @@ -0,0 +1,1193 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + fs/user.cpp + User-space filesystem. + +*******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../syscall.h" +#include "../process.h" + +namespace Sortix { + +namespace UserFS { + +class ChannelDirection; +class Channel; +class ChannelNode; +class Server; +class ServerNode; +class Unode; + +class ChannelDirection +{ +public: + ChannelDirection(); + ~ChannelDirection(); + size_t Send(ioctx_t* ctx, const void* ptr, size_t least, size_t max); + size_t Recv(ioctx_t* ctx, void* ptr, size_t least, size_t max); + void SendClose(); + void RecvClose(); + +private: + static const size_t BUFFER_SIZE = 8192; + uint8_t buffer[BUFFER_SIZE]; + size_t buffer_used; + size_t buffer_offset; + kthread_mutex_t transfer_lock; + kthread_cond_t not_empty; + kthread_cond_t not_full; + bool still_reading; + bool still_writing; + +}; + +class Channel +{ +public: + Channel(); + ~Channel(); + +public: + bool KernelSend(ioctx_t* ctx, const void* ptr, size_t count) + { + return KernelSend(ctx, ptr, count, count) == count; + } + size_t KernelSend(ioctx_t* ctx, const void* ptr, size_t least, size_t max); + bool KernelRecv(ioctx_t* ctx, void* ptr, size_t count) + { + return KernelRecv(ctx, ptr, count, count) == count; + } + size_t KernelRecv(ioctx_t* ctx, void* ptr, size_t least, size_t max); + void KernelClose(); + +public: + bool UserSend(ioctx_t* ctx, const void* ptr, size_t count) + { + return UserSend(ctx, ptr, count, count) == count; + } + size_t UserSend(ioctx_t* ctx, const void* ptr, size_t least, size_t max); + bool UserRecv(ioctx_t* ctx, void* ptr, size_t count) + { + return UserRecv(ctx, ptr, count, count) == count; + } + size_t UserRecv(ioctx_t* ctx, void* ptr, size_t least, size_t max); + void UserClose(); + +private: + kthread_mutex_t kernel_lock; + kthread_mutex_t user_lock; + kthread_mutex_t destruction_lock; + ChannelDirection from_kernel; + ChannelDirection from_user; + bool kernel_closed; + bool user_closed; + +}; + +class ChannelNode : public AbstractInode +{ +public: + ChannelNode(); + ChannelNode(Channel* channel); + ~ChannelNode(); + void Construct(Channel* channel); + 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: + Channel* channel; + +}; + +class Server : public Refcountable +{ +public: + Server(); + virtual ~Server(); + Channel* Connect(); + Channel* Listen(); + Ref BootstrapNode(ino_t ino, mode_t type); + Ref OpenNode(ino_t ino, mode_t type); + +private: + kthread_mutex_t connect_lock; + kthread_cond_t connecting_cond; + kthread_cond_t connectable_cond; + Channel* connecting; + +}; + +class ServerNode : public AbstractInode +{ +public: + ServerNode(Ref server); + virtual ~ServerNode(); + virtual Ref open(ioctx_t* ctx, const char* filename, int flags, + mode_t mode); + +private: + Ref server; + +}; + +class Unode : public Inode +{ +public: + Unode(Ref server, ino_t ino, mode_t type); + virtual ~Unode(); + virtual void linked(); + virtual void unlinked(); + virtual int sync(ioctx_t* ctx); + virtual int stat(ioctx_t* ctx, struct stat* st); + virtual int chmod(ioctx_t* ctx, mode_t mode); + virtual int chown(ioctx_t* ctx, uid_t owner, gid_t group); + virtual int truncate(ioctx_t* ctx, off_t length); + virtual off_t lseek(ioctx_t* ctx, off_t offset, int whence); + virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); + virtual ssize_t pread(ioctx_t* ctx, uint8_t* buf, size_t count, + off_t off); + virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); + virtual ssize_t pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, + off_t off); + virtual int utimes(ioctx_t* ctx, const struct timeval times[2]); + virtual int isatty(ioctx_t* ctx); + virtual ssize_t readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, + size_t size, off_t start, size_t maxcount); + virtual Ref open(ioctx_t* ctx, const char* filename, int flags, + mode_t mode); + virtual int mkdir(ioctx_t* ctx, const char* filename, mode_t mode); + virtual int link(ioctx_t* ctx, const char* filename, Ref node); + virtual int link_raw(ioctx_t* ctx, const char* filename, Ref node); + virtual int unlink(ioctx_t* ctx, const char* filename); + virtual int unlink_raw(ioctx_t* ctx, const char* filename); + virtual int rmdir(ioctx_t* ctx, const char* filename); + virtual int rmdir_me(ioctx_t* ctx); + virtual int symlink(ioctx_t* ctx, const char* oldname, + const char* filename); + virtual ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz); + virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws); + virtual int settermmode(ioctx_t* ctx, unsigned mode); + virtual int gettermmode(ioctx_t* ctx, unsigned* mode); + +private: + bool SendMessage(Channel* channel, size_t type, void* ptr, size_t size, + size_t extra = 0); + bool RecvMessage(Channel* channel, size_t type, void* ptr, size_t size); + void RecvError(Channel* channel); + bool RecvBoolean(Channel* channel); + void UnexpectedResponse(Channel* channel, struct fsm_msg_header* hdr); + +private: + ioctx_t kctx; + Ref server; + +}; + +// +// Implementation of Channel Directory. +// + +ChannelDirection::ChannelDirection() +{ + buffer_used = 0; + buffer_offset = 0; + transfer_lock = KTHREAD_MUTEX_INITIALIZER; + not_empty = KTHREAD_COND_INITIALIZER; + not_full = KTHREAD_COND_INITIALIZER; + still_reading = true; + still_writing = true; +} + +ChannelDirection::~ChannelDirection() +{ +} + +size_t ChannelDirection::Send(ioctx_t* ctx, const void* ptr, size_t least, size_t max) +{ + const uint8_t* src = (const uint8_t*) ptr; + size_t sofar = 0; + ScopedLock inner_lock(&transfer_lock); + while ( true ) + { + while ( true ) + { + if ( !still_reading ) + return errno = EPIPE, sofar; + if ( buffer_used < BUFFER_SIZE ) + break; + if ( least <= sofar ) + return sofar; + if ( !kthread_cond_wait_signal(¬_full, &transfer_lock) ) + return errno = EINTR, sofar; + } + + size_t use_offset = (buffer_offset + buffer_used) % BUFFER_SIZE; + size_t count = max - sofar; + size_t available_to_end = BUFFER_SIZE - use_offset; + size_t available = BUFFER_SIZE - buffer_used; + if ( available_to_end < available ) + available = available_to_end; + if ( available < count ) + count = available; + if ( !ctx->copy_to_dest(buffer + use_offset, src + sofar, count) ) + return sofar; + if ( !buffer_used ) + kthread_cond_signal(¬_empty); + buffer_used += count; + sofar += count; + if ( sofar == max ) + return sofar; + } +} + +size_t ChannelDirection::Recv(ioctx_t* ctx, void* ptr, size_t least, size_t max) +{ + uint8_t* dst = (uint8_t*) ptr; + size_t sofar = 0; + ScopedLock inner_lock(&transfer_lock); + while ( true ) + { + while ( true ) + { + if ( buffer_used ) + break; + if ( least <= sofar ) + return sofar; + if ( !still_writing ) + return errno = EPIPE, sofar; + if ( !kthread_cond_wait_signal(¬_empty, &transfer_lock) ) + return errno = EINTR, sofar; + } + + size_t use_offset = buffer_offset; + size_t count = max - sofar; + size_t available_to_end = BUFFER_SIZE - use_offset; + size_t available = buffer_used; + if ( available_to_end < available ) + available = available_to_end; + if ( available < count ) + count = available; + if ( !ctx->copy_from_src(dst + sofar, buffer + use_offset, count) ) + return sofar; + if ( buffer_used == BUFFER_SIZE ) + kthread_cond_signal(¬_full); + buffer_offset = (buffer_offset + count) % BUFFER_SIZE; + buffer_used -= count; + sofar += count; + if ( sofar == max ) + return sofar; + } +} + +void ChannelDirection::SendClose() +{ + ScopedLock lock(&transfer_lock); + still_writing = false; + kthread_cond_signal(¬_empty); +} + +void ChannelDirection::RecvClose() +{ + ScopedLock lock(&transfer_lock); + still_writing = false; + kthread_cond_signal(¬_full); +} + +// +// Implementation of Channel. +// + +Channel::Channel() +{ + kernel_lock = KTHREAD_MUTEX_INITIALIZER; + user_lock = KTHREAD_MUTEX_INITIALIZER; + destruction_lock = KTHREAD_MUTEX_INITIALIZER; + user_closed = false; + kernel_closed = false; +} + +Channel::~Channel() +{ +} + +size_t Channel::KernelSend(ioctx_t* ctx, const void* ptr, size_t least, + size_t max) +{ + ScopedLockSignal outer_lock(&kernel_lock); + if ( !outer_lock.IsAcquired() ) + return errno = EINTR, 0; + size_t ret = from_kernel.Send(ctx, ptr, least, max); + return ret; +} + +size_t Channel::KernelRecv(ioctx_t* ctx, void* ptr, size_t least, size_t max) +{ + ScopedLockSignal outer_lock(&kernel_lock); + if ( !outer_lock.IsAcquired() ) + return errno = EINTR, 0; + return from_user.Recv(ctx, ptr, least, max); +} + +void Channel::KernelClose() +{ + // No lock needed, this thread is the last to use this object as kernel. + from_kernel.SendClose(); + from_user.RecvClose(); + kthread_mutex_lock(&destruction_lock); + kernel_closed = true; + bool delete_this = user_closed; + kthread_mutex_unlock(&destruction_lock); + if ( delete_this ) + delete this; +} + +size_t Channel::UserSend(ioctx_t* ctx, const void* ptr, size_t least, + size_t max) +{ + ScopedLockSignal outer_lock(&user_lock); + if ( !outer_lock.IsAcquired() ) + return errno = EINTR, 0; + return from_user.Send(ctx, ptr, least, max); +} + +size_t Channel::UserRecv(ioctx_t* ctx, void* ptr, size_t least, size_t max) +{ + ScopedLockSignal outer_lock(&user_lock); + if ( !outer_lock.IsAcquired() ) + return errno = EINTR, 0; + return from_kernel.Recv(ctx, ptr, least, max); +} + +void Channel::UserClose() +{ + // No lock needed, this thread is the last to use this object as user. + from_kernel.RecvClose(); + from_user.SendClose(); + kthread_mutex_lock(&destruction_lock); + user_closed = true; + bool delete_this = kernel_closed; + kthread_mutex_unlock(&destruction_lock); + if ( delete_this ) + delete this; +} + +// +// Implementation of ChannelNode. +// + +ChannelNode::ChannelNode() +{ + channel = NULL; +} + +ChannelNode::ChannelNode(Channel* channel) +{ + Construct(channel); +} + +ChannelNode::~ChannelNode() +{ + if ( channel ) + channel->UserClose(); +} + +void ChannelNode::Construct(Channel* channel) +{ + inode_type = INODE_TYPE_STREAM; + this->channel = channel; + this->type = S_IFCHR; + this->dev = (dev_t) this; + this->ino = 0; + // TODO: Set uid, gid, mode. +} + +ssize_t ChannelNode::read(ioctx_t* ctx, uint8_t* buf, size_t count) +{ + return channel->UserRecv(ctx, buf, /*1*/ count, count); +} + +ssize_t ChannelNode::write(ioctx_t* ctx, const uint8_t* buf, size_t count) +{ + return channel->UserSend(ctx, buf, /*1*/ count, count); +} + +// +// Implementation of Server. +// + +Server::Server() +{ + connect_lock = KTHREAD_MUTEX_INITIALIZER; + connecting_cond = KTHREAD_COND_INITIALIZER; + connectable_cond = KTHREAD_COND_INITIALIZER; + connecting = NULL; +} + +Server::~Server() +{ +} + +Channel* Server::Connect() +{ + Channel* channel = new Channel(); + if ( !channel ) + return NULL; + ScopedLock lock(&connect_lock); + while ( connecting ) + if ( !kthread_cond_wait_signal(&connectable_cond, &connect_lock) ) + { + delete channel; + return errno = EINTR, (Channel*) NULL; + } + connecting = channel; + kthread_cond_signal(&connecting_cond); + return channel; +} + +Channel* Server::Listen() +{ + ScopedLock lock(&connect_lock); + while ( !connecting ) + if ( !kthread_cond_wait_signal(&connecting_cond, &connect_lock) ) + return errno = EINTR, (Channel*) NULL; + Channel* result = connecting; + connecting = NULL; + kthread_cond_signal(&connectable_cond); + return result; +} + +Ref Server::BootstrapNode(ino_t ino, mode_t type) +{ + return Ref(new Unode(Ref(this), ino, type)); +} + +Ref Server::OpenNode(ino_t ino, mode_t type) +{ + return BootstrapNode(ino, type); +} + +// +// Implementation of ServerNode. +// + +ServerNode::ServerNode(Ref server) +{ + inode_type = INODE_TYPE_UNKNOWN; + this->server = server; + this->type = S_IFDIR; + this->dev = (dev_t) this; + this->ino = 0; + // TODO: Set uid, gid, mode. +} + +ServerNode::~ServerNode() +{ +} + +Ref ServerNode::open(ioctx_t* /*ctx*/, const char* filename, int flags, + mode_t mode) +{ + unsigned long long ull_ino; + char* end; + int saved_errno = errno; errno = 0; + if ( !isspace(*filename) && + 0 < (ull_ino = strtoull(filename, &end, 10)) && + *end == '\0' && + errno != ERANGE ) + { + errno = saved_errno; + if ( !(flags & O_CREAT) ) + return errno = ENOENT, Ref(NULL); + ino_t ino = (ino_t) ull_ino; + return server->BootstrapNode(ino, mode & S_IFMT); + } + errno = saved_errno; + if ( !strcmp(filename, "listen") ) + { + Ref node(new ChannelNode); + if ( !node ) + return Ref(NULL); + Channel* channel = server->Listen(); + if ( !channel ) + return Ref(NULL); + node->Construct(channel); + return node; + } + return errno = ENOENT, Ref(NULL); +} + +// +// Implementation of Unode. +// + +Unode::Unode(Ref server, ino_t ino, mode_t type) +{ + SetupKernelIOCtx(&kctx); + this->server = server; + this->ino = ino; + this->dev = (dev_t) server; + this->type = type; +} + +Unode::~Unode() +{ +} + +bool Unode::SendMessage(Channel* channel, size_t type, void* ptr, size_t size, + size_t extra) +{ + struct fsm_msg_header hdr; + hdr.msgtype = type; + hdr.msgsize = size + extra; + if ( !channel->KernelSend(&kctx, &hdr, sizeof(hdr)) ) + return false; + if ( !channel->KernelSend(&kctx, ptr, size) ) + return false; + return true; +} + +bool Unode::RecvMessage(Channel* channel, size_t type, void* ptr, size_t size) +{ + struct fsm_msg_header resp_hdr; + if ( !channel->KernelRecv(&kctx, &resp_hdr, sizeof(resp_hdr)) ) + return false; + if ( resp_hdr.msgtype != type ) + { + UnexpectedResponse(channel, &resp_hdr); + return false; + } + return !ptr || !size || channel->KernelRecv(&kctx, ptr, size); +} + +void Unode::RecvError(Channel* channel) +{ + SetupKernelIOCtx(&kctx); + struct fsm_resp_error resp; + if ( channel->KernelRecv(&kctx, &resp, sizeof(resp)) ) + errno = resp.errnum; + // In case of error, errno is set to that error. +} + +bool Unode::RecvBoolean(Channel* channel) +{ + struct fsm_msg_header resp_hdr; + if ( !channel->KernelRecv(&kctx, &resp_hdr, sizeof(resp_hdr)) ) + return false; + if ( resp_hdr.msgtype == FSM_RESP_SUCCESS ) + return true; + UnexpectedResponse(channel, &resp_hdr); + return false; +} + +void Unode::UnexpectedResponse(Channel* channel, struct fsm_msg_header* hdr) +{ + if ( hdr->msgtype == FSM_RESP_ERROR ) + RecvError(channel); + else + errno = EIO; +} + +void Unode::linked() +{ +} + +void Unode::unlinked() +{ +} + +int Unode::sync(ioctx_t* /*ctx*/) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_sync msg; + msg.ino = ino; + if ( SendMessage(channel, FSM_REQ_SYNC, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::stat(ioctx_t* ctx, struct stat* st) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_stat msg; + struct fsm_resp_stat resp; + msg.ino = ino; + if ( SendMessage(channel, FSM_REQ_STAT, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_STAT, &resp, sizeof(resp)) && + (resp.st.st_dev = (dev_t) server, true) && + ctx->copy_to_dest(st, &resp.st, sizeof(*st)) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::chmod(ioctx_t* /*ctx*/, mode_t mode) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_chmod msg; + msg.ino = ino; + msg.mode = mode; + if ( SendMessage(channel, FSM_REQ_CHMOD, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::chown(ioctx_t* /*ctx*/, uid_t owner, gid_t group) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_chown msg; + msg.ino = ino; + msg.uid = owner; + msg.gid = group; + if ( SendMessage(channel, FSM_REQ_CHOWN, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::truncate(ioctx_t* /*ctx*/, off_t length) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_truncate msg; + msg.ino = ino; + msg.size = length; + if ( SendMessage(channel, FSM_REQ_TRUNCATE, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +off_t Unode::lseek(ioctx_t* /*ctx*/, off_t offset, int whence) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + off_t ret = -1; + struct fsm_req_lseek msg; + struct fsm_resp_lseek resp; + msg.ino = ino; + msg.offset = offset; + msg.whence = whence; + if ( SendMessage(channel, FSM_REQ_LSEEK, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_LSEEK, &resp, sizeof(resp)) ) + ret = resp.offset; + channel->KernelClose(); + return ret; +} + +ssize_t Unode::read(ioctx_t* ctx, uint8_t* buf, size_t count) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + ssize_t ret = -1; + struct fsm_req_read msg; + struct fsm_resp_read resp; + msg.ino = ino; + msg.count = count; + if ( SendMessage(channel, FSM_REQ_READ, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_READ, &resp, sizeof(resp)) ) + { + if ( resp.count < count ) + count = resp.count; + if ( channel->KernelRecv(ctx, buf, count) ) + ret = (ssize_t) count; + } + channel->KernelClose(); + return ret; +} + +ssize_t Unode::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + ssize_t ret = -1; + struct fsm_req_pread msg; + struct fsm_resp_read resp; + msg.ino = ino; + msg.count = count; + msg.offset = off; + if ( SendMessage(channel, FSM_REQ_PREAD, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_READ, &resp, sizeof(resp)) ) + { + if ( resp.count < count ) + count = resp.count; + if ( channel->KernelRecv(ctx, buf, count) ) + ret = (ssize_t) count; + } + channel->KernelClose(); + return ret; +} + +ssize_t Unode::write(ioctx_t* ctx, const uint8_t* buf, size_t count) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_write msg; + msg.ino = ino; + msg.count = count; + struct fsm_msg_header hdr; + hdr.msgtype = FSM_REQ_WRITE; + hdr.msgsize = sizeof(msg) + count; + struct fsm_resp_write resp; + if ( channel->KernelSend(&kctx, &hdr, sizeof(hdr)) && + channel->KernelSend(&kctx, &msg, sizeof(msg)) && + channel->KernelSend(ctx, buf, count) && + RecvMessage(channel, FSM_RESP_WRITE, &resp, sizeof(resp)) ) + ret = (ssize_t) resp.count; + channel->KernelClose(); + return ret; +} + +ssize_t Unode::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count, off_t off) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + ssize_t ret = -1; + struct fsm_req_pwrite msg; + msg.ino = ino; + msg.count = count; + msg.offset = off; + struct fsm_msg_header hdr; + hdr.msgtype = FSM_REQ_PWRITE; + hdr.msgsize = sizeof(msg) + count; + struct fsm_resp_write resp; + if ( channel->KernelSend(&kctx, &hdr, sizeof(hdr)) && + channel->KernelSend(&kctx, &msg, sizeof(msg)) && + channel->KernelSend(ctx, buf, count) && + RecvMessage(channel, FSM_RESP_WRITE, &resp, sizeof(resp)) ) + ret = (ssize_t) resp.count; + channel->KernelClose(); + return ret; +} + +int Unode::utimes(ioctx_t* /*ctx*/, const struct timeval times[2]) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_utimes msg; + msg.ino = ino; + msg.times[0] = times[0]; + msg.times[1] = times[1]; + if ( SendMessage(channel, FSM_REQ_UTIMES, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::isatty(ioctx_t* /*ctx*/) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return 0; + int ret = 0; + struct fsm_req_isatty msg; + msg.ino = ino; + if ( SendMessage(channel, FSM_REQ_ISATTY, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 1; + channel->KernelClose(); + return ret; +} + +ssize_t Unode::readdirents(ioctx_t* ctx, struct kernel_dirent* dirent, + size_t size, off_t start, size_t /*maxcount*/) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + ssize_t ret = -1; + struct fsm_req_readdirents msg; + struct fsm_resp_readdirents resp; + msg.ino = ino; + msg.rec_num = start; + errno = 0; + if ( SendMessage(channel, FSM_REQ_READDIRENTS, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_READDIRENTS, &resp, sizeof(resp)) ) + { + if ( !resp.namelen ) + { + ret = 0; + goto break_if; + } + + struct kernel_dirent entry; + entry.d_reclen = sizeof(entry) + resp.namelen + 1; + entry.d_off = 0; + entry.d_namelen = resp.namelen; + entry.d_dev = (dev_t) server; + entry.d_ino = resp.ino; + + if ( !ctx->copy_from_src(dirent, &entry, sizeof(entry)) ) + goto break_if; + + size_t needed = sizeof(*dirent) + resp.namelen + 1; + if ( size < needed && (errno = ERANGE) ) + goto break_if; + + uint8_t nul = 0; + if ( channel->KernelRecv(ctx, dirent->d_name, resp.namelen) && + ctx->copy_from_src(&dirent->d_name[resp.namelen], &nul, 1) ) + ret = (ssize_t) needed; + } break_if: + channel->KernelClose(); + return ret; +} + +Ref Unode::open(ioctx_t* /*ctx*/, const char* filename, int flags, + mode_t mode) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return Ref(NULL); + size_t filenamelen = strlen(filename); + Ref ret; + struct fsm_req_open msg; + msg.dirino = ino; + msg.flags = flags; + msg.mode = mode; + msg.namelen = filenamelen; + struct fsm_resp_open resp; + if ( SendMessage(channel, FSM_REQ_OPEN, &msg, sizeof(msg), filenamelen) && + channel->KernelSend(&kctx, filename, filenamelen) && + RecvMessage(channel, FSM_RESP_OPEN, &resp, sizeof(resp)) ) + ret = server->OpenNode(resp.ino, resp.type); + channel->KernelClose(); + return ret; +} + +int Unode::mkdir(ioctx_t* /*ctx*/, const char* filename, mode_t mode) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return 0; + size_t filenamelen = strlen(filename); + int ret = -1; + struct fsm_req_mkdir msg; + msg.dirino = ino; + msg.mode = mode; + msg.namelen = filenamelen; + struct fsm_resp_mkdir resp; + if ( SendMessage(channel, FSM_REQ_MKDIR, &msg, sizeof(msg), filenamelen) && + channel->KernelSend(&kctx, filename, filenamelen) && + RecvMessage(channel, FSM_RESP_MKDIR, &resp, sizeof(resp)) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::link(ioctx_t* /*ctx*/, const char* filename, Ref node) +{ + if ( node->dev != this->dev ) + return errno = EXDEV, -1; + Channel* channel = server->Connect(); + if ( !channel ) + return 0; + size_t filenamelen = strlen(filename); + int ret = -1; + struct fsm_req_link msg; + msg.dirino = ino; + msg.linkino = node->ino; + msg.namelen = filenamelen; + if ( SendMessage(channel, FSM_REQ_LINK, &msg, sizeof(msg), filenamelen) && + channel->KernelSend(&kctx, filename, filenamelen) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::link_raw(ioctx_t* /*ctx*/, const char* /*filename*/, Ref /*node*/) +{ + return errno = EPERM, -1; +} + +int Unode::unlink(ioctx_t* /*ctx*/, const char* filename) +{ + // TODO: Make sure the target is no longer used! + Channel* channel = server->Connect(); + if ( !channel ) + return 0; + size_t filenamelen = strlen(filename); + int ret = -1; + struct fsm_req_unlink msg; + msg.dirino = ino; + msg.namelen = filenamelen; + if ( SendMessage(channel, FSM_REQ_UNLINK, &msg, sizeof(msg), filenamelen) && + channel->KernelSend(&kctx, filename, filenamelen) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::unlink_raw(ioctx_t* /*ctx*/, const char* /*filename*/) +{ + return errno = EPERM, -1; +} + +int Unode::rmdir(ioctx_t* /*ctx*/, const char* filename) +{ + // TODO: Make sure the target is no longer used! + Channel* channel = server->Connect(); + if ( !channel ) + return 0; + size_t filenamelen = strlen(filename); + int ret = -1; + struct fsm_req_rmdir msg; + msg.dirino = ino; + msg.namelen = filenamelen; + if ( SendMessage(channel, FSM_REQ_RMDIR, &msg, sizeof(msg), filenamelen) && + channel->KernelSend(&kctx, filename, filenamelen) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::rmdir_me(ioctx_t* /*ctx*/) +{ + return errno = EPERM, -1; +} + +int Unode::symlink(ioctx_t* /*ctx*/, const char* oldname, const char* filename) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return 0; + size_t oldnamelen = strlen(oldname); + size_t filenamelen = strlen(filename); + int ret = -1; + struct fsm_req_symlink msg; + msg.dirino = ino; + msg.targetlen = oldnamelen; + msg.namelen = filenamelen; + size_t extra = msg.targetlen + msg.namelen; + if ( SendMessage(channel, FSM_REQ_SYMLINK, &msg, sizeof(msg), extra) && + channel->KernelSend(&kctx, oldname, oldnamelen) && + channel->KernelSend(&kctx, filename, filenamelen) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +ssize_t Unode::readlink(ioctx_t* ctx, char* buf, size_t bufsiz) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + ssize_t ret = -1; + struct fsm_req_readlink msg; + struct fsm_resp_readlink resp; + msg.ino = ino; + if ( SendMessage(channel, FSM_REQ_READLINK, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_READLINK, &resp, sizeof(resp)) ) + { + if ( resp.targetlen < bufsiz ) + bufsiz = resp.targetlen; + if ( channel->KernelRecv(ctx, buf, bufsiz) ) + ret = (ssize_t) bufsiz; + } + channel->KernelClose(); + return ret; +} + +int Unode::tcgetwinsize(ioctx_t* ctx, struct winsize* ws) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_tcgetwinsize msg; + struct fsm_resp_tcgetwinsize resp; + msg.ino = ino; + if ( SendMessage(channel, FSM_REQ_TCGETWINSIZE, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_TCGETWINSIZE, &resp, sizeof(resp)) && + ctx->copy_to_dest(ws, &resp.size, sizeof(*ws)) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::settermmode(ioctx_t* /*ctx*/, unsigned mode) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_settermmode msg; + msg.ino = ino; + msg.termmode = mode; + if ( SendMessage(channel, FSM_REQ_SETTERMMODE, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_SUCCESS, NULL, 0) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +int Unode::gettermmode(ioctx_t* ctx, unsigned* mode) +{ + Channel* channel = server->Connect(); + if ( !channel ) + return -1; + int ret = -1; + struct fsm_req_gettermmode msg; + struct fsm_resp_gettermmode resp; + msg.ino = ino; + if ( SendMessage(channel, FSM_REQ_GETTERMMODE, &msg, sizeof(msg)) && + RecvMessage(channel, FSM_RESP_GETTERMMODE, &resp, sizeof(resp)) && + ctx->copy_to_dest(mode, &resp.termmode, sizeof(*mode)) ) + ret = 0; + channel->KernelClose(); + return ret; +} + +// +// Initialization. +// + +class FactoryNode : public AbstractInode +{ +public: + FactoryNode(uid_t owner, gid_t group, mode_t mode); + virtual ~FactoryNode() { } + virtual Ref open(ioctx_t* ctx, const char* filename, int flags, + mode_t mode); + +}; + +FactoryNode::FactoryNode(uid_t owner, gid_t group, mode_t mode) +{ + inode_type = INODE_TYPE_UNKNOWN; + dev = (dev_t) this; + ino = 0; + this->type = S_IFDIR; + this->stat_uid = owner; + this->stat_gid = group; + this->stat_mode = (mode & S_SETABLE) | this->type; +} + +Ref FactoryNode::open(ioctx_t* /*ctx*/, const char* filename, + int /*flags*/, mode_t /*mode*/) +{ + if ( !strcmp(filename, "new") ) + { + Ref server(new Server()); + if ( !server ) + return Ref(NULL); + Ref node(new ServerNode(server)); + if ( !node ) + return Ref(NULL); + return node; + } + return errno = ENOENT, Ref(NULL); +} + +static int sys_fsm_fsbind(int rootfd, int mpointfd, int /*flags*/) +{ + Ref desc = CurrentProcess()->GetDescriptor(rootfd); + if ( !desc ) { return -1; } + Ref mpoint = CurrentProcess()->GetDescriptor(mpointfd); + if ( !mpoint ) { return -1; } + Ref mtable = CurrentProcess()->GetMTable(); + Ref node = desc->vnode->inode; + return mtable->AddMount(mpoint->ino, mpoint->dev, node) ? 0 : -1; +} + +void Init(const char* devpath, Ref slashdev) +{ + ioctx_t ctx; SetupKernelIOCtx(&ctx); + Ref node(new FactoryNode(0, 0, 0666)); + if ( !node ) + PanicF("Unable to allocate %s/fs inode.", devpath); + // TODO: Race condition! Create a mkdir function that returns what it + // created, possibly with a O_MKDIR flag to open. + if ( slashdev->mkdir(&ctx, "fs", 0755) < 0 && errno != EEXIST ) + PanicF("Could not create a %s/fs directory", devpath); + Ref mpoint = slashdev->open(&ctx, "fs", O_RDWR, 0); + if ( !mpoint ) + PanicF("Could not open the %s/fs directory", devpath); + Ref mtable = CurrentProcess()->GetMTable(); + // TODO: Make sure that the mount point is *empty*! Add a proper function + // for this on the file descriptor class! + if ( !mtable->AddMount(mpoint->ino, mpoint->dev, node) ) + PanicF("Unable to mount filesystem on %s/fs", devpath); + + Syscall::Register(SYSCALL_FSM_FSBIND, (void*) sys_fsm_fsbind); +} + +} // namespace UserFS +} // namespace Sortix diff --git a/sortix/fs/user.h b/sortix/fs/user.h new file mode 100644 index 00000000..e32ae579 --- /dev/null +++ b/sortix/fs/user.h @@ -0,0 +1,36 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + fs/user.h + User-space filesystem. + +*******************************************************************************/ + +#ifndef SORTIX_FS_USER_H +#define SORTIX_FS_USER_H + +namespace Sortix { +namespace UserFS { + +void Init(const char* devpath, Ref slashdev); + +} // namespace UserFS +} // namespace Sortix + +#endif diff --git a/sortix/include/sortix/kernel/vnode.h b/sortix/include/sortix/kernel/vnode.h index ac738aea..a18a3cc7 100644 --- a/sortix/include/sortix/kernel/vnode.h +++ b/sortix/include/sortix/kernel/vnode.h @@ -75,7 +75,7 @@ public: int settermmode(ioctx_t* ctx, unsigned mode); int gettermmode(ioctx_t* ctx, unsigned* mode); -private: +public /*TODO: private*/: Ref inode; Ref mountedat; ino_t rootino; diff --git a/sortix/include/sortix/syscallnum.h b/sortix/include/sortix/syscallnum.h index 23433514..b8e77f09 100644 --- a/sortix/include/sortix/syscallnum.h +++ b/sortix/include/sortix/syscallnum.h @@ -92,6 +92,7 @@ #define SYSCALL_FCHMOD 68 #define SYSCALL_FCHMODAT 69 #define SYSCALL_LINKAT 70 -#define SYSCALL_MAX_NUM 71 /* index of highest constant + 1 */ +#define SYSCALL_FSM_FSBIND 71 +#define SYSCALL_MAX_NUM 72 /* index of highest constant + 1 */ #endif diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index 95af26b8..16c2a196 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -75,6 +75,7 @@ #include "interrupt.h" #include "dispmsg.h" #include "fs/kram.h" +#include "fs/user.h" #include "kb/ps2.h" #include "kb/layout/us.h" @@ -410,6 +411,9 @@ static void BootThread(void* /*user*/) // Initialize the BGA driver. BGA::Init(); + // Initialize the user-space filesystem framework. + UserFS::Init("/dev", slashdev); + // // Stage 6. Executing Hosted Environment ("User-Space") //