Misc changes, fixing problems with long log strings
[tpg/acess2.git] / Kernel / vfs / open.c
1 /*
2  * Acess2 VFS
3  * - Open, Close and ChDir
4  */
5 #define DEBUG   0
6 #include <acess.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_PATH_SLASHES        256
14
15 // === IMPORTS ===
16 extern tVFS_Node        gVFS_MemRoot;
17 extern tVFS_Mount       *gVFS_RootMount;
18 extern int      VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode);
19
20 // === CODE ===
21 /**
22  * \fn char *VFS_GetAbsPath(const char *Path)
23  * \brief Create an absolute path from a relative one
24  */
25 char *VFS_GetAbsPath(const char *Path)
26 {
27         char    *ret;
28          int    pathLen = strlen(Path);
29         char    *pathComps[MAX_PATH_SLASHES];
30         char    *tmpStr;
31         int             iPos = 0;
32         int             iPos2 = 0;
33         const char      *chroot = CFGPTR(CFG_VFS_CHROOT);
34          int    chrootLen;
35         const char      *cwd = CFGPTR(CFG_VFS_CWD);
36          int    cwdLen;
37         
38         ENTER("sPath", Path);
39         
40         // Memory File
41         if(Path[0] == '$') {
42                 ret = malloc(strlen(Path)+1);
43                 if(!ret) {
44                         Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL");
45                         return NULL;
46                 }
47                 strcpy(ret, Path);
48                 LEAVE('p', ret);
49                 return ret;
50         }
51         
52         // - Fetch ChRoot
53         if( chroot == NULL ) {
54                 chroot = "";
55                 chrootLen = 0;
56         } else {
57                 chrootLen = strlen(chroot);
58         }
59         
60         // Check if the path is already absolute
61         if(Path[0] == '/') {
62                 ret = malloc(pathLen + 1);
63                 if(!ret) {
64                         Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL");
65                         return NULL;
66                 }
67                 strcpy(ret, Path);
68         } else {
69                 if(cwd == NULL) {
70                         cwd = "/";
71                         cwdLen = 1;
72                 }
73                 else {
74                         cwdLen = strlen(cwd);
75                 }
76                 // Prepend the current directory
77                 ret = malloc( cwdLen + 1 + pathLen + 1 );
78                 strcpy(ret, cwd);
79                 ret[cwdLen] = '/';
80                 strcpy(&ret[cwdLen+1], Path);
81                 //Log("ret = '%s'\n", ret);
82         }
83         
84         // Parse Path
85         pathComps[iPos++] = tmpStr = ret+1;
86         while(*tmpStr)
87         {
88                 if(*tmpStr++ == '/')
89                 {
90                         pathComps[iPos++] = tmpStr;
91                         if(iPos == MAX_PATH_SLASHES) {
92                                 LOG("Path '%s' has too many elements", Path);
93                                 free(ret);
94                                 LEAVE('n');
95                                 return NULL;
96                         }
97                 }
98         }
99         pathComps[iPos] = NULL;
100         
101         // Cleanup
102         iPos2 = iPos = 0;
103         while(pathComps[iPos])
104         {
105                 tmpStr = pathComps[iPos];
106                 // Always Increment iPos
107                 iPos++;
108                 // ..
109                 if(tmpStr[0] == '.' && tmpStr[1] == '.' && (tmpStr[2] == '/' || tmpStr[2] == '\0') )
110                 {
111                         if(iPos2 != 0)
112                                 iPos2 --;
113                         continue;
114                 }
115                 // .
116                 if(tmpStr[0] == '.' && (tmpStr[1] == '/' || tmpStr[1] == '\0') )
117                 {
118                         continue;
119                 }
120                 // Empty
121                 if(tmpStr[0] == '/' || tmpStr[0] == '\0')
122                 {
123                         continue;
124                 }
125                 
126                 // Set New Position
127                 pathComps[iPos2] = tmpStr;
128                 iPos2++;
129         }
130         pathComps[iPos2] = NULL;
131         
132         // Build New Path
133         iPos2 = 1;      iPos = 0;
134         ret[0] = '/';
135         while(pathComps[iPos])
136         {
137                 tmpStr = pathComps[iPos];
138                 while(*tmpStr && *tmpStr != '/')
139                 {
140                         ret[iPos2++] = *tmpStr;
141                         tmpStr++;
142                 }
143                 ret[iPos2++] = '/';
144                 iPos++;
145         }
146         if(iPos2 > 1)
147                 ret[iPos2-1] = 0;
148         else
149                 ret[iPos2] = 0;
150         
151         
152         // Prepend the chroot
153         tmpStr = malloc(chrootLen + strlen(ret) + 1);
154         strcpy( tmpStr, chroot );
155         strcpy( tmpStr+chrootLen, ret );
156         free(ret);
157         ret = tmpStr;
158         
159         LEAVE('s', ret);
160         //Log("VFS_GetAbsPath: RETURN '%s'", ret);
161         return ret;
162 }
163
164 /**
165  * \fn char *VFS_ParsePath(const char *Path, char **TruePath)
166  * \brief Parses a path, resolving sysmlinks and applying permissions
167  */
168 tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath)
169 {
170         tVFS_Mount      *mnt;
171         tVFS_Mount      *longestMount = gVFS_RootMount; // Root is first
172          int    cmp, retLength = 0;
173          int    ofs, nextSlash;
174         tVFS_Node       *curNode, *tmpNode;
175         char    *tmp;
176         
177         ENTER("sPath pTruePath", Path, TruePath);
178         
179         // Memory File
180         if(Path[0] == '$') {
181                 if(TruePath) {
182                         *TruePath = malloc(strlen(Path)+1);
183                         strcpy(*TruePath, Path);
184                 }
185                 curNode = gVFS_MemRoot.FindDir(&gVFS_MemRoot, Path);
186                 LEAVE('p', curNode);
187                 return curNode;
188         }
189         
190         // For root we always fast return
191         if(Path[0] == '/' && Path[1] == '\0') {
192                 if(TruePath) {
193                         *TruePath = malloc( gVFS_RootMount->MountPointLen+1 );
194                         strcpy(*TruePath, gVFS_RootMount->MountPoint);
195                 }
196                 LEAVE('p', gVFS_RootMount->RootNode);
197                 return gVFS_RootMount->RootNode;
198         }
199         
200         // Check if there is an`ything mounted
201         if(!gVFS_Mounts) {
202                 Warning("WTF! There's nothing mounted?");
203                 return NULL;
204         }
205         
206         // Find Mountpoint
207         for(mnt = gVFS_Mounts;
208                 mnt;
209                 mnt = mnt->Next)
210         {
211                 // Quick Check
212                 if( Path[mnt->MountPointLen] != '/' && Path[mnt->MountPointLen] != '\0')
213                         continue;
214                 // Length Check - If the length is smaller than the longest match sofar
215                 if(mnt->MountPointLen < longestMount->MountPointLen)    continue;
216                 // String Compare
217                 cmp = strcmp(Path, mnt->MountPoint);
218                 
219                 #if OPEN_MOUNT_ROOT
220                 // Fast Break - Request Mount Root
221                 if(cmp == 0) {
222                         if(TruePath) {
223                                 *TruePath = malloc( mnt->MountPointLen+1 );
224                                 strcpy(*TruePath, mnt->MountPoint);
225                         }
226                         LEAVE('p', mnt->RootNode);
227                         return mnt->RootNode;
228                 }
229                 #endif
230                 // Not a match, continue
231                 if(cmp != '/')  continue;
232                 longestMount = mnt;
233         }
234         
235         // Save to shorter variable
236         mnt = longestMount;
237         
238         LOG("mnt = {MountPoint:\"%s\"}", mnt->MountPoint);
239         
240         // Initialise String
241         if(TruePath)
242         {
243                 *TruePath = malloc( mnt->MountPointLen+1 );
244                 strcpy(*TruePath, mnt->MountPoint);
245                 retLength = mnt->MountPointLen;
246         }
247         
248         curNode = mnt->RootNode;
249         curNode->ReferenceCount ++;     
250         // Parse Path
251         ofs = mnt->MountPointLen+1;
252         for(; (nextSlash = strpos(&Path[ofs], '/')) != -1; ofs += nextSlash + 1)
253         {
254                 char    pathEle[nextSlash+1];
255                 
256                 // Empty String
257                 if(nextSlash == 0)      continue;
258                 
259                 memcpy(pathEle, &Path[ofs], nextSlash);
260                 pathEle[nextSlash] = 0;
261         
262                 // Check permissions on root of filesystem
263                 if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) {
264                         if(curNode->Close)      curNode->Close( curNode );
265                         if(TruePath) {
266                                 free(*TruePath);
267                                 *TruePath = NULL;
268                         }
269                         //Log("Permissions fail on '%s'", Path);
270                         LEAVE('n');
271                         return NULL;
272                 }
273                 
274                 // Check if the node has a FindDir method
275                 if( !curNode->FindDir )
276                 {
277                         if(curNode->Close)      curNode->Close(curNode);
278                         if(TruePath) {
279                                 free(*TruePath);
280                                 *TruePath = NULL;
281                         }
282                         //Log("FindDir fail on '%s'", Path);
283                         LEAVE('n');
284                         return NULL;
285                 }
286                 LOG("FindDir{=%p}(%p, '%s')", curNode->FindDir, curNode, pathEle);
287                 // Get Child Node
288                 tmpNode = curNode->FindDir(curNode, pathEle);
289                 LOG("tmpNode = %p", tmpNode);
290                 if(curNode->Close) {
291                         //LOG2("curNode->Close = %p", curNode->Close);
292                         curNode->Close(curNode);
293                 }
294                 curNode = tmpNode;
295                 
296                 // Error Check
297                 if(!curNode) {
298                         LOG("Node '%s' not found in dir '%s'", pathEle, Path);
299                         if(TruePath) {
300                                 free(*TruePath);
301                                 *TruePath = NULL;
302                         }
303                         //Log("Child fail on '%s' ('%s)", Path, pathEle);
304                         LEAVE('n');
305                         return NULL;
306                 }
307                 
308                 // Handle Symbolic Links
309                 if(curNode->Flags & VFS_FFLAG_SYMLINK) {
310                         if(TruePath) {
311                                 free(*TruePath);
312                                 *TruePath = NULL;
313                         }
314                         if(!curNode->Read) {
315                                 Warning("VFS_ParsePath - Read of node %p is NULL (%s)",
316                                         curNode, Path);
317                                 if(curNode->Close)      curNode->Close(curNode);
318                                 // No need to free *TruePath, see above
319                                 LEAVE('n');
320                                 return NULL;
321                         }
322                         
323                         tmp = malloc( curNode->Size + 1 );
324                         if(!tmp) {
325                                 Log_Warning("VFS", "VFS_ParsePath - Malloc failure");
326                                 // No need to free *TruePath, see above
327                                 LEAVE('n');
328                                 return NULL;
329                         }
330                         curNode->Read( curNode, 0, curNode->Size, tmp );
331                         tmp[ curNode->Size ] = '\0';
332                         
333                         // Parse Symlink Path
334                         curNode = VFS_ParsePath(tmp, TruePath);
335                         if(TruePath)
336                                 LOG("VFS", "*TruePath='%s'", *TruePath);
337                         
338                         // Error Check
339                         if(!curNode) {
340                                 Log_Debug("VFS", "Symlink fail '%s'", tmp);
341                                 free(tmp);      // Free temp string
342                                 if(TruePath)    free(TruePath);
343                                 LEAVE('n');
344                                 return NULL;
345                         }
346                         
347                         // Free temp link
348                         free(tmp);
349                         
350                         // Set Path Variable
351                         if(TruePath) {
352                                 retLength = strlen(*TruePath);
353                         }
354                         
355                         continue;
356                 }
357                 
358                 // Handle Non-Directories
359                 if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) )
360                 {
361                         Warning("VFS_ParsePath - File in directory context");
362                         if(TruePath)    free(*TruePath);
363                         LEAVE('n');
364                         return NULL;
365                 }
366                 
367                 // Check if path needs extending
368                 if(!TruePath)   continue;
369                 
370                 // Increase buffer space
371                 tmp = realloc( *TruePath, retLength + strlen(pathEle) + 1 + 1 );
372                 // Check if allocation succeeded
373                 if(!tmp) {
374                         Warning("VFS_ParsePath -  Unable to reallocate true path buffer");
375                         free(*TruePath);
376                         *TruePath = NULL;
377                         if(curNode->Close)      curNode->Close(curNode);
378                         LEAVE('n');
379                         return NULL;
380                 }
381                 *TruePath = tmp;
382                 // Append to path
383                 (*TruePath)[retLength] = '/';
384                 strcpy(*TruePath+retLength+1, pathEle);
385                 
386                 LOG("*TruePath = '%s'", *TruePath);
387                 
388                 // - Extend Path
389                 retLength += nextSlash + 1;
390         }
391         
392         if( !curNode->FindDir ) {
393                 if(curNode->Close)      curNode->Close(curNode);
394                 if(TruePath) {
395                         free(*TruePath);
396                         *TruePath = NULL;
397                 }
398                 Log("FindDir fail on '%s'", Path);
399                 LEAVE('n');
400                 return NULL;
401         }
402         
403         // Get last node
404         LOG("VFS_ParsePath: FindDir(%p, '%s')", curNode, &Path[ofs]);
405         tmpNode = curNode->FindDir(curNode, &Path[ofs]);
406         LOG("tmpNode = %p", tmpNode);
407         if(curNode->Close)      curNode->Close(curNode);
408         // Check if file was found
409         if(!tmpNode) {
410                 LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path);
411                 //Log("Child fail '%s' ('%s')", Path, &Path[ofs]);
412                 if(TruePath)    free(*TruePath);
413                 if(curNode->Close)      curNode->Close(curNode);
414                 LEAVE('n');
415                 return NULL;
416         }
417         
418         if(TruePath)
419         {
420                 // Increase buffer space
421                 tmp = realloc(*TruePath, retLength + strlen(&Path[ofs]) + 1 + 1);
422                 // Check if allocation succeeded
423                 if(!tmp) {
424                         Warning("VFS_ParsePath -  Unable to reallocate true path buffer");
425                         free(*TruePath);
426                         if(tmpNode->Close)      tmpNode->Close(curNode);
427                         LEAVE('n');
428                         return NULL;
429                 }
430                 *TruePath = tmp;
431                 // Append to path
432                 (*TruePath)[retLength] = '/';
433                 strcpy(*TruePath + retLength + 1, &Path[ofs]);
434                 // - Extend Path
435                 //retLength += strlen(tmpNode->Name) + 1;
436         }
437         
438         LEAVE('p', tmpNode);
439         return tmpNode;
440 }
441
442 /**
443  * \fn int VFS_Open(const char *Path, Uint Mode)
444  * \brief Open a file
445  */
446 int VFS_Open(const char *Path, Uint Mode)
447 {
448         tVFS_Node       *node;
449         char    *absPath;
450          int    i;
451         
452         ENTER("sPath xMode", Path, Mode);
453         
454         // Get absolute path
455         absPath = VFS_GetAbsPath(Path);
456         if(absPath == NULL) {
457                 Log_Warning("VFS", "VFS_Open: Path expansion failed '%s'", Path);
458                 return -1;
459         }
460         LOG("absPath = \"%s\"", absPath);
461         // Parse path and get mount point
462         node = VFS_ParsePath(absPath, NULL);
463         // Free generated path
464         free(absPath);
465         
466         if(!node) {
467                 LOG("Cannot find node");
468                 LEAVE('i', -1);
469                 return -1;
470         }
471         
472         // Check for symlinks
473         if( !(Mode & VFS_OPENFLAG_NOLINK) && (node->Flags & VFS_FFLAG_SYMLINK) )
474         {
475                 if( !node->Read ) {
476                         Warning("No read method on symlink");
477                         LEAVE('i', -1);
478                         return -1;
479                 }
480                 absPath = malloc(node->Size+1); // Allocate Buffer
481                 node->Read( node, 0, node->Size, absPath );     // Read Path
482                 
483                 absPath[ node->Size ] = '\0';   // End String
484                 if(node->Close) node->Close( node );    // Close old node
485                 node = VFS_ParsePath(absPath, NULL);    // Get new node
486                 free( absPath );        // Free allocated path
487         }
488         
489         if(!node) {
490                 LOG("Cannot find node");
491                 LEAVE('i', -1);
492                 return -1;
493         }
494         
495         i = 0;
496         i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0;
497         i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0;
498         i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0;
499         
500         LOG("i = 0b%b", i);
501         
502         // Permissions Check
503         if( !VFS_CheckACL(node, i) ) {
504                 if(node->Close) node->Close( node );
505                 Log("VFS_Open: Permissions Failed");
506                 LEAVE('i', -1);
507                 return -1;
508         }
509         
510         i = VFS_AllocHandle( !!(Mode & VFS_OPENFLAG_USER), node, Mode );
511         if( i >= 0 ) {
512                 LEAVE('x', i);
513                 return i;
514         }
515         
516         Log("VFS_Open: Out of handles");
517         LEAVE('i', -1);
518         return -1;
519 }
520
521
522 /**
523  * \brief Open a file from an open directory
524  */
525 int VFS_OpenChild(Uint *Errno, int FD, const char *Name, Uint Mode)
526 {
527         tVFS_Handle     *h;
528         tVFS_Node       *node;
529          int    i;
530         
531         // Get handle
532         h = VFS_GetHandle(FD);
533         if(h == NULL) {
534                 Log_Warning("VFS", "VFS_OpenChild - Invalid file handle 0x%x", FD);
535                 if(Errno)       *Errno = EINVAL;
536                 LEAVE('i', -1);
537                 return -1;
538         }
539         
540         // Check for directory
541         if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
542                 Log_Warning("VFS", "VFS_OpenChild - Passed handle is not a directory", FD);
543                 if(Errno)       *Errno = ENOTDIR;
544                 LEAVE('i', -1);
545                 return -1;
546         }
547         
548         // Find Child
549         node = h->Node->FindDir(h->Node, Name);
550         if(!node) {
551                 if(Errno)       *Errno = ENOENT;
552                 LEAVE('i', -1);
553                 return -1;
554         }
555         
556         i = 0;
557         i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0;
558         i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0;
559         i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0;
560         
561         // Permissions Check
562         if( !VFS_CheckACL(node, i) ) {
563                 if(node->Close) node->Close( node );
564                 Log_Notice("VFS", "VFS_OpenChild - Permissions Failed");
565                 if(Errno)       *Errno = EACCES;
566                 LEAVE('i', -1);
567                 return -1;
568         }
569         
570         i = VFS_AllocHandle( !!(Mode & VFS_OPENFLAG_USER), node, Mode );
571         if( i >= 0 ) {
572                 LEAVE('x', i);
573                 return i;
574         }
575         
576         Log_Error("VFS", "VFS_OpenChild - Out of handles");
577         if(Errno)       *Errno = ENFILE;
578         LEAVE('i', -1);
579         return -1;
580 }
581
582 /**
583  * \fn void VFS_Close(int FD)
584  * \brief Closes an open file handle
585  */
586 void VFS_Close(int FD)
587 {
588         tVFS_Handle     *h;
589         
590         // Get handle
591         h = VFS_GetHandle(FD);
592         if(h == NULL) {
593                 Log_Warning("VFS", "Invalid file handle passed to VFS_Close, 0x%x\n", FD);
594                 return;
595         }
596         
597         #if VALIDATE_VFS_FUNCTIPONS
598         if(h->Node->Close && !MM_GetPhysAddr(h->Node->Close)) {
599                 Log_Warning("VFS", "Node %p's ->Close method is invalid (%p)",
600                         h->Node, h->Node->Close);
601                 return ;
602         }
603         #endif
604         
605         if(h->Node->Close)
606                 h->Node->Close( h->Node );
607         
608         h->Node = NULL;
609 }
610
611 /**
612  * \brief Change current working directory
613  */
614 int VFS_ChDir(char *Dest)
615 {
616         char    *buf;
617          int    fd;
618         tVFS_Handle     *h;
619         
620         // Create Absolute
621         buf = VFS_GetAbsPath(Dest);
622         if(buf == NULL) {
623                 Log("VFS_ChDir: Path expansion failed");
624                 return -1;
625         }
626         
627         // Check if path exists
628         fd = VFS_Open(buf, VFS_OPENFLAG_EXEC);
629         if(fd == -1) {
630                 Log("VFS_ChDir: Path is invalid");
631                 return -1;
632         }
633         
634         // Get node so we can check for directory
635         h = VFS_GetHandle(fd);
636         if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
637                 Log("VFS_ChDir: Path is not a directory");
638                 VFS_Close(fd);
639                 return -1;
640         }
641         
642         // Close file
643         VFS_Close(fd);
644         
645         // Free old working directory
646         if( CFGPTR(CFG_VFS_CWD) )
647                 free( CFGPTR(CFG_VFS_CWD) );
648         // Set new
649         CFGPTR(CFG_VFS_CWD) = buf;
650         
651         Log("Updated CWD to '%s'", buf);
652         
653         return 1;
654 }
655
656 /**
657  * \fn int VFS_ChRoot(char *New)
658  * \brief Change current root directory
659  */
660 int VFS_ChRoot(char *New)
661 {
662         char    *buf;
663          int    fd;
664         tVFS_Handle     *h;
665         
666         if(New[0] == '/' && New[1] == '\0')
667                 return 1;       // What a useless thing to ask!
668         
669         // Create Absolute
670         buf = VFS_GetAbsPath(New);
671         if(buf == NULL) {
672                 LOG("Path expansion failed");
673                 return -1;
674         }
675         
676         // Check if path exists
677         fd = VFS_Open(buf, VFS_OPENFLAG_EXEC);
678         if(fd == -1) {
679                 LOG("Path is invalid");
680                 return -1;
681         }
682         
683         // Get node so we can check for directory
684         h = VFS_GetHandle(fd);
685         if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
686                 LOG("Path is not a directory");
687                 VFS_Close(fd);
688                 return -1;
689         }
690         
691         // Close file
692         VFS_Close(fd);
693         
694         // Free old working directory
695         if( CFGPTR(CFG_VFS_CHROOT) )
696                 free( CFGPTR(CFG_VFS_CHROOT) );
697         // Set new
698         CFGPTR(CFG_VFS_CHROOT) = buf;
699         
700         LOG("Updated Root to '%s'", buf);
701         
702         return 1;
703 }
704
705 // === EXPORTS ===
706 EXPORT(VFS_Open);
707 EXPORT(VFS_Close);

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