#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 ===
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 );
+ 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)
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);
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;
}
}
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;
// Error Check
if(!curNode) {
LOG("Node '%s' not found in dir '%s'", pathEle, Path);
+ errno = ENOENT;
goto _error;
}
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");
+ errno = ENOENT;
goto _error;
}
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);
if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) )
{
Log_Warning("VFS", "VFS_ParsePath - Path segment is not a directory");
+ errno = ENOTDIR;
goto _error;
}
// Check if allocation succeeded
if(!tmp) {
Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer");
+ errno = ENOMEM;
goto _error;
}
*TruePath = tmp;
// Check final finddir call
if( !curNode->Type || !curNode->Type->FindDir ) {
Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for element of '%s'", 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], ofs, Path);
+ errno = ENOENT;
goto _error;
}
_CloseNode( curNode );
// Check if allocation succeeded
if(!tmp) {
Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer");
+ errno = ENOMEM;
goto _error;
}
*TruePath = tmp;
}
// Open failed, so decrement the open handle count
_DereferenceMount(mnt, "ParsePath - error");
-
LEAVE('n');
return NULL;
}
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;
}
// Check ACLs on the parent
- if( !VFS_CheckACL(pnode, VFS_PERM_EXECUTE|VFS_PERM_WRITE) ) {
+ if( !VFS_CheckACL(pnode, VFS_PERM_EXEC|VFS_PERM_WRITE) ) {
errno = EACCES;
goto _pnode_err;
}
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");
}
// 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);
// 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);
}
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
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(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
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
*/
VFS_Close(fd);
{
- char **cwdptr = Threads_GetCWD();
+ char **cwdptr = Threads_GetCWD(NULL);
// Free old working directory
if( *cwdptr ) free( *cwdptr );
// Set new
// Update
{
- char **chroot_ptr = Threads_GetChroot();
+ char **chroot_ptr = Threads_GetChroot(NULL);
if( *chroot_ptr ) free( *chroot_ptr );
*chroot_ptr = buf;
}
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);