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

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