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

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