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")
//