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

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