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

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