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

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