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

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