EINVAL, // Invalid Paramater
ENOMEM, // No free memory
EACCES, // Not permitted
+ EBUSY, // Resource is busy
ENOTFOUND, // Item not found
EREADONLY, // Read only
ENOTIMPL, // Not implemented
* \return 1 on succes, -1 on error
*/
extern int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options);
+/**
+ * \brief Unmount a mounted filesystem
+ * \param Mountpoint Location of the mount
+ * \return 0 on success, errno on error
+ */
+extern int VFS_Unmount(const char *Mountpoint);
+/**
+ * \brief Attemt to unmount all fileystems
+ * \return Number of unmounted filesytems, -1 if none left to unmount
+ * \note Can return 0 when there are stil volumes mounted if there are open handles
+ */
+extern int VFS_UnmountAll(void);
+
/**
* \brief Create a new directory
* \param Path Path to new directory (absolute or relative)
#define _VFS_INT_H
#include "vfs.h"
+#include <rwlock.h>
// === TYPES ===
typedef struct sVFS_Mount {
char *Options;
tVFS_Driver *Filesystem;
tVFS_Node *RootNode;
+
+ int OpenHandleCount;
+
char StrData[];
} tVFS_Mount;
} tVFS_MMapPage;
// === GLOBALS ===
+extern tRWLock glVFS_MountList;
extern tVFS_Mount *gVFS_Mounts;
// === PROTOTYPES ===
*/
int VFS_MkNod(const char *Path, Uint Flags)
{
+ tVFS_Mount *mountpt;
char *absPath, *name;
int pos = 0, oldpos = 0;
int next = 0;
// Check for root
if(absPath[0] == '\0')
- parent = VFS_ParsePath("/", NULL, NULL);
+ parent = VFS_ParsePath("/", NULL, &mountpt);
else
- parent = VFS_ParsePath(absPath, NULL, NULL);
+ parent = VFS_ParsePath(absPath, NULL, &mountpt);
LOG("parent = %p", parent);
LEAVE('i', -1);
return -1; // Error Check
}
-
+
// Permissions Check
if( !VFS_CheckACL(parent, VFS_PERM_EXECUTE|VFS_PERM_WRITE) ) {
_CloseNode(parent);
+ mountpt->OpenHandleCount --;
free(absPath);
LEAVE('i', -1);
return -1;
LOG("parent = %p", parent);
if(!parent->Type || !parent->Type->MkNod) {
- Warning("VFS_MkNod - Directory has no MkNod method");
+ Log_Warning("VFS", "VFS_MkNod - Directory has no MkNod method");
+ mountpt->OpenHandleCount --;
LEAVE('i', -1);
return -1;
}
free(absPath);
// Free Parent
+ mountpt->OpenHandleCount --;
_CloseNode(parent);
// Error Check
void DevFS_DelDevice(tDevFS_Driver *Device);
#endif
tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options);
+void DevFS_Unmount(tVFS_Node *RootNode);
char *DevFS_ReadDir(tVFS_Node *Node, int Pos);
tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name);
// === GLOBALS ===
tVFS_Driver gDevFS_Info = {
- "devfs", 0, DevFS_InitDevice, NULL, NULL
+ "devfs", 0, DevFS_InitDevice, DevFS_Unmount, NULL
};
tVFS_NodeType gDevFS_DirType = {
.TypeName = "DevFS-Dir",
return &gDevFS_RootNode;
}
+void DevFS_Unmount(tVFS_Node *RootNode)
+{
+
+}
+
/**
* \fn char *DevFS_ReadDir(tVFS_Node *Node, int Pos)
*/
// Create Root Node
root = &RootFS_Files[0];
-
+
+ root->Name[0] = '/';
+ root->Name[1] = '\0';
root->Node.ImplPtr = root;
root->Node.CTime
{
tRamFS_File *parent = Node->ImplPtr;
tRamFS_File *child;
- tRamFS_File *prev = (tRamFS_File *) &parent->Data.FirstChild;
+ tRamFS_File *prev = NULL;
ENTER("pNode sName xFlags", Node, Name, Flags);
- LOG("%i > %i", strlen(Name)+1, sizeof(child->Name));
+ LOG("Sanity check name length - %i > %i", strlen(Name)+1, sizeof(child->Name));
if(strlen(Name) + 1 > sizeof(child->Name))
LEAVE_RET('i', 0);
for( child = parent->Data.FirstChild; child; prev = child, child = child->Next )
{
if(strcmp(child->Name, Name) == 0) {
+ LOG("Duplicate");
LEAVE('i', 0);
return 0;
}
memset(child, 0, sizeof(tRamFS_File));
strcpy(child->Name, Name);
+ LOG("Name = '%s'", child->Name);
child->Parent = parent;
child->Next = NULL;
child->Node.Type = &gRootFS_FileType;
}
- prev->Next = child;
+ // Append!
+ if( prev )
+ prev->Next = child;
+ else
+ parent->Data.FirstChild = child;
parent->Node.Size ++;
tRamFS_File *child = parent->Data.FirstChild;
ENTER("pNode sName", Node, Name);
- //Log("Root_FindDir: (Node=%p, Name='%s')", Node, Name);
- for(;child;child = child->Next)
+ for( child = parent->Data.FirstChild; child; child = child->Next )
{
- //Log(" Root_FindDir: strcmp('%s', '%s')", child->Node.Name, Name);
LOG("child->Name = '%s'", child->Name);
- if(strcmp(child->Name, Name) == 0) {
+ if(strcmp(child->Name, Name) == 0)
+ {
LEAVE('p', &child->Node);
return &child->Node;
}
continue ;
if( h->Node->Type && h->Node->Type->Reference )
h->Node->Type->Reference( h->Node );
+ h->Mount->OpenHandleCount ++;
}
return ret;
continue ;
if( h->Node->Type && h->Node->Type->Reference )
h->Node->Type->Reference( h->Node );
+ h->Mount->OpenHandleCount ++;
}
}
continue ;
if( h->Node->Type && h->Node->Type->Close )
h->Node->Type->Close( h->Node );
+ h->Mount->OpenHandleCount --;
}
free( Handles );
}
/*
* Acess Micro - VFS Server version 1
*/
+#define DEBUG 1
#include <acess.h>
#include <vfs.h>
#include <vfs_int.h>
void VFS_UpdateMountFile(void);
// === GLOBALS ===
-tMutex glVFS_MountList;
+tRWLock glVFS_MountList;
tVFS_Mount *gVFS_Mounts;
tVFS_Mount *gVFS_RootMount = NULL;
Uint32 giVFS_NextMountIdent = 1;
*/
int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options)
{
- tVFS_Mount *mnt;
+ tVFS_Mount *mnt, *parent_mnt;
tVFS_Driver *fs;
int deviceLen = strlen(Device);
int mountLen = strlen(MountPoint);
if(!mnt) {
return -2;
}
+
+ // Validate the mountpoint target
+ // - Only if / is mounted
+ if( gVFS_Mounts )
+ {
+ tVFS_Node *mpnode = VFS_ParsePath(MountPoint, NULL, &parent_mnt);
+ if( !mpnode ) {
+ Log_Warning("VFS", "VFS_Mount - Mountpoint '%s' does not exist", MountPoint);
+ return -1;
+ }
+ if( mpnode->Type->Close )
+ mpnode->Type->Close(mpnode);
+ if( parent_mnt->RootNode == mpnode ) {
+ Log_Warning("VFS", "VFS_Mount - Attempt to mount over '%s' (%s)",
+ MountPoint, parent_mnt->MountPoint);
+ return -1;
+ }
+ }
// HACK: Forces VFS_ParsePath to fall back on root
if(mountLen == 1 && MountPoint[0] == '/')
// Fill Structure
mnt->Filesystem = fs;
+ mnt->OpenHandleCount = 0;
mnt->Device = &mnt->StrData[0];
memcpy( mnt->Device, Device, deviceLen+1 );
if(str) *str = '\0';
} while( str );
args[nArg] = 0; // NULL terminal
-
+
// Initialise Volume
mnt->RootNode = fs->InitDevice(Device, (const char **)args);
if(!mnt->RootNode) {
free(mnt);
+ parent_mnt->OpenHandleCount --;
return -2;
}
if(!gVFS_RootMount) gVFS_RootMount = mnt;
// Add to mount list
- Mutex_Acquire( &glVFS_MountList );
+ RWLock_AcquireWrite( &glVFS_MountList );
{
- tVFS_Mount *tmp;
mnt->Next = NULL;
if(gVFS_Mounts) {
+ tVFS_Mount *tmp;
for( tmp = gVFS_Mounts; tmp->Next; tmp = tmp->Next );
tmp->Next = mnt;
}
gVFS_Mounts = mnt;
}
}
- Mutex_Release( &glVFS_MountList );
+ RWLock_Release( &glVFS_MountList );
Log_Log("VFS", "Mounted '%s' to '%s' ('%s')", Device, MountPoint, Filesystem);
return 0;
}
+int VFS_Unmount(const char *Mountpoint)
+{
+ tVFS_Mount *mount, *prev = NULL;
+ RWLock_AcquireWrite( &glVFS_MountList );
+ for( mount = gVFS_Mounts; mount; prev = mount, mount = mount->Next )
+ {
+ if( strcmp(Mountpoint, mount->MountPoint) == 0 ) {
+ if( mount->OpenHandleCount ) {
+ LOG("Mountpoint busy");
+ RWLock_Release(&glVFS_MountList);
+ return EBUSY;
+ }
+ if(prev)
+ prev->Next = mount->Next;
+ else
+ gVFS_Mounts = mount->Next;
+ break;
+ }
+ }
+ RWLock_Release( &glVFS_MountList );
+ if( !mount ) {
+ LOG("Mountpoint not found");
+ return ENOENT;
+ }
+
+ Log_Warning("VFS", "TODO: Impliment unmount");
+
+ // Decrease the open handle count for the mountpoint filesystem.
+ tVFS_Mount *mpmnt;
+ tVFS_Node *mpnode = VFS_ParsePath(mount->MountPoint, NULL, &mpmnt);
+ if(mpnode)
+ {
+ mpmnt->OpenHandleCount -= 2; // -1 for _ParsePath here, -1 for in _Mount
+ }
+
+ mount->Filesystem->Unmount( mount->RootNode );
+ free(mount);
+
+ return EOK;
+}
+
+int VFS_UnmountAll(void)
+{
+ int nUnmounted = 0;
+ tVFS_Mount *mount, *prev = NULL, *next;
+
+ RWLock_AcquireWrite( &glVFS_MountList );
+ // If we've unmounted the final filesystem, all good
+ if( gVFS_Mounts == NULL) {
+ RWLock_Release( &glVFS_MountList );
+ return -1;
+ }
+
+ for( mount = gVFS_Mounts; mount; prev = mount, mount = next )
+ {
+ next = mount->Next;
+ // Can't unmount stuff with open handles
+ if( mount->OpenHandleCount > 0 ) {
+ LOG("%p (%s) has open handles (%i of them)",
+ mount, mount->MountPoint, mount->OpenHandleCount);
+ continue;
+ }
+
+ if(prev)
+ prev->Next = mount->Next;
+ else
+ gVFS_Mounts = mount->Next;
+
+ if( mount->Filesystem->Unmount ) {
+ mount->Filesystem->Unmount( mount->RootNode );
+ }
+ else {
+ Log_Error("VFS", "%s (%s) does not have an unmount method, not calling it",
+ mount->MountPoint, mount->Filesystem->Name);
+ }
+ free(mount);
+ mount = prev;
+ nUnmounted ++;
+ }
+ RWLock_Release( &glVFS_MountList );
+
+ return nUnmounted;
+}
+
/**
* \brief Gets a mount point given the identifier
*/
tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID)
{
tVFS_Mount *mnt;
+
+ RWLock_AcquireRead(&glVFS_MountList);
for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
{
if(mnt->Identifier == MountID)
- return mnt;
+ break;
}
- return NULL;
+ if(mnt)
+ mnt->OpenHandleCount ++;
+ RWLock_Release(&glVFS_MountList);
+ return mnt;
}
/**
// Format:
// <device>\t<location>\t<type>\t<options>\n
+ RWLock_AcquireRead( &glVFS_MountList );
for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
{
len += 4 + strlen(mnt->Device) + strlen(mnt->MountPoint)
+ strlen(mnt->Filesystem->Name) + strlen(mnt->Options);
}
+ RWLock_Release( &glVFS_MountList );
buf = malloc( len + 1 );
len = 0;
+ RWLock_AcquireRead( &glVFS_MountList );
for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
{
strcpy( &buf[len], mnt->Device );
len += strlen(mnt->Options);
buf[len++] = '\n';
}
+ RWLock_Release( &glVFS_MountList );
buf[len] = 0;
SysFS_UpdateFile( giVFS_MountFileID, buf, len );
// Find Mountpoint
longestMount = gVFS_RootMount;
+ RWLock_AcquireRead( &glVFS_MountList );
for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
{
// Quick Check
if(mnt->MountPointLen < longestMount->MountPointLen) continue;
// String Compare
cmp = strncmp(Path, mnt->MountPoint, mnt->MountPointLen);
+ // Not a match, continue
+ if(cmp != 0) continue;
#if OPEN_MOUNT_ROOT
// Fast Break - Request Mount Root
}
if(MountPoint)
*MountPoint = mnt;
+ RWLock_Release( &glVFS_MountList );
+ LOG("Mount %p root", mnt);
LEAVE('p', mnt->RootNode);
return mnt->RootNode;
}
#endif
- // Not a match, continue
- if(cmp != 0) continue;
longestMount = mnt;
}
+ longestMount->OpenHandleCount ++; // Increment assuimg it worked
+ RWLock_Release( &glVFS_MountList );
// Save to shorter variable
mnt = longestMount;
// EVIL: Goto :)
LOG("Symlink -> '%s', restart", Path);
+ mnt->OpenHandleCount --; // Not in this mountpoint
goto restart_parse;
}
*MountPoint = mnt;
}
+ // Leave the mointpoint's count increased
+
LEAVE('p', tmpNode);
return tmpNode;
free(*TruePath);
*TruePath = NULL;
}
+ // Open failed, so decrement the open handle count
+ mnt->OpenHandleCount --;
+
LEAVE('n');
return NULL;
}
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 ++;
+
LEAVE_RET('x', VFS_int_CreateHandle(node, h->Mount, Mode));
}
#endif
_CloseNode(h->Node);
-
+
+ h->Mount->OpenHandleCount --;
+
h->Node = NULL;
}