metalsvm/fs/initrd.c
2011-04-13 11:03:34 -07:00

343 lines
9 KiB
C

/*
* Copyright 2010 Stefan Lankes, Chair for Operating Systems,
* RWTH Aachen University
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of MetalSVM.
*/
#include <metalsvm/stdlib.h>
#include <metalsvm/stdio.h>
#include <metalsvm/string.h>
#include <metalsvm/fs.h>
#include <metalsvm/errno.h>
#include <asm/multiboot.h>
#include <asm/processor.h>
static vfs_node_t initrd_root;
#define INITRD_MAGIC_NUMBER 0x4711
/*
* Grub maps the initrd as module into the address space of the kernel.
* The module begins with the header "initrd_header_t", which describes
* the number of mounted files and the mount point. The header follows
* by the struct "initrd_file_desc_t" for each file, which will be mounted.
* This struct describes the file properties and the position in the
* module.
*/
typedef struct {
uint32_t magic;
uint32_t nfiles;
char mount_point[MAX_FNAME];
} initrd_header_t;
typedef struct {
uint32_t length;
uint32_t offset;
char fname[MAX_FNAME];
} initrd_file_desc_t;
static ssize_t initrd_read(vfs_node_t* node, uint8_t* buffer, size_t size, off_t offset)
{
uint32_t i, pos = 0, found = 0;
char* data = NULL;
block_list_t* blist = &node->block_list;
/* searching for the valid data block */
if (offset) {
pos = offset / node->block_size;
offset = offset % node->block_size;
}
do {
for(i=0; i<MAX_DATABLOCKS && !data; i++) {
if (blist->data[i]) {
found++;
if (found > pos)
data = (char*) blist->data[i];
}
}
blist = blist->next;
} while(blist && !data);
if (BUILTIN_EXPECT(!data, 0))
return 0;
/*
* If the data block is not large engough,
* we copy only the rest of the current block.
* The user has to restart the read operation
* for the next block.
*/
if (offset+size >= node->block_size)
size = node->block_size - offset;
memcpy(buffer, data + offset, size);
return size;
}
static dirent_t* initrd_readdir(vfs_node_t* node, uint32_t index)
{
uint32_t i, j, count;
dirent_t* dirent;
dir_block_t* dirblock;
block_list_t* blist = &node->block_list;
do {
for(i=0,count=0; i<MAX_DATABLOCKS; i++) {
dirblock = (dir_block_t*) blist->data[i];
for(j=0; dirblock && j<MAX_DIRENTRIES; j++) {
dirent = &dirblock->entries[j];
if (dirent->vfs_node) {
count++;
if (count > index)
return dirent;
}
}
}
blist = blist->next;
} while(blist);
return NULL;
}
static vfs_node_t* initrd_finddir(vfs_node_t* node, const char *name)
{
uint32_t i, j;
dir_block_t* dirblock;
dirent_t* dirent;
block_list_t* blist = &node->block_list;
do {
for(i=0; i<MAX_DATABLOCKS; i++) {
dirblock = (dir_block_t*) blist->data[i];
for(j=0; dirblock && j<MAX_DIRENTRIES; j++) {
dirent = &dirblock->entries[j];
if (!strncmp(dirent->name, name, MAX_FNAME))
return dirent->vfs_node;
}
}
blist = blist->next;
} while(blist);
return NULL;
}
static vfs_node_t* initrd_mkdir(vfs_node_t* node, const char* name)
{
uint32_t i, j;
dir_block_t* dir_block;
dir_block_t* tmp;
dirent_t* dirent;
vfs_node_t* new_node;
block_list_t* blist = &node->block_list;
if (BUILTIN_EXPECT(node->type != FS_DIRECTORY, 0))
return NULL;
/* exists already a entry with same name? */
if (initrd_finddir(node, name))
return NULL;
new_node = kmalloc(sizeof(vfs_node_t));
if (BUILTIN_EXPECT(!new_node, 0))
return NULL;
memset(new_node, 0x00, sizeof(vfs_node_t));
new_node->type = FS_DIRECTORY;
new_node->readdir = &initrd_readdir;
new_node->finddir = &initrd_finddir;
new_node->mkdir = &initrd_mkdir;
spinlock_init(&new_node->lock);
/* create default directory block */
dir_block = (dir_block_t*) kmalloc(sizeof(dir_block_t));
if (BUILTIN_EXPECT(!dir_block, 0))
goto out;
memset(dir_block, 0x00, sizeof(dir_block_t));
new_node->block_list.data[0] = dir_block;
strncpy(dir_block->entries[0].name, ".", MAX_FNAME);
dir_block->entries[0].vfs_node = new_node;
strncpy(dir_block->entries[1].name, "..", MAX_FNAME);
dir_block->entries[1].vfs_node = node;
do {
/* searching for a free directory block */
for(i=0; i<MAX_DATABLOCKS; i++) {
if (blist->data[i]) {
tmp = (dir_block_t*) blist->data[i];
for(j=0; j<MAX_DIRENTRIES; j++) {
dirent = &tmp->entries[j];
if (!dirent->vfs_node) {
dirent->vfs_node = new_node;
strncpy(dirent->name, name, MAX_FNAME);
return new_node;
}
}
}
}
/* if all blocks are reserved, we have to allocate a new one */
if (!blist->next) {
blist->next = (block_list_t*) kmalloc(sizeof(block_list_t));
if (blist->next)
memset(blist->next, 0x00, sizeof(block_list_t));
}
blist = blist->next;
} while(blist);
kfree(dir_block, sizeof(dir_block_t));
out:
kfree(new_node, sizeof(vfs_node_t));
return NULL;
}
int initrd_init(void)
{
dir_block_t* dir_block;
vfs_node_t* tmp;
#if defined(CONFIG_ROCKCREEK) || defined(CONFIG_MULTIBOOT)
uint32_t i, j, k, l;
#endif
#ifdef CONFIG_MULTIBOOT
uint32_t mods_count = 0;
multiboot_module_t* mmodule = NULL;
if (mb_info && (mb_info->flags & MULTIBOOT_INFO_MODS)) {
mmodule = (multiboot_module_t*) mb_info->mods_addr;
mods_count = mb_info->mods_count;
}
#endif
/* Initialize the root directory. */
fs_root = &initrd_root;
memset(&initrd_root, 0x00, sizeof(vfs_node_t));
initrd_root.type = FS_DIRECTORY;
initrd_root.readdir = &initrd_readdir;
initrd_root.finddir = &initrd_finddir;
initrd_root.mkdir = &initrd_mkdir;
spinlock_init(&initrd_root.lock);
/* create default directory block */
dir_block = (dir_block_t*) kmalloc(sizeof(dir_block_t));
if (BUILTIN_EXPECT(!dir_block, 0))
return -ENOMEM;
memset(dir_block, 0x00, sizeof(dir_block_t));
initrd_root.block_list.data[0] = dir_block;
strncpy(dir_block->entries[0].name, ".", MAX_FNAME);
dir_block->entries[0].vfs_node = fs_root;
strncpy(dir_block->entries[1].name, "..", MAX_FNAME);
dir_block->entries[1].vfs_node = fs_root;
/* create the directory bin and dev */
mkdir_fs(fs_root, "bin");
mkdir_fs(fs_root, "sbin");
mkdir_fs(fs_root, "var");
tmp = mkdir_fs(fs_root, "dev");
/* create the character device "null" */
null_init(tmp, "null");
/* For every module.. */
#ifdef CONFIG_MULTIBOOT
for(i=0; i<mods_count; i++) {
initrd_header_t* header = (initrd_header_t*) mmodule[i].mod_start;
#elif defined(CONFIG_ROCKCREEK)
for(i=0; i<1; i++) {
initrd_header_t* header = (initrd_header_t*) bootinfo->addr;
#endif
initrd_file_desc_t* file_desc;
vfs_node_t* new_node;
if (header->magic != INITRD_MAGIC_NUMBER) {
kprintf("Invalid magic number for a init ram disk\n");
continue;
}
tmp = findnode_fs(header->mount_point);
if (BUILTIN_EXPECT(!tmp, 0)) {
kprintf("Did not found mount point %s.\n", header->mount_point);
continue;
}
if (BUILTIN_EXPECT(tmp->type != FS_DIRECTORY, 0)) {
kprintf("%s is not a valid mount point.\n", header->mount_point);
continue;
}
file_desc = (initrd_file_desc_t*) (header + 1);
for(j=0; j<header->nfiles; j++) {
block_list_t* blist;
dir_block_t* dir_block;
dirent_t* dirent;
if (finddir_fs(tmp, file_desc->fname)) {
kprintf("Error: %s alreay exits\n", file_desc->fname);
goto next_file;
}
/* create a new node and map the module as data block */
new_node = kmalloc(sizeof(vfs_node_t));
if (BUILTIN_EXPECT(!new_node, 0)) {
kprintf("Not enough memory to create new initrd node\n");
goto next_file;
}
memset(new_node, 0x00, sizeof(vfs_node_t));
new_node->type = FS_FILE;
new_node->read = initrd_read;
new_node->block_size = file_desc->length;
new_node->block_list.data[0] = ((char*) header) + file_desc->offset;
spinlock_init(&new_node->lock);
/* create a entry for the new node in the directory block of current node */
blist = &tmp->block_list;
do {
for(k=0; k<MAX_DATABLOCKS; k++) {
if (blist->data[k]) {
dir_block = (dir_block_t*) blist->data[k];
for(l=0; l<MAX_DIRENTRIES; l++) {
dirent = &dir_block->entries[l];
if (!dirent->vfs_node) {
dirent->vfs_node = new_node;
strncpy(dirent->name, file_desc->fname, MAX_FNAME);
goto next_file;
}
}
}
}
/* if all blocks are reserved, we have to allocate a new one */
if (!blist->next) {
blist->next = (block_list_t*) kmalloc(sizeof(block_list_t));
if (blist->next)
memset(blist->next, 0x00, sizeof(block_list_t));
}
blist = blist->next;
} while(blist);
next_file:
file_desc++;
}
#if defined(CONFIG_ROCKCREEK) || defined(CONFIG_MULTIBOOT)
}
#endif
return 0;
}