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

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