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

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