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

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