f025e0de0d9044d47b971f7ddbcaed5dcbc72ce8
[tpg/acess2.git] / Kernel / vfs / open.c
1 /*
2  * AcessMicro VFS
3  * - Open, Close and ChDir
4  */
5 #include <common.h>
6 #include "vfs.h"
7 #include "vfs_int.h"
8 #include "vfs_ext.h"
9
10 #define DEBUG   0
11
12 #if DEBUG
13 #else
14 # undef ENTER
15 # undef LOG
16 # undef LEAVE
17 # define ENTER(...)
18 # define LOG(...)
19 # define LEAVE(...)
20 #endif
21
22 // === CONSTANTS ===
23 #define OPEN_MOUNT_ROOT 1
24 #define MAX_KERNEL_FILES        128
25
26 // === IMPORTS ===
27 extern tVFS_Node        gVFS_MemRoot;
28 extern tVFS_Mount       *gRootMount;
29
30 // === GLOBALS ===
31 tVFS_Handle     *gaUserHandles = (void*)MM_PPD_VFS;
32 tVFS_Handle     *gaKernelHandles = (void*)MM_KERNEL_VFS;
33
34 // === CODE ===
35 /**
36  * \fn char *VFS_GetAbsPath(char *Path)
37  * \brief Create an absolute path from a relative one
38  */
39 char *VFS_GetAbsPath(char *Path)
40 {
41         char    *ret;
42          int    pathLen = strlen(Path);
43          int    read, write;
44          int    pos, slashNum=0, baseLen;
45         Uint    slashOffsets[256];
46         char    *cwd = CFGPTR(CFG_VFS_CWD);
47          int    cwdLen;
48         
49         ENTER("sPath", Path);
50         
51         // Memory File
52         if(Path[0] == '$') {
53                 ret = malloc(strlen(Path)+1);
54                 strcpy(ret, Path);
55                 LEAVE('p', ret);
56                 return ret;
57         }
58         
59         // Check if the path is already absolute
60         if(Path[0] == '/') {
61                 ret = malloc(pathLen + 1);
62                 strcpy(ret, Path);
63                 baseLen = 1;
64         } else {
65                 cwdLen = strlen(cwd);
66                 // Prepend the current directory
67                 ret = malloc(cwdLen+pathLen+1);
68                 strcpy(ret, cwd);
69                 strcpy(&ret[cwdLen], Path);
70         
71                 // Pre-fill the slash positions
72                 pos = 0;
73                 while( (pos = strpos( &ret[pos+1], '/' )) != -1 )
74                         slashOffsets[slashNum++] = pos;
75                         
76                 baseLen = cwdLen;
77         }
78         
79         // Remove . and ..
80         read = write = baseLen; // Cwd has already been parsed
81         for(; read < baseLen+pathLen; read = pos+1)
82         {
83                 pos = strpos( &ret[read], '/' );
84                 // If we are in the last section, force a break at the end of the itteration
85                 if(pos == -1)   pos = baseLen+pathLen;
86                 else    pos += read;    // Else, Adjust to absolute
87                 
88                 // Check Length
89                 if(pos - read <= 2)
90                 {
91                         // Current Dir "."
92                         if(strncmp(&ret[read], ".", pos-read) == 0)     continue;
93                         // Parent ".."
94                         if(strncmp(&ret[read], "..", pos-read) == 0)
95                         {
96                                 // If there is no higher, silently ignore
97                                 if(!slashNum)   continue;
98                                 // Reverse write pointer
99                                 write = slashOffsets[ slashNum-- ];
100                                 continue;
101                         }
102                 }
103                 
104                 
105                 // Only copy if the positions differ
106                 if(read != write) {
107                         memcpy( &ret[write], &ret[read], pos-read+1 );
108                 }
109                 write = pos+1;
110                 if(slashNum < 256)
111                         slashOffsets[ slashNum++ ] = pos;
112                 else {
113                         LOG("Path '%s' has too many elements", Path);
114                         free(ret);
115                         LEAVE('n');
116                         return NULL;
117                 }
118         }
119         
120         // `ret` should now be the absolute path
121         LEAVE('s', ret);
122         return ret;
123 }
124
125 /**
126  * \fn char *VFS_ParsePath(char *Path, char **TruePath)
127  * \brief Parses a path, resolving sysmlinks and applying permissions
128  */
129 tVFS_Node *VFS_ParsePath(char *Path, char **TruePath)
130 {
131         tVFS_Mount      *mnt;
132         tVFS_Mount      *longestMount = gRootMount;     // Root is first
133          int    cmp, retLength = 0;
134          int    ofs, nextSlash;
135         tVFS_Node       *curNode, *tmpNode;
136         char    *tmp;
137         
138         ENTER("sPath pTruePath", Path, TruePath);
139         
140         // Memory File
141         if(Path[0] == '$') {
142                 if(TruePath) {
143                         *TruePath = malloc(strlen(Path)+1);
144                         strcpy(*TruePath, Path);
145                 }
146                 curNode = gVFS_MemRoot.FindDir(&gVFS_MemRoot, Path);
147                 LEAVE('p', curNode);
148                 return curNode;
149         }
150         // For root we always fast return
151         
152         if(Path[0] == '/' && Path[1] == '\0') {
153                 if(TruePath) {
154                         *TruePath = malloc( gRootMount->MountPointLen+1 );
155                         strcpy(*TruePath, gRootMount->MountPoint);
156                 }
157                 LEAVE('p', gRootMount->RootNode);
158                 return gRootMount->RootNode;
159         }
160         
161         // Check if there is anything mounted
162         if(!gMounts)    return NULL;
163         
164         // Find Mountpoint
165         for(mnt = gMounts;
166                 mnt;
167                 mnt = mnt->Next)
168         {
169                 // Quick Check
170                 if( Path[mnt->MountPointLen] != '/' && Path[mnt->MountPointLen] != '\0')
171                         continue;
172                 // Length Check - If the length is smaller than the longest match sofar
173                 if(mnt->MountPointLen < longestMount->MountPointLen)    continue;
174                 // String Compare
175                 cmp = strcmp(Path, mnt->MountPoint);
176                 
177                 #if OPEN_MOUNT_ROOT
178                 // Fast Break - Request Mount Root
179                 if(cmp == 0) {
180                         if(TruePath) {
181                                 *TruePath = malloc( mnt->MountPointLen+1 );
182                                 strcpy(*TruePath, mnt->MountPoint);
183                         }
184                         LEAVE('p', mnt->RootNode);
185                         return mnt->RootNode;
186                 }
187                 #endif
188                 // Not a match, continue
189                 if(cmp != '/')  continue;
190                 longestMount = mnt;
191         }
192         
193         // Sanity Check
194         /*if(!longestMount) {
195                 Log("VFS_GetTruePath - ERROR: No Root Node\n");
196                 return NULL;
197         }*/
198         
199         // Save to shorter variable
200         mnt = longestMount;
201         
202         LOG("mnt = {MountPoint:\"%s\"}", mnt->MountPoint);
203         
204         // Initialise String
205         if(TruePath)
206         {
207                 *TruePath = malloc( mnt->MountPointLen+1 );
208                 strcpy(*TruePath, mnt->MountPoint);
209                 retLength = mnt->MountPointLen;
210         }
211         
212         curNode = mnt->RootNode;
213         curNode->ReferenceCount ++;     
214         // Parse Path
215         ofs = mnt->MountPointLen+1;
216         for(; (nextSlash = strpos(&Path[ofs], '/')) != -1; Path[nextSlash]='/',ofs = nextSlash + 1)
217         {
218                 nextSlash += ofs;
219                 Path[nextSlash] = '\0';
220         
221                 // Check for empty string
222                 if( Path[ofs] == '\0' ) continue;
223         
224                 // Check permissions on root of filesystem
225                 if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) {
226                         curNode->Close( curNode );
227                         if(TruePath)    free(*TruePath);
228                         LEAVE('n');
229                         return NULL;
230                 }
231                 
232                 // Check if the node has a FindDir method
233                 if(!curNode->FindDir) {
234                         if(curNode->Close)      curNode->Close(curNode);
235                         if(TruePath)    free(*TruePath);
236                         Path[nextSlash] = '/';
237                         LEAVE('n');
238                         return NULL;
239                 }
240                 LOG("FindDir(%p, '%s')", curNode, &Path[ofs]);
241                 // Get Child Node
242                 tmpNode = curNode->FindDir(curNode, &Path[ofs]);
243                 LOG("tmpNode = %p", tmpNode);
244                 if(curNode->Close)
245                         curNode->Close(curNode);
246                 curNode = tmpNode;
247                 
248                 // Error Check
249                 if(!curNode) {
250                         LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path);
251                         if(TruePath)
252                                 free(*TruePath);
253                         Path[nextSlash] = '/';
254                         LEAVE('n');
255                         return NULL;
256                 }
257                 
258                 // Handle Symbolic Links
259                 if(curNode->Flags & VFS_FFLAG_SYMLINK) {
260                         if(TruePath)
261                                 free(*TruePath);
262                         tmp = malloc( curNode->Size + 1 );
263                         curNode->Read( curNode, 0, curNode->Size, tmp );
264                         tmp[ curNode->Size ] = '\0';
265                         
266                         // Parse Symlink Path
267                         curNode = VFS_ParsePath(tmp, TruePath);
268                         free(tmp);      // Free temp string
269                         
270                         // Error Check
271                         if(!curNode) {
272                                 LEAVE('n');
273                                 return NULL;
274                         }
275                         
276                         // Set Path Variable
277                         if(TruePath) {
278                                 *TruePath = tmp;
279                                 retLength = strlen(tmp);
280                         }
281                         
282                         continue;
283                 }
284                 
285                 // Handle Non-Directories
286                 if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) )
287                 {
288                         Warning("VFS_ParsePath - File in directory context");
289                         if(TruePath)    free(*TruePath);
290                         LEAVE('n');
291                         return NULL;
292                 }
293                 
294                 // Check if path needs extending
295                 if(!TruePath)   continue;
296                 
297                 // Increase buffer space
298                 tmp = realloc( *TruePath, retLength + strlen(&Path[ofs]) + 1 + 1 );
299                 // Check if allocation succeeded
300                 if(!tmp) {
301                         Warning("VFS_ParsePath -  Unable to reallocate true path buffer");
302                         free(*TruePath);
303                         if(curNode->Close)      curNode->Close(curNode);
304                         LEAVE('n');
305                         return NULL;
306                 }
307                 *TruePath = tmp;
308                 // Append to path
309                 (*TruePath)[retLength] = '/';
310                 strcpy(*TruePath+retLength+1, &Path[ofs]);
311                 // - Extend Path
312                 retLength += strlen(&Path[ofs])+1;
313         }
314         
315         // Get last node
316         LOG("VFS_ParsePath: FindDir(%p, '%s')", curNode, &Path[ofs]);
317         tmpNode = curNode->FindDir(curNode, &Path[ofs]);
318         LOG("tmpNode = %p", tmpNode);
319         if(curNode->Close)      curNode->Close(curNode);
320         // Check if file was found
321         if(!tmpNode) {
322                 LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path);
323                 if(TruePath)    free(*TruePath);
324                 if(curNode->Close)      curNode->Close(curNode);
325                 LEAVE('n');
326                 return NULL;
327         }
328         
329         if(TruePath)
330         {
331                 // Increase buffer space
332                 tmp = realloc(*TruePath, retLength + strlen(&Path[ofs]) + 1 + 1);
333                 // Check if allocation succeeded
334                 if(!tmp) {
335                         Warning("VFS_ParsePath -  Unable to reallocate true path buffer");
336                         free(*TruePath);
337                         if(tmpNode->Close)      tmpNode->Close(curNode);
338                         LEAVE('n');
339                         return NULL;
340                 }
341                 *TruePath = tmp;
342                 // Append to path
343                 (*TruePath)[retLength] = '/';
344                 strcpy(*TruePath + retLength + 1, &Path[ofs]);
345                 // - Extend Path
346                 //retLength += strlen(tmpNode->Name) + 1;
347         }
348         
349         LEAVE('p', tmpNode);
350         return tmpNode;
351 }
352
353 /**
354  * \fn int VFS_Open(char *Path, Uint Mode)
355  * \brief Open a file
356  */
357 int VFS_Open(char *Path, Uint Mode)
358 {
359         tVFS_Node       *node;
360         char    *absPath;
361          int    i;
362         
363         ENTER("sPath xMode", Path, Mode);
364         
365         // Get absolute path
366         absPath = VFS_GetAbsPath(Path);
367         LOG("absPath = \"%s\"", absPath);
368         // Parse path and get mount point
369         node = VFS_ParsePath(absPath, NULL);
370         // Free generated path
371         free(absPath);
372         
373         if(!node) {
374                 LOG("Cannot find node");
375                 LEAVE('i', -1);
376                 return -1;
377         }
378         
379         // Check for symlinks
380         if( !(Mode & VFS_OPENFLAG_NOLINK) && (node->Flags & VFS_FFLAG_SYMLINK) )
381         {
382                 if( !node->Read ) {
383                         LOG("No read method on symlink");
384                         LEAVE('i', -1);
385                         return -1;
386                 }
387                 absPath = malloc(node->Size+1); // Allocate Buffer
388                 node->Read( node, 0, node->Size, absPath );     // Read Path
389                 
390                 absPath[ node->Size ] = '\0';   // End String
391                 if(node->Close) node->Close( node );    // Close old node
392                 node = VFS_ParsePath(absPath, NULL);    // Get new node
393                 free( absPath );        // Free allocated path
394         }
395         
396         if(!node) {
397                 LEAVE('i', -1);
398                 return -1;
399         }
400         
401         i = 0;
402         i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0;
403         i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0;
404         i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0;
405         
406         LOG("i = 0b%b", i);
407         
408         // Permissions Check
409         if( !VFS_CheckACL(node, i) ) {
410                 node->Close( node );
411                 LEAVE('i', -1);
412                 return -1;
413         }
414         
415         // Check for a user open
416         if(Mode & VFS_OPENFLAG_USER)
417         {
418                 // Allocate Buffer
419                 if( MM_GetPhysAddr( (Uint)gaUserHandles ) == 0 )
420                 {
421                         Uint    addr, size;
422                         size = CFGINT(CFG_VFS_MAXFILES) * sizeof(tVFS_Handle);
423                         for(addr = 0; addr < size; addr += 0x1000)
424                                 MM_Allocate( (Uint)gaUserHandles + addr );
425                         memset( gaUserHandles, 0, size );
426                 }
427                 // Get a handle
428                 for(i=0;i<CFGINT(CFG_VFS_MAXFILES);i++)
429                 {
430                         if(gaUserHandles[i].Node)       continue;
431                         gaUserHandles[i].Node = node;
432                         gaUserHandles[i].Position = 0;
433                         gaUserHandles[i].Mode = Mode;
434                         LEAVE('i', i);
435                         return i;
436                 }
437         }
438         else
439         {
440                 // Allocate space if not already
441                 if( MM_GetPhysAddr( (Uint)gaKernelHandles ) == 0 )
442                 {
443                         Uint    addr, size;
444                         size = MAX_KERNEL_FILES * sizeof(tVFS_Handle);
445                         for(addr = 0; addr < size; addr += 0x1000)
446                                 MM_Allocate( (Uint)gaKernelHandles + addr );
447                         memset( gaKernelHandles, 0, size );
448                 }
449                 // Get a handle
450                 for(i=0;i<MAX_KERNEL_FILES;i++)
451                 {
452                         if(gaKernelHandles[i].Node)     continue;
453                         gaKernelHandles[i].Node = node;
454                         gaKernelHandles[i].Position = 0;
455                         gaKernelHandles[i].Mode = Mode;
456                         LEAVE('x', i|VFS_KERNEL_FLAG);
457                         return i|VFS_KERNEL_FLAG;
458                 }
459         }
460         
461         LEAVE('i', -1);
462         return -1;
463 }
464
465 /**
466  * \fn void VFS_Close(int FD)
467  * \brief Closes an open file handle
468  */
469 void VFS_Close(int FD)
470 {
471         tVFS_Handle     *h;
472         
473         // Get handle
474         h = VFS_GetHandle(FD);
475         if(h == NULL) {
476                 Warning("Invalid file handle passed to VFS_Close, 0x%x\n", FD);
477                 return;
478         }
479         
480         if(h->Node->Close)
481                 h->Node->Close( h->Node );
482         
483         h->Node = NULL;
484 }
485
486 /**
487  * \fn tVFS_Handle *VFS_GetHandle(int FD)
488  * \brief Gets a pointer to the handle information structure
489  */
490 tVFS_Handle *VFS_GetHandle(int FD)
491 {
492         if(FD < 0)      return NULL;
493         
494         if(FD & VFS_KERNEL_FLAG) {
495                 FD &= (VFS_KERNEL_FLAG - 1);
496                 if(FD >= MAX_KERNEL_FILES)      return NULL;
497                 return &gaKernelHandles[ FD ];
498         } else {
499                 if(FD >= CFGINT(CFG_VFS_MAXFILES))      return NULL;
500                 return &gaUserHandles[ FD ];
501         }
502 }

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