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

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