X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=KernelLand%2FKernel%2Fvfs%2Fopen.c;h=e5a206fbe3a6376dcc8bb00500cd0c7c8dcf2232;hb=9c4eedf4893f851bd1ba60ce541c8d098a9ef7f7;hp=e7d6e1e80d2aa7eb0271b125ea349ebd8e1c8690;hpb=4842e2d6740bcb81da4e94019285bfd2c45425b8;p=tpg%2Facess2.git diff --git a/KernelLand/Kernel/vfs/open.c b/KernelLand/Kernel/vfs/open.c index e7d6e1e8..e5a206fb 100644 --- a/KernelLand/Kernel/vfs/open.c +++ b/KernelLand/Kernel/vfs/open.c @@ -2,6 +2,7 @@ * Acess2 VFS * - Open, Close and ChDir */ +#define SANITY 1 #define DEBUG 0 #include #include "vfs.h" @@ -14,16 +15,41 @@ #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 int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode); extern tVFS_Node *VFS_MemFile_Create(const char *Path); +// === TYPES === +typedef struct sVFS_MarshaledHandle +{ + Uint32 Magic; + tTime AllocTime; + tVFS_Handle Handle; +} tVFS_MarshalledHandle; + // === PROTOTYPES === - int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode ); +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) +{ +// Log_Debug("VFS", "%s: inc. mntpt '%s' to %i", DebugTag, Mount->MountPoint, Mount->OpenHandleCount+1); + Mount->OpenHandleCount ++; +} +void _DereferenceMount(tVFS_Mount *Mount, const char *DebugTag) +{ +// Log_Debug("VFS", "%s: dec. mntpt '%s' to %i", DebugTag, Mount->MountPoint, Mount->OpenHandleCount-1); + ASSERT(Mount->OpenHandleCount > 0); + Mount->OpenHandleCount --; +} /** * \fn char *VFS_GetAbsPath(const char *Path) * \brief Create an absolute path from a relative one @@ -36,9 +62,9 @@ char *VFS_GetAbsPath(const char *Path) char *tmpStr; int iPos = 0; int iPos2 = 0; - const char *chroot = *Threads_GetChroot(); + const char *chroot = *Threads_GetChroot(NULL); int chrootLen; - const char *cwd = *Threads_GetCWD(); + const char *cwd = *Threads_GetCWD(NULL); int cwdLen; ENTER("sPath", Path); @@ -201,6 +227,7 @@ restart_parse: *TruePath = malloc( gVFS_RootMount->MountPointLen+1 ); strcpy(*TruePath, gVFS_RootMount->MountPoint); } + _ReferenceMount(gVFS_RootMount, "ParsePath - Fast Tree Root"); if(MountPoint) *MountPoint = gVFS_RootMount; LEAVE('p', gVFS_RootMount->RootNode); return gVFS_RootMount->RootNode; @@ -238,13 +265,19 @@ restart_parse: *MountPoint = mnt; RWLock_Release( &glVFS_MountList ); LOG("Mount %p root", mnt); + _ReferenceMount(mnt, "ParsePath - Mount Root"); LEAVE('p', mnt->RootNode); return mnt->RootNode; } #endif longestMount = mnt; } - longestMount->OpenHandleCount ++; // Increment assuimg it worked + if(!longestMount) { + Log_Panic("VFS", "VFS_ParsePath - No mount for '%s'", Path); + return NULL; + } + + _ReferenceMount(longestMount, "ParsePath"); RWLock_Release( &glVFS_MountList ); // Save to shorter variable @@ -276,20 +309,28 @@ restart_parse: pathEle[nextSlash] = 0; // Check permissions on root of filesystem - if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) { + if( !VFS_CheckACL(curNode, VFS_PERM_EXEC) ) { LOG("Permissions failure on '%s'", Path); + errno = EPERM; goto _error; } // Check if the node has a FindDir method + if( !curNode->Type ) + { + LOG("Finddir failure on '%s' - No type", Path); + Log_Error("VFS", "Node at '%s' has no type (mount %s:%s)", + Path, mnt->Filesystem->Name, mnt->MountPoint); + goto _error; + } if( !curNode->Type->FindDir ) { - LOG("Finddir failure on '%s'", Path); + LOG("Finddir failure on '%s' - No FindDir method in %s", Path, curNode->Type->TypeName); goto _error; } LOG("FindDir{=%p}(%p, '%s')", curNode->Type->FindDir, curNode, pathEle); // Get Child Node - tmpNode = curNode->Type->FindDir(curNode, pathEle); + tmpNode = curNode->Type->FindDir(curNode, pathEle, 0); LOG("tmpNode = %p", tmpNode); _CloseNode( curNode ); curNode = tmpNode; @@ -297,6 +338,7 @@ restart_parse: // Error Check if(!curNode) { LOG("Node '%s' not found in dir '%s'", pathEle, Path); + errno = ENOENT; goto _error; } @@ -309,11 +351,13 @@ restart_parse: if(!curNode->Type || !curNode->Type->Read) { Log_Warning("VFS", "VFS_ParsePath - Read of symlink node %p'%s' is NULL", curNode, Path); + errno = EINTERNAL; goto _error; } if(iNestedLinks > MAX_NESTED_LINKS) { - Log_Notice("VFS", "VFS_ParsePath - Nested link limit exceeded"); + Log_Notice("VFS", "VFS_ParsePath - Nested link limit exceeded on '%.*s'", ofs, Path); + errno = ENOENT; goto _error; } @@ -324,12 +368,14 @@ restart_parse: int remlen = strlen(Path) - (ofs + nextSlash); if( curNode->Size + remlen > MAX_PATH_LEN ) { Log_Warning("VFS", "VFS_ParsePath - Symlinked path too long"); + errno = ENOENT; goto _error; } - curNode->Type->Read( curNode, 0, curNode->Size, path_buffer ); + curNode->Type->Read( curNode, 0, curNode->Size, path_buffer, 0 ); path_buffer[ curNode->Size ] = '\0'; LOG("path_buffer = '%s'", path_buffer); strcat(path_buffer, Path + ofs+nextSlash); + // TODO: Pass to VFS_GetAbsPath to handle ../. in the symlink Path = path_buffer; // Log_Debug("VFS", "VFS_ParsePath: Symlink translated to '%s'", Path); @@ -338,14 +384,16 @@ restart_parse: // EVIL: Goto :) LOG("Symlink -> '%s', restart", Path); - mnt->OpenHandleCount --; // Not in this mountpoint + _DereferenceMount(mnt, "ParsePath - sym"); goto restart_parse; } // Handle Non-Directories if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) ) { - Log_Warning("VFS", "VFS_ParsePath - Path segment is not a directory"); + Log_Warning("VFS", "VFS_ParsePath - Path segment '%.*s' is not a directory (curNode{%p}->Flags = 0x%x)", + ofs+nextSlash, Path, curNode, curNode->Flags); + errno = ENOTDIR; goto _error; } @@ -357,6 +405,7 @@ restart_parse: // Check if allocation succeeded if(!tmp) { Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer"); + errno = ENOMEM; goto _error; } *TruePath = tmp; @@ -372,17 +421,19 @@ restart_parse: // Check final finddir call if( !curNode->Type || !curNode->Type->FindDir ) { - Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for element of '%s'", Path); + Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for leaf of '%s' (dir '%.*s')", Path, ofs, Path); + errno = ENOENT; goto _error; } // Get last node LOG("FindDir(%p, '%s')", curNode, &Path[ofs]); - tmpNode = curNode->Type->FindDir(curNode, &Path[ofs]); + tmpNode = curNode->Type->FindDir(curNode, &Path[ofs], 0); LOG("tmpNode = %p", tmpNode); // Check if file was found if(!tmpNode) { - LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path); + LOG("Node '%s' not found in dir '%.*s'", &Path[ofs], ofs, Path); + errno = ENOENT; goto _error; } _CloseNode( curNode ); @@ -394,6 +445,7 @@ restart_parse: // Check if allocation succeeded if(!tmp) { Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer"); + errno = ENOMEM; goto _error; } *TruePath = tmp; @@ -421,8 +473,7 @@ _error: *TruePath = NULL; } // Open failed, so decrement the open handle count - mnt->OpenHandleCount --; - + _DereferenceMount(mnt, "ParsePath - error"); LEAVE('n'); return NULL; } @@ -437,7 +488,7 @@ int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode ) ENTER("pNode pMount xMode", Node, Mount, Mode); i = 0; - i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0; + i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXEC : 0; i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0; i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0; @@ -450,6 +501,13 @@ int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode ) errno = EACCES; LEAVE_RET('i', -1); } + + if( MM_GetPhysAddr(Node->Type) == 0 ) { + Log_Error("VFS", "Node %p from mount '%s' (%s) has a bad type (%p)", + Node, Mount->MountPoint, Mount->Filesystem->Name, Node->Type); + errno = EINTERNAL; + LEAVE_RET('i', -1); + } i = VFS_AllocHandle( !!(Mode & VFS_OPENFLAG_USER), Node, Mode ); if( i < 0 ) { @@ -495,13 +553,61 @@ int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode) if( !node && (Flags & VFS_OPENFLAG_CREATE) ) { // TODO: Translate `Mode` into ACL and node flags - // Get parent, create node - if( VFS_MkNod(absPath, 0) ) { + Uint new_flags = 0; + + // Split path at final separator + char *file = strrchr(absPath, '/'); + *file = '\0'; + file ++; + + // Get parent node + tVFS_Mount *pmnt; + tVFS_Node *pnode = VFS_ParsePath(absPath, NULL, &pmnt); + if(!pnode) { + LOG("Unable to open parent '%s'", absPath); + free(absPath); + errno = ENOENT; + LEAVE_RET('i', -1); + } + + // Check ACLs on the parent + if( !VFS_CheckACL(pnode, VFS_PERM_EXEC|VFS_PERM_WRITE) ) { + errno = EACCES; + goto _pnode_err; + } + + // Check that there's a MkNod method + if( !pnode->Type || !pnode->Type->MkNod ) { + Log_Warning("VFS", "VFS_Open - Directory has no MkNod method"); + errno = EINVAL; + goto _pnode_err; + } + + node = pnode->Type->MkNod(pnode, file, new_flags); + if( !node ) { + LOG("Cannot create node '%s' in '%s'", file, absPath); + errno = ENOENT; + goto _pnode_err; + } + // Set mountpoint (and increment open handle count) + mnt = pmnt; + _ReferenceMount(mnt, "Open - create"); + // Fall through on error check + + _CloseNode(pnode); + _DereferenceMount(pmnt, "Open - create"); + goto _pnode_ok; + + _pnode_err: + if( pnode ) { + _CloseNode(pnode); + _DereferenceMount(pmnt, "Open - create,fail"); free(absPath); - return -1; } - node = VFS_ParsePath(absPath, NULL, &mnt); + LEAVE('i', -1); + return -1; } + _pnode_ok: // Free generated path free(absPath); @@ -511,7 +617,7 @@ int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode) { LOG("Cannot find node"); errno = ENOENT; - LEAVE_RET('i', -1); + goto _error; } // Check for symlinks @@ -520,26 +626,35 @@ int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode) char tmppath[node->Size+1]; if( node->Size > MAX_PATH_LEN ) { Log_Warning("VFS", "VFS_Open - Symlink is too long (%i)", node->Size); - LEAVE_RET('i', -1); + goto _error; } if( !node->Type || !node->Type->Read ) { Log_Warning("VFS", "VFS_Open - No read method on symlink"); - LEAVE_RET('i', -1); + goto _error; } // Read symlink's path - node->Type->Read( node, 0, node->Size, tmppath ); + node->Type->Read( node, 0, node->Size, tmppath, 0 ); tmppath[ node->Size ] = '\0'; _CloseNode( node ); + _DereferenceMount(mnt, "Open - symlink"); // Open the target node = VFS_ParsePath(tmppath, NULL, &mnt); if(!node) { LOG("Cannot find symlink target node (%s)", tmppath); errno = ENOENT; - LEAVE_RET('i', -1); + goto _error; } } - LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Flags)); + int ret = VFS_int_CreateHandle(node, mnt, Flags); + LEAVE_RET('x', ret); +_error: + if( node ) + { + _DereferenceMount(mnt, "Open - error"); + _CloseNode(node); + } + LEAVE_RET('i', -1); } @@ -576,14 +691,14 @@ int VFS_OpenChild(int FD, const char *Name, Uint Mode) } // Find Child - node = h->Node->Type->FindDir(h->Node, Name); + node = h->Node->Type->FindDir(h->Node, Name, 0); if(!node) { errno = ENOENT; LEAVE_RET('i', -1); } // Increment open handle count, no problems with the mount going away as `h` is already open on it - h->Mount->OpenHandleCount ++; + _ReferenceMount(h->Mount, "OpenChild"); LEAVE_RET('x', VFS_int_CreateHandle(node, h->Mount, Mode)); } @@ -605,7 +720,8 @@ int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode) // Does the filesystem support this? if( !mnt->Filesystem->GetNodeFromINode ) { - LOG("Filesystem does not support inode accesses"); + Log_Notice("VFS", "Filesystem '%s' does not support inode accesses", + mnt->Filesystem->Name); errno = ENOENT; LEAVE_RET('i', -1); } @@ -621,6 +737,31 @@ int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode) LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Mode)); } +int VFS_Reopen(int FD, const char *Path, int Flags) +{ + tVFS_Handle *h = VFS_GetHandle(FD); + if(!h) { + errno = EBADF; + return -1; + } + + int newf = VFS_Open(Path, Flags); + if( newf == -1 ) { + // errno = set by VFS_Open + return -1; + } + + _CloseNode(h->Node); + _DereferenceMount(h->Mount, "Reopen"); + memcpy(h, VFS_GetHandle(newf), sizeof(*h)); + _ReferenceNode(h->Node); + _ReferenceMount(h->Mount, "Reopen"); + + VFS_Close(newf); + + return FD; +} + /** * \fn void VFS_Close(int FD) * \brief Closes an open file handle @@ -633,24 +774,73 @@ 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 ; + } + #if VALIDATE_VFS_FUNCTIPONS 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 + LOG("Handle %x", FD); _CloseNode(h->Node); - h->Mount->OpenHandleCount --; + if( h->Mount ) { + _DereferenceMount(h->Mount, "Close"); + } h->Node = NULL; } +int VFS_DuplicateFD(int SrcFD, int DstFD) +{ + int isUser = !(SrcFD & VFS_KERNEL_FLAG); + tVFS_Handle *src = VFS_GetHandle(SrcFD); + if( !src ) return -1; + if( DstFD == -1 ) { + DstFD = VFS_AllocHandle(isUser, src->Node, src->Mode); + } + else { + // Can't overwrite + if( VFS_GetHandle(DstFD) ) + return -1; + VFS_SetHandle(DstFD, src->Node, src->Mode); + } + _ReferenceMount(src->Mount, "DuplicateFD"); + _ReferenceNode(src->Node); + memcpy(VFS_GetHandle(DstFD), src, sizeof(tVFS_Handle)); + return DstFD; +} + +/* + * Update flags on a FD + */ +int VFS_SetFDFlags(int FD, int Mask, int Value) +{ + tVFS_Handle *h = VFS_GetHandle(FD); + if(!h) { + errno = EBADF; + return -1; + } + int ret = h->Mode; + + Value &= Mask; + h->Mode &= ~Mask; + h->Mode |= Value; + return ret; +} + /** * \brief Change current working directory */ @@ -686,14 +876,14 @@ int VFS_ChDir(const char *Dest) VFS_Close(fd); { - char **cwdptr = Threads_GetCWD(); + char **cwdptr = Threads_GetCWD(NULL); // Free old working directory if( *cwdptr ) free( *cwdptr ); // Set new *cwdptr = buf; } - Log("Updated CWD to '%s'", buf); + Log_Debug("VFS", "Updated CWD to '%s'", buf); return 1; } @@ -738,7 +928,7 @@ int VFS_ChRoot(const char *New) // Update { - char **chroot_ptr = Threads_GetChroot(); + char **chroot_ptr = Threads_GetChroot(NULL); if( *chroot_ptr ) free( *chroot_ptr ); *chroot_ptr = buf; } @@ -748,6 +938,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);