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

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