VFS - Fixed unmount issues and added filesystem autodetection
[tpg/acess2.git] / KernelLand / Kernel / vfs / mount.c
index 1773218..5ac67a5 100644 (file)
@@ -1,6 +1,7 @@
 /* 
  * Acess Micro - VFS Server version 1
  */
+#define DEBUG  0
 #include <acess.h>
 #include <vfs.h>
 #include <vfs_int.h>
@@ -14,10 +15,11 @@ extern char *gsVFS_MountFile;
 #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;
@@ -37,25 +39,75 @@ 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, 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;
@@ -64,6 +116,7 @@ int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem
        
        // Fill Structure
        mnt->Filesystem = fs;
+       mnt->OpenHandleCount = 0;
        
        mnt->Device = &mnt->StrData[0];
        memcpy( mnt->Device, Device, deviceLen+1 );
@@ -74,13 +127,35 @@ int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem
        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
@@ -93,11 +168,11 @@ int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem
        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;
                }
@@ -105,27 +180,130 @@ int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem
                        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;
 }
 
 /**
@@ -142,14 +320,17 @@ void VFS_UpdateMountFile(void)
        // 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 );
@@ -168,6 +349,7 @@ void VFS_UpdateMountFile(void)
                len += strlen(mnt->Options);
                buf[len++] = '\n';
        }
+       RWLock_Release( &glVFS_MountList );
        buf[len] = 0;
        
        SysFS_UpdateFile( giVFS_MountFileID, buf, len );

UCC git Repository :: git.ucc.asn.au