Fixed VFS_ParsePath freeing a string that is returned to the user
[tpg/acess2.git] / Kernel / vfs / open.c
1 /*
2  * AcessMicro VFS
3  * - Open, Close and ChDir
4  */
5 #include <common.h>
6 #include "vfs.h"
7 #include "vfs_int.h"
8 #include "vfs_ext.h"
9
10 #define DEBUG   0
11
12 #if DEBUG
13 #else
14 # undef ENTER
15 # undef LOG
16 # undef LEAVE
17 # define ENTER(...)
18 # define LOG(...)
19 # define LEAVE(...)
20 #endif
21
22 // === CONSTANTS ===
23 #define OPEN_MOUNT_ROOT 1
24 #define MAX_KERNEL_FILES        128
25
26 // === IMPORTS ===
27 extern tVFS_Node        gVFS_MemRoot;
28 extern tVFS_Mount       *gRootMount;
29
30 // === GLOBALS ===
31 tVFS_Handle     *gaUserHandles = (void*)MM_PPD_VFS;
32 tVFS_Handle     *gaKernelHandles = (void*)MM_KERNEL_VFS;
33
34 // === CODE ===
35 /**
36  * \fn char *VFS_GetAbsPath(char *Path)
37  * \brief Create an absolute path from a relative one
38  */
39 char *VFS_GetAbsPath(char *Path)
40 {
41         char    *ret;
42          int    pathLen = strlen(Path);
43          int    read, write;
44          int    pos, slashNum=0, baseLen;
45         Uint    slashOffsets[256];
46         char    *cwd = CFGPTR(CFG_VFS_CWD);
47          int    cwdLen;
48         
49         ENTER("sPath", Path);
50         
51         // Memory File
52         if(Path[0] == '$') {
53                 ret = malloc(strlen(Path)+1);
54                 strcpy(ret, Path);
55                 LEAVE('p', ret);
56                 return ret;
57         }
58         
59         // Check if the path is already absolute
60         if(Path[0] == '/') {
61                 ret = malloc(pathLen + 1);
62                 strcpy(ret, Path);
63                 baseLen = 1;
64         } else {
65                 cwdLen = strlen(cwd);
66                 // Prepend the current directory
67                 ret = malloc(cwdLen+pathLen+1);
68                 strcpy(ret, cwd);
69                 strcpy(&ret[cwdLen], Path);
70         
71                 // Pre-fill the slash positions
72                 pos = 0;
73                 while( (pos = strpos( &ret[pos+1], '/' )) != -1 )
74                         slashOffsets[slashNum++] = pos;
75                         
76                 baseLen = cwdLen;
77         }
78         
79         // Remove . and ..
80         read = write = baseLen; // Cwd has already been parsed
81         for(; read < baseLen+pathLen; read = pos+1)
82         {
83                 pos = strpos( &ret[read], '/' );
84                 // If we are in the last section, force a break at the end of the itteration
85                 if(pos == -1)   pos = baseLen+pathLen;
86                 else    pos += read;    // Else, Adjust to absolute
87                 
88                 // Check Length
89                 if(pos - read <= 2)
90                 {
91                         // Current Dir "."
92                         if(strncmp(&ret[read], ".", pos-read) == 0)     continue;
93                         // Parent ".."
94                         if(strncmp(&ret[read], "..", pos-read) == 0)
95                         {
96                                 // If there is no higher, silently ignore
97                                 if(!slashNum)   continue;
98                                 // Reverse write pointer
99                                 write = slashOffsets[ slashNum-- ];
100                                 continue;
101                         }
102                 }
103                 
104                 
105                 // Only copy if the positions differ
106                 if(read != write) {
107                         memcpy( &ret[write], &ret[read], pos-read+1 );
108                 }
109                 write = pos+1;
110                 if(slashNum < 256)
111                         slashOffsets[ slashNum++ ] = pos;
112                 else {
113                         LOG("Path '%s' has too many elements", Path);
114                         free(ret);
115                         LEAVE('n');
116                         return NULL;
117                 }
118         }
119         
120         // `ret` should now be the absolute path
121         LEAVE('s', ret);
122         return ret;
123 }
124
125 /**
126  * \fn char *VFS_ParsePath(char *Path, char **TruePath)
127  * \brief Parses a path, resolving sysmlinks and applying permissions
128  */
129 tVFS_Node *VFS_ParsePath(char *Path, char **TruePath)
130 {
131         tVFS_Mount      *mnt;
132         tVFS_Mount      *longestMount = gRootMount;     // Root is first
133          int    cmp, retLength = 0;
134          int    ofs, nextSlash;
135         tVFS_Node       *curNode, *tmpNode;
136         char    *tmp;
137         
138         ENTER("sPath pTruePath", Path, TruePath);
139         
140         // Memory File
141         if(Path[0] == '$') {
142                 if(TruePath) {
143                         *TruePath = malloc(strlen(Path)+1);
144                         strcpy(*TruePath, Path);
145                 }
146                 curNode = gVFS_MemRoot.FindDir(&gVFS_MemRoot, Path);
147                 LEAVE('p', curNode);
148                 return curNode;
149         }
150         // For root we always fast return
151         
152         if(Path[0] == '/' && Path[1] == '\0') {
153                 if(TruePath) {
154                         *TruePath = malloc( gRootMount->MountPointLen+1 );
155                         strcpy(*TruePath, gRootMount->MountPoint);
156                 }
157                 LEAVE('p', gRootMount->RootNode);
158                 return gRootMount->RootNode;
159         }
160         
161         // Check if there is anything mounted
162         if(!gMounts)    return NULL;
163         
164         // Find Mountpoint
165         for(mnt = gMounts;
166                 mnt;
167                 mnt = mnt->Next)
168         {
169                 // Quick Check
170                 if( Path[mnt->MountPointLen] != '/' && Path[mnt->MountPointLen] != '\0')
171                         continue;
172                 // Length Check - If the length is smaller than the longest match sofar
173                 if(mnt->MountPointLen < longestMount->MountPointLen)    continue;
174                 // String Compare
175                 cmp = strcmp(Path, mnt->MountPoint);
176                 
177                 #if OPEN_MOUNT_ROOT
178                 // Fast Break - Request Mount Root
179                 if(cmp == 0) {
180                         if(TruePath) {
181                                 *TruePath = malloc( mnt->MountPointLen+1 );
182                                 strcpy(*TruePath, mnt->MountPoint);
183                         }
184                         LEAVE('p', mnt->RootNode);
185                         return mnt->RootNode;
186                 }
187                 #endif
188                 // Not a match, continue
189                 if(cmp != '/')  continue;
190                 longestMount = mnt;
191         }
192         
193         // Sanity Check
194         /*if(!longestMount) {
195                 Log("VFS_GetTruePath - ERROR: No Root Node\n");
196                 return NULL;
197         }*/
198         
199         // Save to shorter variable
200         mnt = longestMount;
201         
202         LOG("mnt = {MountPoint:\"%s\"}", mnt->MountPoint);
203         
204         // Initialise String
205         if(TruePath)
206         {
207                 *TruePath = malloc( mnt->MountPointLen+1 );
208                 strcpy(*TruePath, mnt->MountPoint);
209                 retLength = mnt->MountPointLen;
210         }
211         
212         curNode = mnt->RootNode;
213         curNode->ReferenceCount ++;     
214         // Parse Path
215         ofs = mnt->MountPointLen+1;
216         for(; (nextSlash = strpos(&Path[ofs], '/')) != -1; Path[nextSlash]='/',ofs = nextSlash + 1)
217         {
218                 nextSlash += ofs;
219                 Path[nextSlash] = '\0';
220         
221                 // Check for empty string
222                 if( Path[ofs] == '\0' ) continue;
223         
224                 // Check permissions on root of filesystem
225                 if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) {
226                         curNode->Close( curNode );
227                         if(TruePath) {
228                                 free(*TruePath);
229                                 *TruePath = NULL;
230                         }
231                         LEAVE('n');
232                         return NULL;
233                 }
234                 
235                 // Check if the node has a FindDir method
236                 if(!curNode->FindDir) {
237                         if(curNode->Close)      curNode->Close(curNode);
238                         if(TruePath) {
239                                 free(*TruePath);
240                                 *TruePath = NULL;
241                         }
242                         Path[nextSlash] = '/';
243                         LEAVE('n');
244                         return NULL;
245                 }
246                 LOG("FindDir(%p, '%s')", curNode, &Path[ofs]);
247                 // Get Child Node
248                 tmpNode = curNode->FindDir(curNode, &Path[ofs]);
249                 LOG("tmpNode = %p", tmpNode);
250                 if(curNode->Close)
251                         curNode->Close(curNode);
252                 curNode = tmpNode;
253                 
254                 // Error Check
255                 if(!curNode) {
256                         LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path);
257                         if(TruePath) {
258                                 free(*TruePath);
259                                 *TruePath = NULL;
260                         }
261                         Path[nextSlash] = '/';
262                         LEAVE('n');
263                         return NULL;
264                 }
265                 
266                 // Handle Symbolic Links
267                 if(curNode->Flags & VFS_FFLAG_SYMLINK) {
268                         if(TruePath) {
269                                 free(*TruePath);
270                                 *TruePath = NULL;
271                         }
272                         tmp = malloc( curNode->Size + 1 );
273                         curNode->Read( curNode, 0, curNode->Size, tmp );
274                         tmp[ curNode->Size ] = '\0';
275                         
276                         // Parse Symlink Path
277                         curNode = VFS_ParsePath(tmp, TruePath);
278                         
279                         // Error Check
280                         if(!curNode) {
281                                 free(tmp);      // Free temp string
282                                 LEAVE('n');
283                                 return NULL;
284                         }
285                         
286                         // Set Path Variable
287                         if(TruePath) {
288                                 *TruePath = tmp;
289                                 retLength = strlen(tmp);
290                         } else {
291                                 free(tmp);      // Free temp string
292                         }
293                         
294                         continue;
295                 }
296                 
297                 // Handle Non-Directories
298                 if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) )
299                 {
300                         Warning("VFS_ParsePath - File in directory context");
301                         if(TruePath)    free(*TruePath);
302                         LEAVE('n');
303                         return NULL;
304                 }
305                 
306                 // Check if path needs extending
307                 if(!TruePath)   continue;
308                 
309                 // Increase buffer space
310                 tmp = realloc( *TruePath, retLength + strlen(&Path[ofs]) + 1 + 1 );
311                 // Check if allocation succeeded
312                 if(!tmp) {
313                         Warning("VFS_ParsePath -  Unable to reallocate true path buffer");
314                         free(*TruePath);
315                         if(curNode->Close)      curNode->Close(curNode);
316                         LEAVE('n');
317                         return NULL;
318                 }
319                 *TruePath = tmp;
320                 // Append to path
321                 (*TruePath)[retLength] = '/';
322                 strcpy(*TruePath+retLength+1, &Path[ofs]);
323                 // - Extend Path
324                 retLength += strlen(&Path[ofs])+1;
325         }
326         
327         // Get last node
328         LOG("VFS_ParsePath: FindDir(%p, '%s')", curNode, &Path[ofs]);
329         tmpNode = curNode->FindDir(curNode, &Path[ofs]);
330         LOG("tmpNode = %p", tmpNode);
331         if(curNode->Close)      curNode->Close(curNode);
332         // Check if file was found
333         if(!tmpNode) {
334                 LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path);
335                 if(TruePath)    free(*TruePath);
336                 if(curNode->Close)      curNode->Close(curNode);
337                 LEAVE('n');
338                 return NULL;
339         }
340         
341         if(TruePath)
342         {
343                 // Increase buffer space
344                 tmp = realloc(*TruePath, retLength + strlen(&Path[ofs]) + 1 + 1);
345                 // Check if allocation succeeded
346                 if(!tmp) {
347                         Warning("VFS_ParsePath -  Unable to reallocate true path buffer");
348                         free(*TruePath);
349                         if(tmpNode->Close)      tmpNode->Close(curNode);
350                         LEAVE('n');
351                         return NULL;
352                 }
353                 *TruePath = tmp;
354                 // Append to path
355                 (*TruePath)[retLength] = '/';
356                 strcpy(*TruePath + retLength + 1, &Path[ofs]);
357                 // - Extend Path
358                 //retLength += strlen(tmpNode->Name) + 1;
359         }
360         
361         LEAVE('p', tmpNode);
362         return tmpNode;
363 }
364
365 /**
366  * \fn int VFS_Open(char *Path, Uint Mode)
367  * \brief Open a file
368  */
369 int VFS_Open(char *Path, Uint Mode)
370 {
371         tVFS_Node       *node;
372         char    *absPath;
373          int    i;
374         
375         ENTER("sPath xMode", Path, Mode);
376         
377         // Get absolute path
378         absPath = VFS_GetAbsPath(Path);
379         LOG("absPath = \"%s\"", absPath);
380         // Parse path and get mount point
381         node = VFS_ParsePath(absPath, NULL);
382         // Free generated path
383         free(absPath);
384         
385         if(!node) {
386                 LOG("Cannot find node");
387                 LEAVE('i', -1);
388                 return -1;
389         }
390         
391         // Check for symlinks
392         if( !(Mode & VFS_OPENFLAG_NOLINK) && (node->Flags & VFS_FFLAG_SYMLINK) )
393         {
394                 if( !node->Read ) {
395                         LOG("No read method on symlink");
396                         LEAVE('i', -1);
397                         return -1;
398                 }
399                 absPath = malloc(node->Size+1); // Allocate Buffer
400                 node->Read( node, 0, node->Size, absPath );     // Read Path
401                 
402                 absPath[ node->Size ] = '\0';   // End String
403                 if(node->Close) node->Close( node );    // Close old node
404                 node = VFS_ParsePath(absPath, NULL);    // Get new node
405                 free( absPath );        // Free allocated path
406         }
407         
408         if(!node) {
409                 LEAVE('i', -1);
410                 return -1;
411         }
412         
413         i = 0;
414         i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0;
415         i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0;
416         i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0;
417         
418         LOG("i = 0b%b", i);
419         
420         // Permissions Check
421         if( !VFS_CheckACL(node, i) ) {
422                 node->Close( node );
423                 LEAVE('i', -1);
424                 return -1;
425         }
426         
427         // Check for a user open
428         if(Mode & VFS_OPENFLAG_USER)
429         {
430                 // Allocate Buffer
431                 if( MM_GetPhysAddr( (Uint)gaUserHandles ) == 0 )
432                 {
433                         Uint    addr, size;
434                         size = CFGINT(CFG_VFS_MAXFILES) * sizeof(tVFS_Handle);
435                         for(addr = 0; addr < size; addr += 0x1000)
436                                 MM_Allocate( (Uint)gaUserHandles + addr );
437                         memset( gaUserHandles, 0, size );
438                 }
439                 // Get a handle
440                 for(i=0;i<CFGINT(CFG_VFS_MAXFILES);i++)
441                 {
442                         if(gaUserHandles[i].Node)       continue;
443                         gaUserHandles[i].Node = node;
444                         gaUserHandles[i].Position = 0;
445                         gaUserHandles[i].Mode = Mode;
446                         LEAVE('i', i);
447                         return i;
448                 }
449         }
450         else
451         {
452                 // Allocate space if not already
453                 if( MM_GetPhysAddr( (Uint)gaKernelHandles ) == 0 )
454                 {
455                         Uint    addr, size;
456                         size = MAX_KERNEL_FILES * sizeof(tVFS_Handle);
457                         for(addr = 0; addr < size; addr += 0x1000)
458                                 MM_Allocate( (Uint)gaKernelHandles + addr );
459                         memset( gaKernelHandles, 0, size );
460                 }
461                 // Get a handle
462                 for(i=0;i<MAX_KERNEL_FILES;i++)
463                 {
464                         if(gaKernelHandles[i].Node)     continue;
465                         gaKernelHandles[i].Node = node;
466                         gaKernelHandles[i].Position = 0;
467                         gaKernelHandles[i].Mode = Mode;
468                         LEAVE('x', i|VFS_KERNEL_FLAG);
469                         return i|VFS_KERNEL_FLAG;
470                 }
471         }
472         
473         LEAVE('i', -1);
474         return -1;
475 }
476
477 /**
478  * \fn void VFS_Close(int FD)
479  * \brief Closes an open file handle
480  */
481 void VFS_Close(int FD)
482 {
483         tVFS_Handle     *h;
484         
485         // Get handle
486         h = VFS_GetHandle(FD);
487         if(h == NULL) {
488                 Warning("Invalid file handle passed to VFS_Close, 0x%x\n", FD);
489                 return;
490         }
491         
492         if(h->Node->Close)
493                 h->Node->Close( h->Node );
494         
495         h->Node = NULL;
496 }
497
498 /**
499  * \fn tVFS_Handle *VFS_GetHandle(int FD)
500  * \brief Gets a pointer to the handle information structure
501  */
502 tVFS_Handle *VFS_GetHandle(int FD)
503 {
504         if(FD < 0)      return NULL;
505         
506         if(FD & VFS_KERNEL_FLAG) {
507                 FD &= (VFS_KERNEL_FLAG - 1);
508                 if(FD >= MAX_KERNEL_FILES)      return NULL;
509                 return &gaKernelHandles[ FD ];
510         } else {
511                 if(FD >= CFGINT(CFG_VFS_MAXFILES))      return NULL;
512                 return &gaUserHandles[ FD ];
513         }
514 }

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