Merge branch 'master' of git://git.ucc.asn.au/tpg/acess2
[tpg/acess2.git] / KernelLand / Kernel / vfs / open.c
1 /*
2  * Acess2 VFS
3  * - Open, Close and ChDir
4  */
5 #define SANITY  1
6 #define DEBUG   0
7 #include <acess.h>
8 #include "vfs.h"
9 #include "vfs_int.h"
10 #include "vfs_ext.h"
11 #include <threads.h>
12
13 // === CONSTANTS ===
14 #define OPEN_MOUNT_ROOT 1
15 #define MAX_PATH_SLASHES        256
16 #define MAX_NESTED_LINKS        4
17 #define MAX_PATH_LEN    255
18
19 // === IMPORTS ===
20 extern tVFS_Mount       *gVFS_RootMount;
21 extern int      VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode);
22 extern tVFS_Node        *VFS_MemFile_Create(const char *Path);
23
24 // === PROTOTYPES ===
25 void    _ReferenceMount(tVFS_Mount *Mount, const char *DebugTag);
26 void    _DereferenceMount(tVFS_Mount *Mount, const char *DebugTag);
27  int    VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode );
28
29 // === CODE ===
30 void _ReferenceMount(tVFS_Mount *Mount, const char *DebugTag)
31 {
32 //      Log_Debug("VFS", "%s: inc. mntpt '%s' to %i", DebugTag, Mount->MountPoint, Mount->OpenHandleCount+1);
33         Mount->OpenHandleCount ++;
34 }
35 void _DereferenceMount(tVFS_Mount *Mount, const char *DebugTag)
36 {
37 //      Log_Debug("VFS", "%s: dec. mntpt '%s' to %i", DebugTag, Mount->MountPoint, Mount->OpenHandleCount-1);
38         ASSERT(Mount->OpenHandleCount > 0);
39         Mount->OpenHandleCount --;
40 }
41 /**
42  * \fn char *VFS_GetAbsPath(const char *Path)
43  * \brief Create an absolute path from a relative one
44  */
45 char *VFS_GetAbsPath(const char *Path)
46 {
47         char    *ret;
48          int    pathLen = strlen(Path);
49         char    *pathComps[MAX_PATH_SLASHES];
50         char    *tmpStr;
51         int             iPos = 0;
52         int             iPos2 = 0;
53         const char      *chroot = *Threads_GetChroot();
54          int    chrootLen;
55         const char      *cwd = *Threads_GetCWD();
56          int    cwdLen;
57         
58         ENTER("sPath", Path);
59         
60         // Memory File
61         if(Path[0] == '$') {
62                 ret = malloc(strlen(Path)+1);
63                 if(!ret) {
64                         Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL");
65                         return NULL;
66                 }
67                 strcpy(ret, Path);
68                 LEAVE('p', ret);
69                 return ret;
70         }
71         
72         // - Fetch ChRoot
73         if( chroot == NULL )
74                 chroot = "";
75         chrootLen = strlen(chroot);
76         // Trim trailing slash off chroot
77         if( chrootLen && chroot[chrootLen - 1] == '/' )
78                 chrootLen -= 1;
79         
80         // Check if the path is already absolute
81         if(Path[0] == '/') {
82                 ret = malloc(chrootLen + pathLen + 1);
83                 if(!ret) {
84                         Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL");
85                         return NULL;
86                 }
87                 strcpy(ret + chrootLen, Path);
88         }
89         else {
90                 if(cwd == NULL) {
91                         cwd = "/";
92                         cwdLen = 1;
93                 }
94                 else {
95                         cwdLen = strlen(cwd);
96                 }
97                 // Prepend the current directory
98                 ret = malloc(chrootLen + cwdLen + 1 + pathLen + 1 );
99                 strcpy(ret+chrootLen, cwd);
100                 ret[cwdLen] = '/';
101                 strcpy(ret+chrootLen+cwdLen+1, Path);
102                 //Log("ret = '%s'", ret);
103         }
104         
105         // Parse Path
106         pathComps[iPos++] = tmpStr = ret+chrootLen+1;
107         while(*tmpStr)
108         {
109                 if(*tmpStr++ == '/')
110                 {
111                         pathComps[iPos++] = tmpStr;
112                         if(iPos == MAX_PATH_SLASHES) {
113                                 LOG("Path '%s' has too many elements", Path);
114                                 free(ret);
115                                 LEAVE('n');
116                                 return NULL;
117                         }
118                 }
119         }
120         pathComps[iPos] = NULL;
121         
122         // Cleanup
123         iPos2 = iPos = 0;
124         while(pathComps[iPos])
125         {
126                 tmpStr = pathComps[iPos];
127                 // Always Increment iPos
128                 iPos++;
129                 // ..
130                 if(tmpStr[0] == '.' && tmpStr[1] == '.' && (tmpStr[2] == '/' || tmpStr[2] == '\0') )
131                 {
132                         if(iPos2 != 0)
133                                 iPos2 --;
134                         continue;
135                 }
136                 // .
137                 if(tmpStr[0] == '.' && (tmpStr[1] == '/' || tmpStr[1] == '\0') )
138                 {
139                         continue;
140                 }
141                 // Empty
142                 if(tmpStr[0] == '/' || tmpStr[0] == '\0')
143                 {
144                         continue;
145                 }
146                 
147                 // Set New Position
148                 pathComps[iPos2] = tmpStr;
149                 iPos2++;
150         }
151         pathComps[iPos2] = NULL;
152         
153         // Build New Path
154         iPos2 = chrootLen + 1;  iPos = 0;
155         ret[0] = '/';
156         while(pathComps[iPos])
157         {
158                 tmpStr = pathComps[iPos];
159                 while(*tmpStr && *tmpStr != '/')
160                 {
161                         ret[iPos2++] = *tmpStr;
162                         tmpStr++;
163                 }
164                 ret[iPos2++] = '/';
165                 iPos++;
166         }
167         if(iPos2 > 1)
168                 ret[iPos2-1] = 0;
169         else
170                 ret[iPos2] = 0;
171
172         // Prepend the chroot
173         if(chrootLen)
174                 memcpy( ret, chroot, chrootLen );
175         
176         LEAVE('s', ret);
177 //      Log_Debug("VFS", "VFS_GetAbsPath: RETURN '%s'", ret);
178         return ret;
179 }
180
181 /**
182  * \fn char *VFS_ParsePath(const char *Path, char **TruePath)
183  * \brief Parses a path, resolving sysmlinks and applying permissions
184  */
185 tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath, tVFS_Mount **MountPoint)
186 {
187         tVFS_Mount      *mnt, *longestMount;
188          int    cmp, retLength = 0;
189          int    ofs, nextSlash;
190          int    iNestedLinks = 0;
191         tVFS_Node       *curNode, *tmpNode;
192         char    *tmp;
193         char    path_buffer[MAX_PATH_LEN+1];
194         
195         ENTER("sPath pTruePath", Path, TruePath);
196         
197         // HACK: Memory File
198         if(Threads_GetUID() == 0 && Path[0] == '$') {
199                 if(TruePath) {
200                         *TruePath = malloc(strlen(Path)+1);
201                         strcpy(*TruePath, Path);
202                 }
203                 curNode = VFS_MemFile_Create(Path);
204                 if(MountPoint) {
205                         *MountPoint = NULL;
206                 }
207                 LEAVE('p', curNode);
208                 return curNode;
209         }
210
211 restart_parse:  
212         // For root we always fast return
213         if(Path[0] == '/' && Path[1] == '\0') {
214                 if(TruePath) {
215                         *TruePath = malloc( gVFS_RootMount->MountPointLen+1 );
216                         strcpy(*TruePath, gVFS_RootMount->MountPoint);
217                 }
218                 _ReferenceMount(gVFS_RootMount, "ParsePath - Fast Tree Root");
219                 if(MountPoint)  *MountPoint = gVFS_RootMount;
220                 LEAVE('p', gVFS_RootMount->RootNode);
221                 return gVFS_RootMount->RootNode;
222         }
223         
224         // Check if there is anything mounted
225         if(!gVFS_Mounts) {
226                 Log_Error("VFS", "VFS_ParsePath - No filesystems mounted");
227                 return NULL;
228         }
229         
230         // Find Mountpoint
231         longestMount = gVFS_RootMount;
232         RWLock_AcquireRead( &glVFS_MountList );
233         for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
234         {
235                 // Quick Check
236                 if( Path[mnt->MountPointLen] != '/' && Path[mnt->MountPointLen] != '\0')
237                         continue;
238                 // Length Check - If the length is smaller than the longest match sofar
239                 if(mnt->MountPointLen < longestMount->MountPointLen)    continue;
240                 // String Compare
241                 cmp = strncmp(Path, mnt->MountPoint, mnt->MountPointLen);
242                 // Not a match, continue
243                 if(cmp != 0)    continue;
244                 
245                 #if OPEN_MOUNT_ROOT
246                 // Fast Break - Request Mount Root
247                 if(Path[mnt->MountPointLen] == '\0') {
248                         if(TruePath) {
249                                 *TruePath = malloc( mnt->MountPointLen+1 );
250                                 strcpy(*TruePath, mnt->MountPoint);
251                         }
252                         if(MountPoint)
253                                 *MountPoint = mnt;
254                         RWLock_Release( &glVFS_MountList );
255                         LOG("Mount %p root", mnt);
256                         _ReferenceMount(mnt, "ParsePath - Mount Root");
257                         LEAVE('p', mnt->RootNode);
258                         return mnt->RootNode;
259                 }
260                 #endif
261                 longestMount = mnt;
262         }
263         if(!longestMount) {
264                 Log_Panic("VFS", "VFS_ParsePath - No mount for '%s'", Path);
265                 return NULL;
266         }
267         
268         _ReferenceMount(longestMount, "ParsePath");
269         RWLock_Release( &glVFS_MountList );
270         
271         // Save to shorter variable
272         mnt = longestMount;
273         
274         LOG("mnt = {MountPoint:\"%s\"}", mnt->MountPoint);
275         
276         // Initialise String
277         if(TruePath)
278         {
279                 // Assumes that the resultant path (here) will not be > strlen(Path) + 1
280                 *TruePath = malloc( strlen(Path) + 1 );
281                 strcpy(*TruePath, mnt->MountPoint);
282                 retLength = mnt->MountPointLen;
283         }
284         
285         curNode = mnt->RootNode;
286         curNode->ReferenceCount ++;     
287         // Parse Path
288         ofs = mnt->MountPointLen+1;
289         for(; (nextSlash = strpos(&Path[ofs], '/')) != -1; ofs += nextSlash + 1)
290         {
291                 char    pathEle[nextSlash+1];
292                 
293                 // Empty String
294                 if(nextSlash == 0)      continue;
295                 
296                 memcpy(pathEle, &Path[ofs], nextSlash);
297                 pathEle[nextSlash] = 0;
298         
299                 // Check permissions on root of filesystem
300                 if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) {
301                         LOG("Permissions failure on '%s'", Path);
302                         goto _error;
303                 }
304                 
305                 // Check if the node has a FindDir method
306                 if( !curNode->Type )
307                 {
308                         LOG("Finddir failure on '%s' - No type", Path);
309                         Log_Error("VFS", "Node at '%s' has no type (mount %s:%s)",
310                                 Path, mnt->Filesystem->Name, mnt->MountPoint);
311                         goto _error;
312                 }
313                 if( !curNode->Type->FindDir )
314                 {
315                         LOG("Finddir failure on '%s' - No FindDir method in %s", Path, curNode->Type->TypeName);
316                         goto _error;
317                 }
318                 LOG("FindDir{=%p}(%p, '%s')", curNode->Type->FindDir, curNode, pathEle);
319                 // Get Child Node
320                 tmpNode = curNode->Type->FindDir(curNode, pathEle);
321                 LOG("tmpNode = %p", tmpNode);
322                 _CloseNode( curNode );
323                 curNode = tmpNode;
324                 
325                 // Error Check
326                 if(!curNode) {
327                         LOG("Node '%s' not found in dir '%s'", pathEle, Path);
328                         goto _error;
329                 }
330                 
331                 // Handle Symbolic Links
332                 if(curNode->Flags & VFS_FFLAG_SYMLINK) {
333                         if(TruePath) {
334                                 free(*TruePath);
335                                 *TruePath = NULL;
336                         }
337                         if(!curNode->Type || !curNode->Type->Read) {
338                                 Log_Warning("VFS", "VFS_ParsePath - Read of symlink node %p'%s' is NULL",
339                                         curNode, Path);
340                                 goto _error;
341                         }
342                         
343                         if(iNestedLinks > MAX_NESTED_LINKS) {
344                                 Log_Notice("VFS", "VFS_ParsePath - Nested link limit exceeded");
345                                 goto _error;
346                         }
347                         
348                         // Parse Symlink Path
349                         // - Just update the path variable and restart the function
350                         // > Count nested symlinks and limit to some value (counteracts loops)
351                         {
352                                  int    remlen = strlen(Path) - (ofs + nextSlash);
353                                 if( curNode->Size + remlen > MAX_PATH_LEN ) {
354                                         Log_Warning("VFS", "VFS_ParsePath - Symlinked path too long");
355                                         goto _error;
356                                 }
357                                 curNode->Type->Read( curNode, 0, curNode->Size, path_buffer );
358                                 path_buffer[ curNode->Size ] = '\0';
359                                 LOG("path_buffer = '%s'", path_buffer);
360                                 strcat(path_buffer, Path + ofs+nextSlash);
361                                 // TODO: Pass to VFS_GetAbsPath to handle ../. in the symlink
362                                 
363                                 Path = path_buffer;
364 //                              Log_Debug("VFS", "VFS_ParsePath: Symlink translated to '%s'", Path);
365                                 iNestedLinks ++;
366                         }
367
368                         // EVIL: Goto :)
369                         LOG("Symlink -> '%s', restart", Path);
370                         _DereferenceMount(mnt, "ParsePath - sym");
371                         goto restart_parse;
372                 }
373                 
374                 // Handle Non-Directories
375                 if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) )
376                 {
377                         Log_Warning("VFS", "VFS_ParsePath - Path segment is not a directory");
378                         goto _error;
379                 }
380                 
381                 // Check if path needs extending
382                 if(!TruePath)   continue;
383                 
384                 // Increase buffer space
385                 tmp = realloc( *TruePath, retLength + strlen(pathEle) + 1 + 1 );
386                 // Check if allocation succeeded
387                 if(!tmp) {
388                         Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer");
389                         goto _error;
390                 }
391                 *TruePath = tmp;
392                 // Append to path
393                 (*TruePath)[retLength] = '/';
394                 strcpy(*TruePath+retLength+1, pathEle);
395                 
396                 LOG("*TruePath = '%s'", *TruePath);
397                 
398                 // - Extend Path
399                 retLength += nextSlash + 1;
400         }
401
402         // Check final finddir call     
403         if( !curNode->Type || !curNode->Type->FindDir ) {
404                 Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for element of '%s'", Path);
405                 goto _error;
406         }
407         
408         // Get last node
409         LOG("FindDir(%p, '%s')", curNode, &Path[ofs]);
410         tmpNode = curNode->Type->FindDir(curNode, &Path[ofs]);
411         LOG("tmpNode = %p", tmpNode);
412         // Check if file was found
413         if(!tmpNode) {
414                 LOG("Node '%s' not found in dir '%.*s'", &Path[ofs], ofs, Path);
415                 goto _error;
416         }
417         _CloseNode( curNode );
418         
419         if(TruePath)
420         {
421                 // Increase buffer space
422                 tmp = realloc(*TruePath, retLength + strlen(&Path[ofs]) + 1 + 1);
423                 // Check if allocation succeeded
424                 if(!tmp) {
425                         Log_Warning("VFS", "VFS_ParsePath -  Unable to reallocate true path buffer");
426                         goto _error;
427                 }
428                 *TruePath = tmp;
429                 // Append to path
430                 (*TruePath)[retLength] = '/';
431                 strcpy(*TruePath + retLength + 1, &Path[ofs]);
432                 // - Extend Path
433                 //retLength += strlen(tmpNode->Name) + 1;
434         }
435
436         if( MountPoint ) {
437                 *MountPoint = mnt;
438         }
439         
440         // Leave the mointpoint's count increased
441         
442         LEAVE('p', tmpNode);
443         return tmpNode;
444
445 _error:
446         _CloseNode( curNode );
447         
448         if(TruePath && *TruePath) {
449                 free(*TruePath);
450                 *TruePath = NULL;
451         }
452         // Open failed, so decrement the open handle count
453         _DereferenceMount(mnt, "ParsePath - error");
454         
455         LEAVE('n');
456         return NULL;
457 }
458
459 /**
460  * \brief Create and return a handle number for the given node and mode
461  */
462 int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode )
463 {
464          int    i;
465         
466         ENTER("pNode pMount xMode", Node, Mount, Mode);
467
468         i = 0;
469         i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0;
470         i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0;
471         i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0;
472         
473         LOG("i = 0b%b", i);
474         
475         // Permissions Check
476         if( !VFS_CheckACL(Node, i) ) {
477                 _CloseNode( Node );
478                 Log_Log("VFS", "VFS_int_CreateHandle: Permissions Failed");
479                 errno = EACCES;
480                 LEAVE_RET('i', -1);
481         }
482
483         if( MM_GetPhysAddr(Node->Type) == 0 ) {
484                 Log_Error("VFS", "Node %p from mount '%s' (%s) has a bad type (%p)",
485                         Node, Mount->MountPoint, Mount->Filesystem->Name, Node->Type);
486                 errno = EINTERNAL;
487                 LEAVE_RET('i', -1);
488         }
489         
490         i = VFS_AllocHandle( !!(Mode & VFS_OPENFLAG_USER), Node, Mode );
491         if( i < 0 ) {
492                 Log_Notice("VFS", "VFS_int_CreateHandle: Out of handles");
493                 errno = ENFILE;
494                 LEAVE_RET('i', -1);
495         }
496
497         VFS_GetHandle(i)->Mount = Mount;
498
499         LEAVE_RET('x', i);
500 }
501
502 /**
503  * \fn int VFS_Open(const char *Path, Uint Mode)
504  * \brief Open a file
505  */
506 int VFS_Open(const char *Path, Uint Flags)
507 {
508         return VFS_OpenEx(Path, Flags, 0);
509 }
510
511 int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode)
512 {
513         tVFS_Node       *node;
514         tVFS_Mount      *mnt;
515         char    *absPath;
516         
517         ENTER("sPath xFlags oMode", Path, Flags);
518         
519         // Get absolute path
520         absPath = VFS_GetAbsPath(Path);
521         if(absPath == NULL) {
522                 Log_Warning("VFS", "VFS_Open: Path expansion failed '%s'", Path);
523                 LEAVE_RET('i', -1);
524         }
525         LOG("absPath = \"%s\"", absPath);
526         
527         // Parse path and get mount point
528         node = VFS_ParsePath(absPath, NULL, &mnt);
529         
530         // Create file if requested and it doesn't exist
531         if( !node && (Flags & VFS_OPENFLAG_CREATE) )
532         {
533                 // TODO: Translate `Mode` into ACL and node flags
534                 Uint    new_flags = 0;
535                 
536                 // Split path at final separator
537                 char *file = strrchr(absPath, '/');
538                 *file = '\0';
539                 file ++;
540
541                 // Get parent node
542                 tVFS_Mount      *pmnt;
543                 tVFS_Node *pnode = VFS_ParsePath(absPath, NULL, &pmnt);
544                 if(!pnode) {
545                         LOG("Unable to open parent '%s'", absPath);
546                         free(absPath);
547                         errno = ENOENT;
548                         LEAVE_RET('i', -1);
549                 }
550
551                 // Check ACLs on the parent
552                 if( !VFS_CheckACL(pnode, VFS_PERM_EXECUTE|VFS_PERM_WRITE) ) {
553                         errno = EACCES;
554                         goto _pnode_err;
555                 }
556
557                 // Check that there's a MkNod method
558                 if( !pnode->Type || !pnode->Type->MkNod ) {
559                         Log_Warning("VFS", "VFS_Open - Directory has no MkNod method");
560                         errno = EINVAL;
561                         goto _pnode_err;
562                 }
563                 
564                 node = pnode->Type->MkNod(pnode, file, new_flags);
565                 if( !node ) {
566                         LOG("Cannot create node '%s' in '%s'", file, absPath);
567                         errno = ENOENT;
568                         goto _pnode_err;
569                 }
570                 // Set mountpoint (and increment open handle count)
571                 mnt = pmnt;
572                 _ReferenceMount(mnt, "Open - create");
573                 // Fall through on error check
574                 
575                 _CloseNode(pnode);
576                 _DereferenceMount(pmnt, "Open - create");
577                 goto _pnode_ok;
578
579         _pnode_err:
580                 if( pnode ) {
581                         _CloseNode(pnode);
582                         _DereferenceMount(pmnt, "Open - create,fail");
583                         free(absPath);
584                 }
585                 LEAVE('i', -1);
586                 return -1;
587         }
588         _pnode_ok:
589         
590         // Free generated path
591         free(absPath);
592         
593         // Check for error
594         if(!node)
595         {
596                 LOG("Cannot find node");
597                 errno = ENOENT;
598                 goto _error;
599         }
600         
601         // Check for symlinks
602         if( !(Flags & VFS_OPENFLAG_NOLINK) && (node->Flags & VFS_FFLAG_SYMLINK) )
603         {
604                 char    tmppath[node->Size+1];
605                 if( node->Size > MAX_PATH_LEN ) {
606                         Log_Warning("VFS", "VFS_Open - Symlink is too long (%i)", node->Size);
607                         goto _error;
608                 }
609                 if( !node->Type || !node->Type->Read ) {
610                         Log_Warning("VFS", "VFS_Open - No read method on symlink");
611                         goto _error;
612                 }
613                 // Read symlink's path
614                 node->Type->Read( node, 0, node->Size, tmppath );
615                 tmppath[ node->Size ] = '\0';
616                 _CloseNode( node );
617                 _DereferenceMount(mnt, "Open - symlink");
618                 // Open the target
619                 node = VFS_ParsePath(tmppath, NULL, &mnt);
620                 if(!node) {
621                         LOG("Cannot find symlink target node (%s)", tmppath);
622                         errno = ENOENT;
623                         goto _error;
624                 }
625         }
626
627          int    ret = VFS_int_CreateHandle(node, mnt, Flags);
628         LEAVE_RET('x', ret);
629 _error:
630         if( node )
631         {
632                 _DereferenceMount(mnt, "Open - error");
633                 _CloseNode(node);
634         }
635         LEAVE_RET('i', -1);
636 }
637
638
639 /**
640  * \brief Open a file from an open directory
641  */
642 int VFS_OpenChild(int FD, const char *Name, Uint Mode)
643 {
644         tVFS_Handle     *h;
645         tVFS_Node       *node;
646         
647         ENTER("xFD sName xMode", FD, Name, Mode);
648
649         // Get handle
650         h = VFS_GetHandle(FD);
651         if(h == NULL) {
652                 Log_Warning("VFS", "VFS_OpenChild - Invalid file handle 0x%x", FD);
653                 errno = EINVAL;
654                 LEAVE_RET('i', -1);
655         }
656         
657         // Check for directory
658         if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
659                 Log_Warning("VFS", "VFS_OpenChild - Passed handle is not a directory");
660                 errno = ENOTDIR;
661                 LEAVE_RET('i', -1);
662         }
663
664         // Sanity check
665         if( !h->Node->Type || !h->Node->Type->FindDir ) {
666                 Log_Error("VFS", "VFS_OpenChild - Node does not have a type/is missing FindDir");
667                 errno = ENOTDIR;
668                 LEAVE_RET('i', -1);
669         }
670         
671         // Find Child
672         node = h->Node->Type->FindDir(h->Node, Name);
673         if(!node) {
674                 errno = ENOENT;
675                 LEAVE_RET('i', -1);
676         }
677
678         // Increment open handle count, no problems with the mount going away as `h` is already open on it
679         _ReferenceMount(h->Mount, "OpenChild");
680
681         LEAVE_RET('x', VFS_int_CreateHandle(node, h->Mount, Mode));
682 }
683
684 int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode)
685 {
686         tVFS_Mount      *mnt;
687         tVFS_Node       *node;
688
689         ENTER("iMount XInode xMode", Mount, Inode, Mode);
690         
691         // Get mount point
692         mnt = VFS_GetMountByIdent(Mount);
693         if( !mnt ) {
694                 LOG("Mount point ident invalid");
695                 errno = ENOENT;
696                 LEAVE_RET('i', -1);
697         }
698         
699         // Does the filesystem support this?
700         if( !mnt->Filesystem->GetNodeFromINode ) {
701                 LOG("Filesystem does not support inode accesses");
702                 errno = ENOENT;
703                 LEAVE_RET('i', -1);
704         }
705
706         // Get node
707         node = mnt->Filesystem->GetNodeFromINode(mnt->RootNode, Inode);
708         if( !node ) {
709                 LOG("Unable to find inode");
710                 errno = ENOENT;
711                 LEAVE_RET('i', -1);
712         }
713         
714         LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Mode));
715 }
716
717 /**
718  * \fn void VFS_Close(int FD)
719  * \brief Closes an open file handle
720  */
721 void VFS_Close(int FD)
722 {
723         tVFS_Handle     *h;
724         
725         // Get handle
726         h = VFS_GetHandle(FD);
727         if(h == NULL) {
728                 Log_Warning("VFS", "Invalid file handle passed to VFS_Close, 0x%x", FD);
729                 return;
730         }
731
732         if( h->Node == NULL ) {
733                 Log_Warning("VFS", "Non-open handle passed to VFS_Close, 0x%x", FD);
734                 return ;
735         }       
736
737         #if VALIDATE_VFS_FUNCTIPONS
738         if(h->Node->Close && !MM_GetPhysAddr(h->Node->Close)) {
739                 Log_Warning("VFS", "Node %p's ->Close method is invalid (%p)",
740                         h->Node, h->Node->Close);
741                 return ;
742         }
743         #endif
744         
745         LOG("Handle %x", FD);
746         _CloseNode(h->Node);
747
748         if( h->Mount ) {
749                 _DereferenceMount(h->Mount, "Close");
750         }
751
752         h->Node = NULL;
753 }
754
755 /**
756  * \brief Change current working directory
757  */
758 int VFS_ChDir(const char *Dest)
759 {
760         char    *buf;
761          int    fd;
762         tVFS_Handle     *h;
763         
764         // Create Absolute
765         buf = VFS_GetAbsPath(Dest);
766         if(buf == NULL) {
767                 Log_Notice("VFS", "VFS_ChDir: Path expansion failed");
768                 return -1;
769         }
770         
771         // Check if path exists
772         fd = VFS_Open(buf, VFS_OPENFLAG_EXEC);
773         if(fd == -1) {
774                 Log_Notice("VFS", "VFS_ChDir: Path is invalid");
775                 return -1;
776         }
777         
778         // Get node so we can check for directory
779         h = VFS_GetHandle(fd);
780         if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
781                 Log("VFS_ChDir: Path is not a directory");
782                 VFS_Close(fd);
783                 return -1;
784         }
785         
786         // Close file
787         VFS_Close(fd);
788         
789         {
790                 char    **cwdptr = Threads_GetCWD();
791                 // Free old working directory
792                 if( *cwdptr )   free( *cwdptr );
793                 // Set new
794                 *cwdptr = buf;
795         }
796         
797         Log_Debug("VFS", "Updated CWD to '%s'", buf);
798         
799         return 1;
800 }
801
802 /**
803  * \fn int VFS_ChRoot(char *New)
804  * \brief Change current root directory
805  */
806 int VFS_ChRoot(const char *New)
807 {
808         char    *buf;
809          int    fd;
810         tVFS_Handle     *h;
811         
812         if(New[0] == '/' && New[1] == '\0')
813                 return 1;       // What a useless thing to ask!
814         
815         // Create Absolute
816         buf = VFS_GetAbsPath(New);
817         if(buf == NULL) {
818                 LOG("Path expansion failed");
819                 return -1;
820         }
821         
822         // Check if path exists
823         fd = VFS_Open(buf, VFS_OPENFLAG_EXEC);
824         if(fd == -1) {
825                 LOG("Path is invalid");
826                 return -1;
827         }
828         
829         // Get node so we can check for directory
830         h = VFS_GetHandle(fd);
831         if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) {
832                 LOG("Path is not a directory");
833                 VFS_Close(fd);
834                 return -1;
835         }
836         
837         // Close file
838         VFS_Close(fd);
839
840         // Update       
841         {
842                 char    **chroot_ptr = Threads_GetChroot();
843                 if( *chroot_ptr )       free( *chroot_ptr );
844                 *chroot_ptr = buf;
845         }
846         
847         LOG("Updated Root to '%s'", buf);
848         
849         return 1;
850 }
851
852 // === EXPORTS ===
853 EXPORT(VFS_Open);
854 EXPORT(VFS_Close);

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