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

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