Merge branch 'master' of git://git.ucc.asn.au/tpg/acess2
[tpg/acess2.git] / KernelLand / Kernel / vfs / mount.c
1 /* 
2  * Acess Micro - VFS Server version 1
3  */
4 #define SANITY  1
5 #define DEBUG   0
6 #include <acess.h>
7 #include <vfs.h>
8 #include <vfs_int.h>
9 #include <fs_sysfs.h>
10
11 // === IMPORTS ===
12 extern int      giVFS_MountFileID;
13 extern char     *gsVFS_MountFile;
14
15 // === PROTOTYPES ===
16 #if 0
17  int    VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options);
18 #endif
19 void    VFS_int_Unmount(tVFS_Mount *Mount);
20 void    VFS_UpdateMountFile(void);
21
22 // === GLOBALS ===
23 tRWLock glVFS_MountList;
24 tVFS_Mount      *gVFS_Mounts;
25 tVFS_Mount      *gVFS_RootMount = NULL;
26 Uint32  giVFS_NextMountIdent = 1;
27
28 // === CODE ===
29 /**
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
36  * 
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
39  * driver's mount.
40  */
41 int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options)
42 {
43         tVFS_Mount      *mnt, *parent_mnt;
44         tVFS_Driver     *fs;
45          int    deviceLen = strlen(Device);
46          int    mountLen = strlen(MountPoint);
47          int    argLen = strlen(Options);
48         
49         // Get the filesystem
50         if( Filesystem && Filesystem[0] )
51         {
52                 fs = VFS_GetFSByName(Filesystem);
53                 if(!fs) {
54                         Log_Warning("VFS", "VFS_Mount - Unknown FS Type '%s'", Filesystem);
55                         return -ENOENT;
56                 }
57         }
58         else
59         {
60                 int fd = VFS_Open(Device, VFS_OPENFLAG_READ);
61                 if( fd == -1 ) {
62                         Log_Warning("VFS", "VFS_Mount - Unable to open '%s' for autodetect", Device);
63                         return -ENOENT;
64                 }
65                 
66                 tVFS_Driver     *bestfs = NULL;
67                  int    bestrank = 0, rank;
68                 for( fs = gVFS_Drivers; fs; fs = fs->Next )
69                 {
70                         if(!fs->Detect) continue ;
71                         rank = fs->Detect(fd);
72                         if(!rank)       continue ;
73                         if(!bestfs || rank > bestrank) {
74                                 bestfs = fs;
75                                 bestrank = rank;
76                         }
77                 }
78                 VFS_Close(fd);
79                 if( bestfs == NULL ) {
80                         Log_Warning("VFS", "VFS_Mount - Filesystem autodetection failed");
81                         return -1;
82                 }
83                 
84                 fs = bestfs;
85         }
86         
87         // Validate the mountpoint target
88         // - Only if / is mounted
89         if( gVFS_Mounts )
90         {
91                 tVFS_Node *mpnode = VFS_ParsePath(MountPoint, NULL, &parent_mnt);
92                 if( !mpnode ) {
93                         Log_Warning("VFS", "VFS_Mount - Mountpoint '%s' does not exist", MountPoint);
94                         return -1;
95                 }
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);
101                         return -1;
102                 }
103         }
104         
105         // Create mount information
106         mnt = malloc( sizeof(tVFS_Mount)+deviceLen+1+mountLen+1+argLen+1 );
107         if(!mnt) {
108                 ASSERT(parent_mnt->OpenHandleCount > 0);
109                 parent_mnt->OpenHandleCount --;
110                 return -2;
111         }
112
113         // HACK: Forces VFS_ParsePath to fall back on root  
114         if(mountLen == 1 && MountPoint[0] == '/')
115                 mnt->MountPointLen = 0;
116         else
117                 mnt->MountPointLen = mountLen;
118         
119         // Fill Structure
120         mnt->Filesystem = fs;
121         mnt->OpenHandleCount = 0;
122         
123         mnt->Device = &mnt->StrData[0];
124         memcpy( mnt->Device, Device, deviceLen+1 );
125         
126         mnt->MountPoint = &mnt->StrData[deviceLen+1];
127         memcpy( mnt->MountPoint, MountPoint, mountLen+1 );
128         
129         mnt->Options = &mnt->StrData[deviceLen+1+mountLen+1];
130         memcpy( mnt->Options, Options, argLen+1 );
131         
132         // Parse options string
133         char    *str = mnt->Options;
134          int    nArg = 0;
135         do {
136                 nArg ++;
137         } while( (str = strchr(str, ',')) );
138
139         char    *args[nArg + 1];
140         str = mnt->Options;
141         nArg = 0;
142         do {
143                 args[nArg++] = str;
144                 str = strchr(str, ',');
145                 if(str) *str = '\0';
146         } while( str );
147         args[nArg] = 0; // NULL terminal
148         
149         // Initialise Volume
150         mnt->RootNode = fs->InitDevice(Device, (const char **)args);
151         if(!mnt->RootNode) {
152                 free(mnt);
153                 ASSERT(parent_mnt->OpenHandleCount>0);
154                 parent_mnt->OpenHandleCount --;
155                 return -2;
156         }
157
158         // Repair the options string
159         while( nArg -- > 1 )
160                 args[nArg][-1] = ',';
161
162         mnt->Identifier = giVFS_NextMountIdent++;
163         #if 0
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 ++;
168         #endif
169         
170         // Set root
171         if(!gVFS_RootMount)     gVFS_RootMount = mnt;
172         
173         // Add to mount list
174         RWLock_AcquireWrite( &glVFS_MountList );
175         {
176                 mnt->Next = NULL;
177                 if(gVFS_Mounts) {
178                         tVFS_Mount      *tmp;
179                         for( tmp = gVFS_Mounts; tmp->Next; tmp = tmp->Next );
180                         tmp->Next = mnt;
181                 }
182                 else {
183                         gVFS_Mounts = mnt;
184                 }
185         }
186         RWLock_Release( &glVFS_MountList );
187         
188         Log_Log("VFS", "Mounted '%s' to '%s' ('%s')", Device, MountPoint, fs->Name);
189         
190         VFS_UpdateMountFile();
191         
192         return 0;
193 }
194
195 void VFS_int_Unmount(tVFS_Mount *Mount)
196 {
197         // Decrease the open handle count for the mountpoint filesystem.
198         if( Mount != gVFS_RootMount )
199         {
200                 tVFS_Mount      *mpmnt;
201                 for( mpmnt = gVFS_Mounts; mpmnt; mpmnt = mpmnt->Next )
202                 {
203                         if( strncmp(mpmnt->MountPoint, Mount->MountPoint, mpmnt->MountPointLen) != 0 )
204                                 continue ;
205                         if( Mount->MountPoint[ mpmnt->MountPointLen ] != '/' )
206                                 continue ;
207                         break;
208                 }
209                 if(mpmnt) {
210                         ASSERT(mpmnt->OpenHandleCount>0);
211                         mpmnt->OpenHandleCount --;
212                 }
213                 else {
214                         Log_Notice("VFS", "Mountpoint '%s' has no parent", Mount->MountPoint);
215                 }
216         }
217
218         if( Mount->Filesystem->Unmount )
219                 Mount->Filesystem->Unmount( Mount->RootNode );
220         LOG("%p (%s) unmounted", Mount, Mount->MountPoint);
221         free(Mount);
222 }
223
224 int VFS_Unmount(const char *Mountpoint)
225 {
226         tVFS_Mount      *mount, *prev = NULL;
227         RWLock_AcquireWrite( &glVFS_MountList );
228         for( mount = gVFS_Mounts; mount; prev = mount, mount = mount->Next )
229         {
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);
236                                 return EBUSY;
237                         }
238                         if(prev)
239                                 prev->Next = mount->Next;
240                         else
241                                 gVFS_Mounts = mount->Next;
242                         break;
243                 }
244         }
245         RWLock_Release( &glVFS_MountList );
246         if( !mount ) {
247                 LOG("Mountpoint not found");
248                 return ENOENT;
249         }
250
251         VFS_int_Unmount(mount);
252
253         VFS_UpdateMountFile();
254         
255         return EOK;
256 }
257
258 int VFS_UnmountAll(void)
259 {
260          int    nUnmounted = 0;
261         tVFS_Mount      *mount, *prev = NULL, *next;
262
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 );
267                 
268                 // Final unmount means VFS completely deinited
269                 VFS_Deinit();
270                 return -1;
271         }
272
273         for( mount = gVFS_Mounts; mount; prev = mount, mount = next )
274         {
275                 next = mount->Next;
276                 
277                 ASSERT(mount->OpenHandleCount >= 0);            
278
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);
283                         continue;
284                 }
285                 
286                 if(prev)
287                         prev->Next = mount->Next;
288                 else
289                         gVFS_Mounts = mount->Next;
290                 
291                 VFS_int_Unmount(mount);
292                 mount = prev;
293                 nUnmounted ++;
294         }
295         RWLock_Release( &glVFS_MountList );
296
297         VFS_UpdateMountFile();
298
299         return nUnmounted;
300 }
301
302 /**
303  * \brief Gets a mount point given the identifier
304  */
305 tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID)
306 {
307         tVFS_Mount      *mnt;
308         
309         RWLock_AcquireRead(&glVFS_MountList);
310         for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
311         {
312                 if(mnt->Identifier == MountID)
313                         break;
314         }
315         if(mnt)
316                 mnt->OpenHandleCount ++;
317         RWLock_Release(&glVFS_MountList);
318         return mnt;
319 }
320
321 /**
322  * \brief Updates the mount file buffer
323  * 
324  * Updates the ProcFS mounts file buffer to match the current mounts list.
325  */
326 void VFS_UpdateMountFile(void)
327 {
328          int    len = 0;
329         char    *buf;
330         tVFS_Mount      *mnt;
331         
332         // Format:
333         // <device>\t<location>\t<type>\t<options>\n
334         
335         RWLock_AcquireRead( &glVFS_MountList );
336         for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
337         {
338                 len += 4 + strlen(mnt->Device) + strlen(mnt->MountPoint)
339                         + strlen(mnt->Filesystem->Name) + strlen(mnt->Options);
340         }
341         RWLock_Release( &glVFS_MountList );
342         
343         buf = malloc( len + 1 );
344         len = 0;
345         RWLock_AcquireRead( &glVFS_MountList );
346         for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
347         {
348                 strcpy( &buf[len], mnt->Device );
349                 len += strlen(mnt->Device);
350                 buf[len++] = '\t';
351                 
352                 strcpy( &buf[len], mnt->MountPoint );
353                 len += strlen(mnt->MountPoint);
354                 buf[len++] = '\t';
355                 
356                 strcpy( &buf[len], mnt->Filesystem->Name );
357                 len += strlen(mnt->Filesystem->Name);
358                 buf[len++] = '\t';
359                 
360                 strcpy( &buf[len], mnt->Options );
361                 len += strlen(mnt->Options);
362                 buf[len++] = '\n';
363         }
364         RWLock_Release( &glVFS_MountList );
365         buf[len] = 0;
366         
367         SysFS_UpdateFile( giVFS_MountFileID, buf, len );
368         if( gsVFS_MountFile )   free( gsVFS_MountFile );
369         gsVFS_MountFile = buf;
370 }

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