/*
* Acess Micro - VFS Server version 1
*/
+#define DEBUG 0
#include <acess.h>
#include <vfs.h>
#include <vfs_int.h>
#if 0
int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options);
#endif
+void VFS_int_Unmount(tVFS_Mount *Mount);
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);
int argLen = strlen(Options);
// Get the filesystem
- fs = VFS_GetFSByName(Filesystem);
- if(!fs) {
- Log_Warning("VFS", "VFS_Mount - Unknown FS Type '%s'", Filesystem);
- return -1;
+ if( Filesystem && Filesystem[0] )
+ {
+ fs = VFS_GetFSByName(Filesystem);
+ if(!fs) {
+ Log_Warning("VFS", "VFS_Mount - Unknown FS Type '%s'", Filesystem);
+ return -ENOENT;
+ }
+ }
+ else
+ {
+ int fd = VFS_Open(Device, VFS_OPENFLAG_READ);
+ if( fd == -1 ) {
+ Log_Warning("VFS", "VFS_Mount - Unable to open '%s' for autodetect", Device);
+ return -ENOENT;
+ }
+
+ tVFS_Driver *bestfs = NULL;
+ int bestrank = 0, rank;
+ for( fs = gVFS_Drivers; fs; fs = fs->Next )
+ {
+ if(!fs->Detect) continue ;
+ rank = fs->Detect(fd);
+ if(!rank) continue ;
+ if(!bestfs || rank > bestrank) {
+ bestfs = fs;
+ bestrank = rank;
+ }
+ }
+ VFS_Close(fd);
+ if( bestfs == NULL ) {
+ Log_Warning("VFS", "VFS_Mount - Filesystem autodetection failed");
+ return -1;
+ }
+
+ fs = bestfs;
+ }
+
+ // 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;
+ }
}
// Create mount information
mnt = malloc( sizeof(tVFS_Mount)+deviceLen+1+mountLen+1+argLen+1 );
if(!mnt) {
+ parent_mnt->OpenHandleCount --;
return -2;
}
-
+
// HACK: Forces VFS_ParsePath to fall back on root
if(mountLen == 1 && MountPoint[0] == '/')
mnt->MountPointLen = 0;
// Fill Structure
mnt->Filesystem = fs;
+ mnt->OpenHandleCount = 0;
mnt->Device = &mnt->StrData[0];
memcpy( mnt->Device, Device, deviceLen+1 );
mnt->Options = &mnt->StrData[deviceLen+1+mountLen+1];
memcpy( mnt->Options, Options, argLen+1 );
+ // Parse options string
+ char *str = mnt->Options;
+ int nArg = 0;
+ do {
+ nArg ++;
+ } while( (str = strchr(str, ',')) );
+
+ char *args[nArg + 1];
+ str = mnt->Options;
+ nArg = 0;
+ do {
+ args[nArg++] = str;
+ str = strchr(str, ',');
+ if(str) *str = '\0';
+ } while( str );
+ args[nArg] = 0; // NULL terminal
+
// Initialise Volume
- mnt->RootNode = fs->InitDevice(Device, NULL); //&ArgString);
+ mnt->RootNode = fs->InitDevice(Device, (const char **)args);
if(!mnt->RootNode) {
free(mnt);
+ parent_mnt->OpenHandleCount --;
return -2;
}
+ // Repair the options string
+ while( nArg -- > 1 )
+ args[nArg][-1] = ',';
+
mnt->Identifier = giVFS_NextMountIdent++;
#if 0
// Ensure identifiers don't repeat
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);
+ Log_Log("VFS", "Mounted '%s' to '%s' ('%s')", Device, MountPoint, fs->Name);
VFS_UpdateMountFile();
return 0;
}
+void VFS_int_Unmount(tVFS_Mount *Mount)
+{
+ // Decrease the open handle count for the mountpoint filesystem.
+ if( Mount != gVFS_RootMount )
+ {
+ tVFS_Mount *mpmnt;
+ for( mpmnt = gVFS_Mounts; mpmnt; mpmnt = mpmnt->Next )
+ {
+ if( strncmp(mpmnt->MountPoint, Mount->MountPoint, mpmnt->MountPointLen) != 0 )
+ continue ;
+ if( Mount->MountPoint[ mpmnt->MountPointLen ] != '/' )
+ continue ;
+ break;
+ }
+ if(mpmnt) {
+ mpmnt->OpenHandleCount -= 1;
+ }
+ else {
+ Log_Notice("VFS", "Mountpoint '%s' has no parent", Mount->MountPoint);
+ }
+ }
+
+ if( Mount->Filesystem->Unmount )
+ Mount->Filesystem->Unmount( Mount->RootNode );
+ LOG("%p (%s) unmounted", Mount, Mount->MountPoint);
+ free(Mount);
+}
+
+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;
+ }
+
+ VFS_int_Unmount(mount);
+
+ VFS_UpdateMountFile();
+
+ 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;
+
+ VFS_int_Unmount(mount);
+ mount = prev;
+ nUnmounted ++;
+ }
+ RWLock_Release( &glVFS_MountList );
+
+ VFS_UpdateMountFile();
+
+ 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 );