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

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