Kernel - Added 'Flags' param to VFS Read/Write/FindDir
[tpg/acess2.git] / KernelLand / Modules / Filesystems / RAMDisk / ramdisk.c
1 /*
2  * Acess2 Kernel - RAM Disk Support
3  * - By John Hodge (thePowersGang)
4  *
5  * ramdisk.c
6  * - Core File
7  */
8 #define DEBUG   1
9 #include <acess.h>
10 #include <modules.h>
11 #include "ramdisk.h"
12 #define VERSION VER2(0,1)
13
14 #define MIN_RAMDISK_SIZE        (1*1024*1024)
15 #define MAX_RAMDISK_SIZE        (64*1024*1024)
16
17 // === PROTOTYPES ===
18  int    RAMFS_Install(char **Arguments);
19  int    RAMFS_Cleanup(void);
20 // --- Mount/Unmount ---
21 tVFS_Node       *RAMFS_InitDevice(const char *Device, const char **Options);
22 void    RAMFS_Unmount(tVFS_Node *Node);
23 // --- Directories ---
24  int    RAMFS_ReadDir(tVFS_Node *Node, int Index, char Dest[256]);
25 tVFS_Node       *RAMFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags);
26 tVFS_Node       *RAMFS_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
27  int    RAMFS_Link(tVFS_Node *DirNode, const char *Name, tVFS_Node *Node);
28  int    RAMFS_Unlink(tVFS_Node *Node, const char *Name);
29 // --- Files ---
30 size_t  RAMFS_Read(tVFS_Node *Node, off_t Offset, size_t Size, void *Buffer, Uint Flags);
31 size_t  RAMFS_Write(tVFS_Node *Node, off_t Offset, size_t Size, const void *Buffer, Uint Flags);
32 // --- Internals --
33 void    RAMFS_int_RefFile(tRAMFS_Inode *Inode);
34 void    RAMFS_int_DerefFile(tRAMFS_Inode *Inode);
35 void    *RAMFS_int_GetPage(tRAMFS_File *File, int Page, int bCanAlloc);
36 void    RAMFS_int_DropPage(void *Page);
37 Uint32  RAMFS_int_AllocatePage(tRAMDisk *Disk);
38 void    RAMFS_int_RefFile(tRAMFS_Inode *Inode);
39 void    RAMFS_int_DerefFile(tRAMFS_Inode *Inode);
40
41 // === GLOBALS ===
42 MODULE_DEFINE(0, VERSION, FS_RAMDisk, RAMFS_Install, RAMFS_Cleanup, NULL);
43 tVFS_Driver     gRAMFS_Driver = {
44         .Name = "ramfs",
45         .InitDevice = RAMFS_InitDevice,
46         .Unmount = RAMFS_Unmount
47         // TODO: GetNodeFromInode
48         };
49 tVFS_NodeType   gRAMFS_DirNodeType = {
50         .ReadDir = RAMFS_ReadDir,
51         .FindDir = RAMFS_FindDir,
52         .MkNod   = RAMFS_MkNod,
53         .Link    = RAMFS_Link,
54         .Unlink  = RAMFS_Unlink
55         };
56 tVFS_NodeType   gRAMFS_FileNodeType = {
57         .Read  = RAMFS_Read,
58         .Write = RAMFS_Write
59         };
60
61 // === CODE ===
62 int RAMFS_Install(char **Arguments)
63 {
64         VFS_AddDriver( &gRAMFS_Driver );
65         return 0;
66 }
67
68 int RAMFS_Cleanup(void)
69 {
70         return 0;
71 }
72
73
74 // --- Mount/Unmount
75 const char *_GetOption(const char *Data, const char *Option)
76 {
77          int    len = strlen(Option);
78         if( strncmp(Data, Option, len) != 0 )
79                 return NULL;
80         if( Data[len] != '=' )
81                 return NULL;
82         return Data + len + 1;
83 }
84
85 tVFS_Node *RAMFS_InitDevice(const char *Device, const char **Options)
86 {
87         size_t  size = 0;
88         tRAMDisk        *rd;
89
90         if( Options ) 
91         {
92                 const char *v;
93                 for( int i = 0; Options[i]; i ++ )
94                 {
95                         LOG("Options[%i] = '%s'", i, Options[i]);
96                         if( (v = _GetOption(Options[i], "megs")) )
97                         {
98                                 size = atoi(v) * 1024 * 1024;
99                                 LOG("Size set to %iMiB", size>>20);
100                         }
101                 }
102         }
103
104         LOG("Disk Size %iKiB", size>>10);
105
106         if( size > MAX_RAMDISK_SIZE || size < MIN_RAMDISK_SIZE )
107                 return NULL;
108
109          int    n_pages = size / PAGE_SIZE;
110         
111         rd = calloc(1, sizeof(tRAMDisk) + n_pages * sizeof(tPAddr) + n_pages / 8);
112         rd->MaxPages = n_pages;
113         rd->RootDir.Inode.Disk = rd;
114         rd->RootDir.Inode.Node.ImplPtr = &rd->RootDir;
115         rd->RootDir.Inode.Node.Flags = VFS_FFLAG_DIRECTORY;
116         rd->RootDir.Inode.Node.Size = -1;
117         rd->RootDir.Inode.Node.Type = &gRAMFS_DirNodeType;
118         rd->Bitmap = (void*)&rd->PhysPages[ n_pages ];
119
120         for( int i = 0; i < n_pages; i ++ )
121         {
122                 rd->PhysPages[i] = MM_AllocPhys();
123                 if( !rd->PhysPages[i] ) {
124                         // Um... oops?
125                         break;
126                 }
127         }
128
129         LOG("Mounted");
130         return &rd->RootDir.Inode.Node;
131 }
132
133 void RAMFS_Unmount(tVFS_Node *Node)
134 {
135         Log_Warning("RAMDisk", "TODO: Impliment unmounting");
136 }
137
138 // --- Directories ---
139 int RAMFS_ReadDir(tVFS_Node *Node, int Index, char Dest[FILENAME_MAX])
140 {
141         tRAMFS_Dir      *dir = Node->ImplPtr;
142         for( tRAMFS_DirEnt *d = dir->FirstEnt; d; d = d->Next )
143         {
144                 if( Index -- == 0 ) {
145                         LOG("Return %s", d->Name);
146                         strncpy(Dest, d->Name, FILENAME_MAX);
147                         return 0;
148                 }
149         }
150         LOG("Return -ENOENT");
151         return -ENOENT;
152 }
153
154 tVFS_Node *RAMFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags)
155 {
156         tRAMFS_Dir      *dir = Node->ImplPtr;
157
158         for( tRAMFS_DirEnt *d = dir->FirstEnt; d; d = d->Next )
159         {
160                 if( strcmp(d->Name, Name) == 0 ) {
161                         LOG("Return %p", &d->Inode->Node);
162                         return &d->Inode->Node;
163                 }
164         }
165         
166         LOG("Return NULL");
167         return NULL;
168 }
169
170 tVFS_Node *RAMFS_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
171 {
172         tRAMFS_Dir      *dir = Node->ImplPtr;
173         if( RAMFS_FindDir(Node, Name, 0) != NULL )
174                 return NULL;
175         
176         tRAMFS_DirEnt   *de = malloc( sizeof(tRAMFS_DirEnt) + strlen(Name) + 1 );
177         de->Next = NULL;
178         de->NameLen = strlen(Name);
179         strcpy(de->Name, Name);
180
181         if( Flags & VFS_FFLAG_DIRECTORY ) {
182                 tRAMFS_Dir      *newdir = calloc(1, sizeof(tRAMFS_Dir));
183                 newdir->Inode.Node.Type = &gRAMFS_DirNodeType;
184                 de->Inode = &newdir->Inode;
185         }
186         else {
187                 tRAMFS_File     *newfile = calloc(1, sizeof(tRAMFS_File));
188                 newfile->Inode.Node.Type = &gRAMFS_FileNodeType;
189                 de->Inode = &newfile->Inode;
190         }
191         de->Inode->Disk = dir->Inode.Disk;
192         de->Inode->Node.Flags = Flags;
193         de->Inode->Node.ImplPtr = de->Inode;
194
195         RAMFS_int_RefFile(de->Inode);
196
197         // TODO: Lock?
198         if(dir->FirstEnt)
199                 dir->LastEnt->Next = de;
200         else
201                 dir->FirstEnt = de;
202         dir->LastEnt = de;
203
204         return &de->Inode->Node;
205 }
206
207 int RAMFS_Link(tVFS_Node *DirNode, const char *Name, tVFS_Node *FileNode)
208 {
209         tRAMFS_Dir      *dir = DirNode->ImplPtr;
210         tRAMFS_DirEnt   *dp;
211
212         for( dp = dir->FirstEnt; dp; dp = dp->Next )
213         {
214                 if( strcmp(dp->Name, Name) == 0 )
215                         return -1;
216         }
217         
218         dp = malloc( sizeof(tRAMFS_DirEnt) + strlen(Name) + 1 );
219         dp->Next = NULL;
220         dp->Inode = FileNode->ImplPtr;
221         RAMFS_int_RefFile(dp->Inode);
222         strcpy(dp->Name, Name);
223
224         // TODO: Lock?  
225         if(dir->FirstEnt)
226                 dir->LastEnt->Next = dp;
227         else
228                 dir->FirstEnt = dp;
229         dir->LastEnt = dp;
230         
231         return 0;
232 }
233
234 int RAMFS_Unlink(tVFS_Node *Node, const char *Name)
235 {
236         tRAMFS_Dir      *dir = Node->ImplPtr;
237         tRAMFS_DirEnt   *dp, *p = NULL;
238
239         // TODO: Is locking needed?
240
241         // Find the directory entry
242         for( dp = dir->FirstEnt; dp; p = dp, dp = dp->Next )
243         {
244                 if( strcmp(dp->Name, Name) == 0 )
245                         break ;
246         }
247         if( !dp )       return -1;
248         
249         // Dereference the file
250         RAMFS_int_DerefFile( dp->Inode );
251
252         // Remove and free directory entry
253         if(!p)
254                 dir->FirstEnt = dp->Next;
255         else
256                 p->Next = dp->Next;
257         if( dir->LastEnt == dp )
258                 dir->LastEnt = p;
259         free(dp);
260
261         return 0;
262 }
263
264 // --- Files ---
265 size_t RAMFS_int_DoIO(tRAMFS_File *File, off_t Offset, size_t Size, void *Buffer, int bRead)
266 {
267         size_t  rem;
268         Uint8   *page_virt;
269          int    page;
270
271         if( Offset >= File->Size )      return 0;
272         
273         if( Offset + Size > File->Size )        Size = File->Size - Size;
274
275         if( Size == 0 )         return 0;
276
277         page = Offset / PAGE_SIZE;
278         Offset %= PAGE_SIZE;
279         rem = Size;
280
281         page_virt = RAMFS_int_GetPage(File, page++, !bRead);
282         if(!page_virt)  return 0;
283         
284         if( Offset + Size <= PAGE_SIZE )
285         {
286                 if( bRead )
287                         memcpy(Buffer, page_virt + Offset, Size);
288                 else
289                         memcpy(page_virt + Offset, Buffer, Size);
290                 return 0;
291         }
292         
293         if( bRead )
294                 memcpy(Buffer, page_virt + Offset, PAGE_SIZE - Offset);
295         else
296                 memcpy(page_virt + Offset, Buffer, PAGE_SIZE - Offset);
297         Buffer += PAGE_SIZE - Offset;
298         rem -= PAGE_SIZE - Offset;
299         
300         while( rem >= PAGE_SIZE )
301         {
302                 RAMFS_int_DropPage(page_virt);
303                 page_virt = RAMFS_int_GetPage(File, page++, !bRead);
304                 if(!page_virt)  return Size - rem;
305                 
306                 if( bRead )
307                         memcpy(Buffer, page_virt, PAGE_SIZE);
308                 else
309                         memcpy(page_virt, Buffer, PAGE_SIZE);
310                 
311                 Buffer += PAGE_SIZE;
312                 rem -= PAGE_SIZE;
313         }
314
315         if( rem > 0 )
316         {
317                 page_virt = RAMFS_int_GetPage(File, page, !bRead);
318                 if(!page_virt)  return Size - rem;
319                 if( bRead )
320                         memcpy(Buffer, page_virt, rem);
321                 else
322                         memcpy(page_virt, Buffer, rem);
323         }
324
325         RAMFS_int_DropPage(page_virt);
326         
327         return Size;
328 }
329
330 size_t RAMFS_Read(tVFS_Node *Node, off_t Offset, size_t Size, void *Buffer, Uint Flags)
331 {
332         tRAMFS_File     *file = Node->ImplPtr;
333         
334         return RAMFS_int_DoIO(file, Offset, Size, Buffer, 1);
335 }
336
337 size_t RAMFS_Write(tVFS_Node *Node, off_t Offset, size_t Size, const void *Buffer, Uint Flags)
338 {
339         tRAMFS_File     *file = Node->ImplPtr;
340         
341         // TODO: Limit checks?
342         if( Offset == file->Size )
343                 file->Size += Size;
344
345         return RAMFS_int_DoIO(file, Offset, Size, (void*)Buffer, 0);
346 }
347
348 // --- Internals ---
349 void *RAMFS_int_GetPage(tRAMFS_File *File, int Page, int bCanAlloc)
350 {
351         Uint32  page_id = 0;
352         Uint32  *page_in_1 = NULL;
353         Uint32  *page_in_2 = NULL;
354
355          int    ofs = 0, block;
356
357         if( Page < 0 )  return NULL;
358         
359         if( Page < RAMFS_NDIRECT )
360         {
361                 if( File->PagesDirect[Page] )
362                         page_id = File->PagesDirect[Page];
363         }
364         else if( Page - RAMFS_NDIRECT < PAGE_SIZE/4 )
365         {
366                 ofs = Page - RAMFS_NDIRECT;
367                 if( File->Indirect1Page == 0 ) {
368                         if( !bCanAlloc )
369                                 return NULL;
370                         else
371                                 File->Indirect1Page = RAMFS_int_AllocatePage(File->Inode.Disk) + 1;
372                 }
373                 page_in_1 = MM_MapTemp( File->Inode.Disk->PhysPages[File->Indirect1Page-1] );
374                 page_id = page_in_1[ofs];
375         }
376         else if( Page - RAMFS_NDIRECT - PAGE_SIZE/4 < (PAGE_SIZE/4)*(PAGE_SIZE/4) )
377         {
378                 block = (Page - RAMFS_NDIRECT - PAGE_SIZE/4) / (PAGE_SIZE/4);
379                 ofs   = (Page - RAMFS_NDIRECT - PAGE_SIZE/4) % (PAGE_SIZE/4);
380                 if( File->Indirect2Page == 0 ){
381                         if( !bCanAlloc )
382                                 return NULL;
383                         else
384                                 File->Indirect2Page = RAMFS_int_AllocatePage(File->Inode.Disk) + 1;
385                 }
386
387                 page_in_2 = MM_MapTemp( File->Inode.Disk->PhysPages[File->Indirect2Page-1] );
388                 if( page_in_2[block] == 0 ) {
389                         if( !bCanAlloc )
390                                 return NULL;
391                         else
392                                 page_in_2[block] = RAMFS_int_AllocatePage(File->Inode.Disk) + 1;
393                 }
394
395                 page_in_1 = MM_MapTemp( File->Inode.Disk->PhysPages[page_in_2[block] - 1] );
396                 page_id = page_in_1[ofs];
397         }
398
399         if( page_id == 0 )
400         {
401                 if( !bCanAlloc )
402                         return NULL;
403                 
404                 page_id = RAMFS_int_AllocatePage(File->Inode.Disk) + 1;
405                 if(page_in_1)
406                         page_in_1[ofs] = page_id;
407                 else
408                         File->PagesDirect[Page] = page_id;
409         }
410         
411         MM_FreeTemp( page_in_1 );
412         MM_FreeTemp( page_in_2 );
413
414         return MM_MapTemp( File->Inode.Disk->PhysPages[page_id - 1] );
415 }
416
417 void RAMFS_int_DropPage(void *Page)
418 {
419         MM_FreeTemp( Page );
420 }
421
422 Uint32 RAMFS_int_AllocatePage(tRAMDisk *Disk)
423 {
424          int    i, j;
425
426         // Quick check
427         if( Disk->nUsedPages == Disk->MaxPages )
428                 return 0;
429
430         // Find a chunk with at least one free page
431         for( i = 0; i < Disk->MaxPages / 32; i ++ )
432         {
433                 if( Disk->Bitmap[i] != -1 )
434                         break;
435         }
436         if( i == Disk->MaxPages / 32 ) {
437                 Log_Error("RAMFS", "Bookkeeping error, count and bitmap disagree");
438                 return 0;
439         }
440
441         // Find the exact page
442         for( j = 0; j < 32; j ++ )
443         {
444                 if( !(Disk->Bitmap[i] & (1U << j)) )
445                         break ;
446         }
447         ASSERT(j < 32);
448
449         return i * 32 + j;
450 }
451
452 void RAMFS_int_RefFile(tRAMFS_Inode *Inode)
453 {
454         Inode->nLink ++;
455 }
456
457 void RAMFS_int_DerefFile(tRAMFS_Inode *Inode)
458 {
459         Inode->nLink --;
460         if( Inode->nLink >= 0 )
461                 return ;
462
463         Log_Error("RAMFS", "TODO: Clean up files when deleted");
464
465         // Need to delete file
466         switch( Inode->Type )
467         {
468         case 0: // File
469                 break ;
470         case 1: // Directory
471                 break ;
472         case 2: // Symlink
473                 break ;
474         }
475         free(Inode);
476 }

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