root / Whitix / branches / hybrid / fs / devfs.c

Revision 524, 11.6 kB (checked in by mwhitworth, 5 months ago)

Add DevMountRoot code to devfs (temporarily). Update for new APIs.

Line 
1/*  This file is part of Whitix.
2 *
3 *  Whitix is free software; you can redistribute it and/or modify
4 *  it under the terms of the GNU General Public License as published by
5 *  the Free Software Foundation; either version 2 of the License, or
6 *  (at your option) any later version.
7 *
8 *  Whitix is distributed in the hope that it will be useful,
9 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 *  GNU General Public License for more details.
12 *
13 *  You should have received a copy of the GNU General Public License
14 *  along with Whitix; if not, write to the Free Software
15 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16 *
17 */
18
19/* devfs.c
20 *
21 * Implements the fully dynamic device filesystem and redirects I/O requests to the appropriate device. A fully dynamic
22 * device file-system. At startup, each device registers itself with the device file-system, and the fs keeps track of
23 * read, write and ioctl functions for each device. But this is hidden from the user, because it is mounted at DEVICES_PATH
24*/
25
26#include <console.h>
27#include <llist.h>
28#include <typedefs.h>
29#include <fs/vfs.h>
30#include <fs/devfs.h>
31#include <malloc.h>
32#include <init.h>
33#include <error.h>
34#include <module.h>
35
36/* Implement proper ramdisk support and remove. */
37#include "../devices/storage/ata/ata.h"
38
39/* Local prototypes */
40int DevFsDirSize(struct DevFsDir* dir);
41
42/* Root directory of the device filesystem. */
43struct DevFsDir* rootDir;
44struct DevFsEntry rootEntry;
45
46struct SuperBlockOps devFsSbOps;
47struct VNodeOps devFsVOps;
48struct FileOps devFsFileOps;
49
50int DevFsBlockRead(struct File* file,BYTE* buffer,DWORD size);
51int DevFsBlockWrite(struct File* file,BYTE* buffer,DWORD size);
52int DevFsBlockIoCtl(struct File* file,unsigned long code,char* data);
53
54/* Generic operations for block devices */
55struct FileOps devFsBlockOps={
56        .read = DevFsBlockRead,
57        .write = DevFsBlockWrite,
58        .ioctl= DevFsBlockIoCtl
59};
60
61int DevFsBlockRead(struct File* file,BYTE* buffer,DWORD size)
62{
63        int readSize,readOffset,copyOffset=0;
64        struct Buffer* buff;
65        struct StorageDevice* sDevice;
66
67        /* Get it from ops */
68        sDevice=(struct StorageDevice*)DeviceGetOps(file->vNode);
69
70        while (size > 0)
71        {
72                readOffset=(file->position % sDevice->softBlockSize);
73                readSize=MIN(sDevice->softBlockSize-readOffset,size);
74
75                buff=BlockRead(sDevice,file->position/sDevice->softBlockSize);
76
77                /* Might be possible, if file->position > number of sectors */
78                if (!buff)
79                        return -EIO;
80
81                memcpy(buffer+copyOffset,buff->data+readOffset,readSize);
82                size-=readSize;
83                copyOffset+=readSize;
84                file->position+=readSize;
85        }
86
87        return copyOffset;
88}
89
90int DevFsBlockWrite(struct File* file,BYTE* buffer,DWORD size)
91{
92        int writeSize,writeOffset,copyOffset=0;
93        struct Buffer* buff;
94        struct StorageDevice* sDevice;
95
96        /* Get it from ops */
97        sDevice=(struct StorageDevice*)DeviceGetOps(file->vNode);
98
99        while (size > 0)
100        {
101                writeOffset=(file->position % sDevice->softBlockSize);
102                writeSize=MIN(sDevice->softBlockSize-writeOffset,size);
103
104                buff=BlockRead(sDevice,file->position/sDevice->softBlockSize);
105
106                /* Might be possible, if file->position > number of sectors */
107                if (!buff)
108                        return -EIO;
109
110                memcpy((char*)(buff->data+writeOffset),(char*)(buffer+copyOffset),writeSize);
111                BlockWrite(sDevice,buff);
112
113                size-=writeSize;
114                copyOffset+=writeSize;
115                file->position+=writeSize;
116        }
117
118        return copyOffset;
119}
120
121int DevFsBlockIoCtl(struct File* file,unsigned long code,char* data)
122{
123        return StorageIoCtl((struct StorageDevice*)DeviceGetOps(file->vNode),code,data);
124}
125
126int DevFsReadVNode(struct VNode* vNode)
127{
128        struct DevFsEntry* entry=(struct DevFsEntry*)(vNode->id);
129        struct StorageDevice* sDevice;
130
131        vNode->extraInfo=entry;
132        vNode->vNodeOps=&devFsVOps;
133
134        vNode->mode=VFS_ATTR_READ | VFS_ATTR_WRITE;
135
136        if (entry->type & VFS_ATTR_FILE)
137        {
138                vNode->mode |= VFS_ATTR_FILE;
139                vNode->devId.major=entry->device.devId.major;
140                vNode->devId.minor=entry->device.devId.minor;
141                if (entry->type & DEVICE_BLOCK)
142                {
143                        vNode->fileOps=&devFsBlockOps;
144                        vNode->mode |= VFS_ATTR_BLOCK; 
145               
146                        /* Get the total size of the block device. */
147                        sDevice=(struct StorageDevice*)DeviceGetOps(vNode);
148                        vNode->size=sDevice->blockSize*sDevice->totalSectors;
149                }else if (entry->type & DEVICE_CHAR)
150                {
151                        vNode->mode |= VFS_ATTR_CHAR;
152                        /* Just redirect to the usual char devices */
153                        vNode->fileOps=entry->device.ops;
154                        vNode->size=0;
155                }
156        }else if (entry->type & VFS_ATTR_DIR)
157        {
158                vNode->fileOps=&devFsFileOps;
159                vNode->mode|=VFS_ATTR_DIR;
160                vNode->size=DevFsDirSize(&entry->dir);
161        }
162
163        return 0;
164}
165
166int DevFsFreeVNode(struct VNode* vNode)
167{
168        /* This function is so that extraInfo (which contains the DevFsEntry and could be
169        malloc'ed) is not freed by the virtual filesystem layer. */
170        return 0;
171}
172
173int DevFsLookup(struct VNode** retVal,struct VNode* dir,char* name,int nameLength)
174{
175        struct DevFsDir* dDir;
176        struct DevFsEntry* entry;
177
178        entry=(struct DevFsEntry*)(dir->extraInfo);
179        dDir=&entry->dir;
180
181        /* Handle '..'. '..' on the root directory will already have been handled by the VFS. */
182        if (nameLength == 2 && name[0] == '.' && name[1] == '.')
183        {
184                dDir=dDir->parent;
185                entry=(struct DevFsEntry*)(((DWORD)dDir)-offsetof(struct DevFsEntry, dir));
186                goto found;
187        }
188
189        ListForEachEntry(entry,&(dDir->entries),next)
190                if (!strnicmp(entry->name,name,nameLength))
191                        goto found;
192
193        /* Cycled through the whole directory with no luck */
194        return -ENOENT;
195
196found:
197        *retVal=VNodeGet(dir->superBlock,(DWORD)entry);
198        return 0;
199}
200
201struct DevFsDir* DevDirLookup(char* name,int nameLength,struct DevFsDir* dir)
202{
203        struct DevFsEntry* entry;
204       
205        /* Look it up */
206        ListForEachEntry(entry,&(dir->entries),next)
207        {
208                if ((entry->type & VFS_ATTR_DIR) && !strnicmp(entry->name, name, nameLength))
209                        return &entry->dir;
210        }       
211       
212        return NULL;
213}
214
215int DevAddDevice(char* name, WORD major, WORD minor, int type, void* ops)
216{
217        struct DevFsEntry* entry;
218        struct DevFsDevice* device;
219        struct DevFsDir* dir=rootDir;
220        char* endName;
221        DWORD offset;
222
223        /* Sanity checks */
224        if (!name || !ops)
225                return -EFAULT;
226
227        if (!major)
228                return -EINVAL;
229
230        /* Get to the directory first. */
231        while ((endName=strchr(name,'/')))
232        {
233                offset=(DWORD)(endName-name);
234                dir=DevDirLookup(name, offset,dir);
235                if (!dir)
236                        return -ENOENT;
237                       
238                name+=offset+1;
239        }
240
241//      printf("DevAddDevice(%s)\n", name);
242
243        /* Check if the name exists already */
244        ListForEachEntry(entry,&(dir->entries),next)
245                if (!strnicmp(entry->name,name,strlen(name)))
246                        return -EEXIST;
247
248        /* Allocate the DevFsEntry, and the name with it */
249        entry=(struct DevFsEntry*)malloc(sizeof(struct DevFsEntry)+strlen(name)+1);
250        if (!entry)
251                return -ENOMEM;
252
253        /* Always a device */
254        entry->type=type | VFS_ATTR_FILE | VFS_ATTR_READ | VFS_ATTR_WRITE;
255        strcpy(entry->name,name);
256       
257        device=(struct DevFsDevice*)(&entry->device);
258       
259        device->ops=ops;
260        device->devId.major=major;
261        device->devId.minor=minor;
262        ListAddTail(&entry->next,&dir->entries);
263
264        return 0;
265}
266
267SYMBOL_EXPORT(DevAddDevice);
268
269/* Find root block device by major/minor number. Used in the ISO boot, which
270 * will soon be replaced with a init ramdisk. */
271
272struct StorageDevice* DevFindRootDev(WORD major,WORD minor)
273{
274        /* Any storage devices will be found in the Storage directory. */
275        struct DevFsDir* dir;
276        struct DevFsEntry* entry;
277       
278        dir=DevDirLookup("Storage", strlen("Storage"), rootDir);
279       
280        if (!dir)
281                return NULL;
282       
283        ListForEachEntry(entry,&(dir->entries), next)
284        {
285                if (entry->type & VFS_ATTR_FILE)
286                {
287                        if (entry->device.devId.major == major && entry->device.devId.minor == minor && (entry->type & DEVICE_BLOCK))
288                                return (struct StorageDevice*)(entry->device.ops);
289                }
290        }
291
292        return NULL;
293}
294
295/* FIXME: Must be a better way to handle this. */
296struct StorageDevice* DevGetCdDevice()
297{
298        int i;
299        struct StorageDevice* rootDev=NULL;
300
301        /* Temporary hack for live cd */
302        for (i=0; i<256; i+=64)
303        {
304                struct AtaDrive* drive;
305                rootDev=DevFindRootDev(4,i);
306                if (!rootDev)
307                        continue;
308
309                drive=(struct AtaDrive*)(rootDev->priv);
310
311                if (drive->type & ATA_REMOVABLE)
312                        break;
313        }
314
315        /* Didn't find it */
316        if (i == 256)
317                rootDev=NULL;
318
319        return rootDev;
320}
321
322void DevMountRoot(BYTE biosDriveNo, WORD rootDevMajor, WORD rootDevMinor)
323{
324        struct StorageDevice* rootDev=NULL;
325
326        /* For ISO filesystems, have a ramdisk - makes life a lot easier */
327        if (biosDriveNo > 0x80 && rootDevMajor == 4)
328                rootDev=DevGetCdDevice();
329        else
330                /* Decode root filesystem here */
331                rootDev=DevFindRootDev(rootDevMajor,rootDevMinor);
332
333        if (!rootDev)
334        {
335                KePrint("Device major:%d, minor: %d not valid root device.\n",(int)rootDevMajor,(int)rootDevMinor);
336                cli();
337                hlt();
338        }
339
340        return VfsMountRoot(rootDev);
341}
342
343SYMBOL_EXPORT(DevMountRoot);
344
345int DevAddDir(char* name)
346{
347        char* endName;
348        DWORD offset;
349        struct DevFsDir* dir=rootDir, *newDir;
350        struct DevFsEntry* entry;       
351       
352        /* Get to the directory */
353        while ((endName=strchr(name,'/')))
354        {
355                offset=(DWORD)(endName-name);
356                dir=DevDirLookup(name, strlen(name),dir);
357                if (!dir)
358                        return -ENOENT;
359        }
360       
361        /* Check it doesn't exist already. */
362        ListForEachEntry(entry,&(dir->entries),next)
363                if (!strnicmp(entry->name,name,strlen(name)))
364                        return -EEXIST;
365       
366        /* Add entry. */
367        entry=(struct DevFsEntry*)malloc(sizeof(struct DevFsEntry)+strlen(name)+1);
368        entry->type=VFS_ATTR_DIR | VFS_ATTR_READ;
369        strcpy(entry->name, name);
370       
371        newDir=&entry->dir;
372        INIT_LIST_HEAD(&newDir->entries);
373        newDir->parent=dir;
374       
375        ListAddTail(&entry->next,&dir->entries);
376        return 0;
377}
378
379int DevRemoveDevice(struct DevFsEntry* device)
380{
381        KePrint("DevRemoveDevice\n");
382        return 0;
383}
384
385int DevFsPermission(struct VNode* node,int access)
386{
387        KePrint("DevFsPermission\n");
388        return 0;
389}
390
391int DevFsReadDir(struct File* file,void* dirEntries)
392{
393        struct DevFsEntry* entry;
394        struct DevFsEntry* entryDir=(struct DevFsEntry*)(file->vNode->extraInfo);
395        struct DevFsDir* dir=&entryDir->dir;
396        int ret;
397
398        ListForEachEntry(entry, &(dir->entries), next)
399        {
400                ret=FillDir(dirEntries,entry->name,strlen(entry->name),(DWORD)entry);
401                if (ret < 0)
402                        return ret;
403
404                file->position++;
405        }
406
407        return 0;
408}
409
410struct SuperBlockOps devFsSbOps=
411{
412        .readVNode = DevFsReadVNode,
413        .freeVNode = DevFsFreeVNode,
414};
415
416struct VNodeOps devFsVOps=
417{
418        .lookup=DevFsLookup,
419        .permission=DevFsPermission,
420};
421
422/* Only for devfs directories */
423struct FileOps devFsFileOps=
424{
425        .readDir=DevFsReadDir,
426};
427
428int DevFsDirSize(struct DevFsDir* dir)
429{
430        struct DevFsEntry* entry;
431        int i=0;
432
433        ListForEachEntry(entry, &(dir->entries), next)
434                i++;
435
436        return i;
437}
438
439struct VfsSuperBlock* DevFsReadSuper(struct StorageDevice* dev,int flags,char* data)
440{
441        struct VfsSuperBlock* retVal;
442
443        /* Should not be mounted on a device */
444        if (dev)
445                return NULL;
446
447        retVal=VfsAllocSuper(NULL,0);
448        if (!retVal)
449                return NULL;
450
451        retVal->sbOps=&devFsSbOps;
452        retVal->mount=VNodeGet(retVal,(DWORD)&rootEntry);
453        retVal->mount->mode=VFS_ATTR_DIR | VFS_ATTR_READ;
454        retVal->mount->size=DevFsDirSize(rootDir);
455        retVal->mount->vNodeOps=&devFsVOps;
456        retVal->mount->fileOps=&devFsFileOps;
457        retVal->mount->extraInfo=(void*)&rootEntry;
458
459        return retVal;
460}
461
462static struct FileSystem devFileSystem=
463{
464        .name = "DevFs",
465        .readSuper = DevFsReadSuper,
466};
467
468int DevFsInit()
469{
470        /* Set up the data structure here, and DevFsReadSuper doesn't do too much (at the moment) */
471        VfsRegisterFileSystem(&devFileSystem);
472
473        /* Set up the root directory here, as devices will be added before ReadSuper is called. */
474        rootEntry.type=VFS_ATTR_DIR | VFS_ATTR_READ | VFS_ATTR_WRITE;
475        rootEntry.name[0]='\0';
476        rootEntry.dir.parent=NULL; /* Parent is handled by VFS for the root directory. */
477        INIT_LIST_HEAD(&rootEntry.dir.entries);
478        rootDir=&rootEntry.dir;
479
480        /* Add standard directories */
481        DevAddDir("Consoles");
482        DevAddDir("Special");
483        DevAddDir("Storage");
484        DevAddDir("Input");
485
486        return 0;
487}
488
489void DevFsExit()
490{
491        VfsDeregisterFileSystem(&devFileSystem);
492}
493
494FsInit(DevFsInit);
Note: See TracBrowser for help on using the browser.