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

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