From 3c3c26b58055f511af5b7f0c3ab22e83961c775f Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 22 Jun 2014 09:44:16 +0800 Subject: [PATCH] Kernel/VFS - Kernel-side implementation of marshalled FDs --- KernelLand/Kernel/include/vfs_ext.h | 14 ++++ KernelLand/Kernel/syscalls.c | 14 +++- KernelLand/Kernel/syscalls.lst | 4 ++ KernelLand/Kernel/vfs/open.c | 107 ++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) diff --git a/KernelLand/Kernel/include/vfs_ext.h b/KernelLand/Kernel/include/vfs_ext.h index 65a81624..99a64e3b 100644 --- a/KernelLand/Kernel/include/vfs_ext.h +++ b/KernelLand/Kernel/include/vfs_ext.h @@ -226,6 +226,20 @@ extern int VFS_DuplicateFD(int SrcFD, int DstFD); */ extern int VFS_SetFDFlags(int FD, int Mask, int Value); +/** + * \brief Save specified file handle such that it can be passed between processes + * \param FD File descriptor to save + * \return Marshalled handle, or (uint64_t)-1 on error + */ +extern Uint64 VFS_MarshalHandle(int FD); + +/** + * \brief Restore a marshalled handle into the current process + * \param Handle returned by VFS_MarshalHandle + * \return File descriptor, or -1 on error + */ +extern int VFS_UnmarshalHandle(Uint64 Handle); + /** * \brief Get file information from an open file * \param FD File handle returned by ::VFS_Open diff --git a/KernelLand/Kernel/syscalls.c b/KernelLand/Kernel/syscalls.c index ccbe6774..75ed8745 100644 --- a/KernelLand/Kernel/syscalls.c +++ b/KernelLand/Kernel/syscalls.c @@ -403,7 +403,19 @@ void SyscallHandler(tSyscallRegs *Regs) case SYS_UNLINK: Log_Error("Syscalls", "TODO: Impliment SYS_UNLINK"); - // Fall + break; + + case SYS_MARSHALFD: + ret = VFS_MarshalHandle(Regs->Arg1); + break; + case SYS_UNMARSHALFD: + #if BITS == 64 + ret = VFS_UnmarshalHandle( Regs->Arg1 ); + #else + ret = VFS_UnmarshalHandle( ARG64(1,2) ); + #endif + break; + // -- Debug //#if DEBUG_BUILD case SYS_DEBUG: diff --git a/KernelLand/Kernel/syscalls.lst b/KernelLand/Kernel/syscalls.lst index 6947daaa..d3b45573 100644 --- a/KernelLand/Kernel/syscalls.lst +++ b/KernelLand/Kernel/syscalls.lst @@ -71,3 +71,7 @@ SYS_CHDIR Change current directory SYS_GETCWD Get current directory SYS_MOUNT Mount a filesystem SYS_SELECT Wait for file handles + +SYS_MARSHALFD Create a reference to a FD suitable for handing to another process +SYS_UNMARSHALFD Accept a marshaled FD + diff --git a/KernelLand/Kernel/vfs/open.c b/KernelLand/Kernel/vfs/open.c index 27e9878c..77e58f5c 100644 --- a/KernelLand/Kernel/vfs/open.c +++ b/KernelLand/Kernel/vfs/open.c @@ -15,16 +15,29 @@ #define MAX_PATH_SLASHES 256 #define MAX_NESTED_LINKS 4 #define MAX_PATH_LEN 255 +#define MAX_MARSHALLED_HANDLES 16 // Max outstanding // === IMPORTS === extern tVFS_Mount *gVFS_RootMount; extern tVFS_Node *VFS_MemFile_Create(const char *Path); +// === TYPES === +typedef struct sVFS_MarshaledHandle +{ + Uint32 Magic; + tTime AllocTime; + tVFS_Handle Handle; +} tVFS_MarshalledHandle; + // === PROTOTYPES === void _ReferenceMount(tVFS_Mount *Mount, const char *DebugTag); void _DereferenceMount(tVFS_Mount *Mount, const char *DebugTag); int VFS_int_CreateHandle(tVFS_Node *Node, tVFS_Mount *Mount, int Mode); +// === GLOBALS === +tMutex glVFS_MarshalledHandles; +tVFS_MarshalledHandle gaVFS_MarshalledHandles[MAX_MARSHALLED_HANDLES]; + // === CODE === void _ReferenceMount(tVFS_Mount *Mount, const char *DebugTag) { @@ -733,6 +746,7 @@ int VFS_Reopen(int FD, const char *Path, int Flags) int newf = VFS_Open(Path, Flags); if( newf == -1 ) { + // errno = set by VFS_Open return -1; } @@ -759,11 +773,13 @@ void VFS_Close(int FD) h = VFS_GetHandle(FD); if(h == NULL) { Log_Warning("VFS", "Invalid file handle passed to VFS_Close, 0x%x", FD); + errno = EINVAL; return; } if( h->Node == NULL ) { Log_Warning("VFS", "Non-open handle passed to VFS_Close, 0x%x", FD); + errno = EINVAL; return ; } @@ -771,6 +787,7 @@ void VFS_Close(int FD) if(h->Node->Close && !MM_GetPhysAddr(h->Node->Close)) { Log_Warning("VFS", "Node %p's ->Close method is invalid (%p)", h->Node, h->Node->Close); + errno = EINTERNAL; return ; } #endif @@ -920,6 +937,96 @@ int VFS_ChRoot(const char *New) return 1; } +/* + * Marshal a handle so that it can be transferred between processes + */ +Uint64 VFS_MarshalHandle(int FD) +{ + tVFS_Handle *h = VFS_GetHandle(FD); + if(!h || !h->Node) { + errno = EBADF; + return -1; + } + + // Allocate marshal location + int ret = -1; + Mutex_Acquire(&glVFS_MarshalledHandles); + for( int i = 0; i < MAX_MARSHALLED_HANDLES; i ++ ) + { + tVFS_MarshalledHandle* mh = &gaVFS_MarshalledHandles[i]; + if( mh->Handle.Node == NULL ) { + mh->Handle.Node = h->Node; + mh->AllocTime = now(); + ret = i; + } + if( now() - mh->AllocTime > 2000 ) { + Log_Notice("VFS", "TODO: Expire marshalled handle"); + } + } + Mutex_Release(&glVFS_MarshalledHandles); + if( ret < 0 ) { + // TODO: Need to clean up lost handles to avoid DOS + Log_Warning("VFS", "Out of marshaled handle slots"); + errno = EAGAIN; + return -1; + } + + // Populate + tVFS_MarshalledHandle* mh = &gaVFS_MarshalledHandles[ret]; + mh->Handle = *h; + _ReferenceMount(h->Mount, "MarshalHandle"); + _ReferenceNode(h->Node); + mh->Magic = rand(); + + return (Uint64)mh->Magic << 32 | ret; +} + +/* + * Un-marshal a handle into the current process + * NOTE: Does not support unmarshalling into kernel handle list + */ +int VFS_UnmarshalHandle(Uint64 Handle) +{ + Uint32 magic = Handle >> 32; + int id = Handle & 0xFFFFFFFF; + + // Range check + if( id >= MAX_MARSHALLED_HANDLES ) { + LOG("ID too high (%i > %i)", id, MAX_MARSHALLED_HANDLES); + errno = EINVAL; + return -1; + } + + + // Check validity + tVFS_MarshalledHandle *mh = &gaVFS_MarshalledHandles[id]; + if( mh->Handle.Node == NULL ) { + LOG("Target node is NULL"); + errno = EINVAL; + return -1; + } + if( mh->Magic != magic ) { + LOG("Magic mismatch (0x%08x != 0x%08x)", magic, mh->Magic); + errno = EACCES; + return -1; + } + + Mutex_Acquire(&glVFS_MarshalledHandles); + // - Create destination handle + int ret = VFS_AllocHandle(true, mh->Handle.Node, mh->Handle.Mode); + // - Clear allocation + mh->Handle.Node = NULL; + Mutex_Release(&glVFS_MarshalledHandles); + if( ret == -1 ) { + errno = ENFILE; + return -1; + } + + // No need to reference node/mount, new handle takes marshalled reference + + return ret; +} + // === EXPORTS === EXPORT(VFS_Open); EXPORT(VFS_Close); -- 2.20.1