2 * Acess Micro - VFS Server version 1
12 extern int giVFS_MountFileID;
13 extern char *gsVFS_MountFile;
17 int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options);
19 void VFS_int_Unmount(tVFS_Mount *Mount);
20 void VFS_UpdateMountFile(void);
23 tRWLock glVFS_MountList;
24 tVFS_Mount *gVFS_Mounts;
25 tVFS_Mount *gVFS_RootMount = NULL;
26 Uint32 giVFS_NextMountIdent = 1;
30 * \brief Mount a device
31 * \param Device Device string to mount
32 * \param MountPoint Destination for the mount
33 * \param Filesystem Filesystem to use for the mount
34 * \param Options Options to be passed to the filesystem
35 * \return -1 on Invalid FS, -2 on No Mem, 0 on success
37 * Mounts the filesystem on \a Device at \a MountPoint using the driver
38 * \a Filesystem. The options in the string \a Options is passed to the
41 int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options)
43 tVFS_Mount *mnt, *parent_mnt;
45 int deviceLen = strlen(Device);
46 int mountLen = strlen(MountPoint);
47 int argLen = strlen(Options);
50 if( Filesystem && Filesystem[0] )
52 fs = VFS_GetFSByName(Filesystem);
54 Log_Warning("VFS", "VFS_Mount - Unknown FS Type '%s'", Filesystem);
60 int fd = VFS_Open(Device, VFS_OPENFLAG_READ);
62 Log_Warning("VFS", "VFS_Mount - Unable to open '%s' for autodetect", Device);
66 tVFS_Driver *bestfs = NULL;
67 int bestrank = 0, rank;
68 for( fs = gVFS_Drivers; fs; fs = fs->Next )
70 if(!fs->Detect) continue ;
71 rank = fs->Detect(fd);
73 if(!bestfs || rank > bestrank) {
79 if( bestfs == NULL ) {
80 Log_Warning("VFS", "VFS_Mount - Filesystem autodetection failed");
87 // Validate the mountpoint target
88 // - Only if / is mounted
91 tVFS_Node *mpnode = VFS_ParsePath(MountPoint, NULL, &parent_mnt);
93 Log_Warning("VFS", "VFS_Mount - Mountpoint '%s' does not exist", MountPoint);
96 if( mpnode->Type->Close )
97 mpnode->Type->Close(mpnode);
98 if( parent_mnt->RootNode == mpnode ) {
99 Log_Warning("VFS", "VFS_Mount - Attempt to mount over '%s' (%s)",
100 MountPoint, parent_mnt->MountPoint);
105 // Create mount information
106 mnt = malloc( sizeof(tVFS_Mount)+deviceLen+1+mountLen+1+argLen+1 );
108 ASSERT(parent_mnt->OpenHandleCount > 0);
109 parent_mnt->OpenHandleCount --;
113 // HACK: Forces VFS_ParsePath to fall back on root
114 if(mountLen == 1 && MountPoint[0] == '/')
115 mnt->MountPointLen = 0;
117 mnt->MountPointLen = mountLen;
120 mnt->Filesystem = fs;
121 mnt->OpenHandleCount = 0;
123 mnt->Device = &mnt->StrData[0];
124 memcpy( mnt->Device, Device, deviceLen+1 );
126 mnt->MountPoint = &mnt->StrData[deviceLen+1];
127 memcpy( mnt->MountPoint, MountPoint, mountLen+1 );
129 mnt->Options = &mnt->StrData[deviceLen+1+mountLen+1];
130 memcpy( mnt->Options, Options, argLen+1 );
132 // Parse options string
133 char *str = mnt->Options;
137 } while( (str = strchr(str, ',')) );
139 char *args[nArg + 1];
144 str = strchr(str, ',');
147 args[nArg] = 0; // NULL terminal
150 mnt->RootNode = fs->InitDevice(Device, (const char **)args);
153 ASSERT(parent_mnt->OpenHandleCount>0);
154 parent_mnt->OpenHandleCount --;
158 // Repair the options string
160 args[nArg][-1] = ',';
162 mnt->Identifier = giVFS_NextMountIdent++;
164 // Ensure identifiers don't repeat
165 // - Only a problem if there have been 4 billion mounts
166 while( giVFS_NextMountIdent == 0 || VFS_GetMountByIdent(giVFS_NextMountIdent) )
167 giVFS_NextMountIdent ++;
171 if(!gVFS_RootMount) gVFS_RootMount = mnt;
174 RWLock_AcquireWrite( &glVFS_MountList );
179 for( tmp = gVFS_Mounts; tmp->Next; tmp = tmp->Next );
186 RWLock_Release( &glVFS_MountList );
188 Log_Log("VFS", "Mounted '%s' to '%s' ('%s')", Device, MountPoint, fs->Name);
190 VFS_UpdateMountFile();
195 void VFS_int_Unmount(tVFS_Mount *Mount)
197 // Decrease the open handle count for the mountpoint filesystem.
198 if( Mount != gVFS_RootMount )
201 for( mpmnt = gVFS_Mounts; mpmnt; mpmnt = mpmnt->Next )
203 if( strncmp(mpmnt->MountPoint, Mount->MountPoint, mpmnt->MountPointLen) != 0 )
205 if( Mount->MountPoint[ mpmnt->MountPointLen ] != '/' )
210 ASSERT(mpmnt->OpenHandleCount>0);
211 mpmnt->OpenHandleCount --;
214 Log_Notice("VFS", "Mountpoint '%s' has no parent", Mount->MountPoint);
218 if( Mount->Filesystem->Unmount )
219 Mount->Filesystem->Unmount( Mount->RootNode );
220 LOG("%p (%s) unmounted", Mount, Mount->MountPoint);
224 int VFS_Unmount(const char *Mountpoint)
226 tVFS_Mount *mount, *prev = NULL;
227 RWLock_AcquireWrite( &glVFS_MountList );
228 for( mount = gVFS_Mounts; mount; prev = mount, mount = mount->Next )
230 if( strcmp(Mountpoint, mount->MountPoint) == 0 ) {
231 if( mount->OpenHandleCount ) {
232 LOG("Mountpoint busy");
233 RWLock_Release(&glVFS_MountList);
234 Log_Log("VFS", "Unmount of '%s' deferred, still busy (%i open handles)",
235 Mountpoint, mount->OpenHandleCount);
239 prev->Next = mount->Next;
241 gVFS_Mounts = mount->Next;
245 RWLock_Release( &glVFS_MountList );
247 LOG("Mountpoint not found");
251 VFS_int_Unmount(mount);
253 VFS_UpdateMountFile();
258 int VFS_UnmountAll(void)
261 tVFS_Mount *mount, *prev = NULL, *next;
263 RWLock_AcquireWrite( &glVFS_MountList );
264 // If we've unmounted the final filesystem, all good
265 if( gVFS_Mounts == NULL) {
266 RWLock_Release( &glVFS_MountList );
268 // Final unmount means VFS completely deinited
273 for( mount = gVFS_Mounts; mount; prev = mount, mount = next )
277 ASSERT(mount->OpenHandleCount >= 0);
279 // Can't unmount stuff with open handles
280 if( mount->OpenHandleCount > 0 ) {
281 LOG("%p (%s) has open handles (%i of them)",
282 mount, mount->MountPoint, mount->OpenHandleCount);
287 prev->Next = mount->Next;
289 gVFS_Mounts = mount->Next;
291 VFS_int_Unmount(mount);
295 RWLock_Release( &glVFS_MountList );
297 VFS_UpdateMountFile();
303 * \brief Gets a mount point given the identifier
305 tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID)
309 RWLock_AcquireRead(&glVFS_MountList);
310 for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
312 if(mnt->Identifier == MountID)
316 mnt->OpenHandleCount ++;
317 RWLock_Release(&glVFS_MountList);
322 * \brief Updates the mount file buffer
324 * Updates the ProcFS mounts file buffer to match the current mounts list.
326 void VFS_UpdateMountFile(void)
333 // <device>\t<location>\t<type>\t<options>\n
335 RWLock_AcquireRead( &glVFS_MountList );
336 for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
338 len += 4 + strlen(mnt->Device) + strlen(mnt->MountPoint)
339 + strlen(mnt->Filesystem->Name) + strlen(mnt->Options);
341 RWLock_Release( &glVFS_MountList );
343 buf = malloc( len + 1 );
345 RWLock_AcquireRead( &glVFS_MountList );
346 for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
348 strcpy( &buf[len], mnt->Device );
349 len += strlen(mnt->Device);
352 strcpy( &buf[len], mnt->MountPoint );
353 len += strlen(mnt->MountPoint);
356 strcpy( &buf[len], mnt->Filesystem->Name );
357 len += strlen(mnt->Filesystem->Name);
360 strcpy( &buf[len], mnt->Options );
361 len += strlen(mnt->Options);
364 RWLock_Release( &glVFS_MountList );
367 SysFS_UpdateFile( giVFS_MountFileID, buf, len );
368 if( gsVFS_MountFile ) free( gsVFS_MountFile );
369 gsVFS_MountFile = buf;