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

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