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

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