/**
* @file initrd.c
* Filesystem module for handling the initrd
*
* Copyright (C) 2018 Clyne Sullivan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
typedef struct {
char *address;
uint32_t size;
} initrd_info;
// Defined by linker script
extern uint8_t _binary_initrd_img_start[];
extern uint8_t _binary_initrd_img_size[];
static const uint8_t *initrd_start = (uint8_t *)_binary_initrd_img_start;
static const uint32_t initrd_size = (uint32_t)_binary_initrd_img_size;
void *initrd_open(const char *file);
uint32_t initrd_read(vfs_file_info *info, uint32_t count, uint8_t *buffer);
int initrd_close(vfs_file_info *info);
int initrd_seek(vfs_file_info *info, uint32_t offset, int whence);
/**
* Gets the start of the offset-th file in the initrd.
* @return Pointer to the file, NULL otherwise
*/
char *initrd_getfile(uint32_t offset);
static const vfs_volume_funcs initrd_funcs = {
initrd_open,
initrd_close,
initrd_read,
0, // write
0, // readdir
initrd_seek
};
/**
* Custom strncmp() implementation used by this driver.
*/
int initrd_strncmp(const char *a, const char *b, unsigned int n)
{
for (unsigned int i = 0; i < n; i++) {
if (a[i] != b[i])
return 1;
}
return 0;
}
void initrd_init(void)
{
vfs_mount(&initrd_funcs, VFS_READONLY);
}
void *initrd_open(const char *file)
{
// Iterate through all files in the initrd
char *ptr;
for (uint32_t i = 0; ptr = initrd_getfile(i), ptr != 0; i++) {
uint32_t len = *((uint32_t *)ptr);
if (!initrd_strncmp(file, ptr + 4, len)) {
// If we found our file, create an info structure and return it
initrd_info *file = (initrd_info *)malloc(
sizeof(initrd_info));
file->address = ptr + len + 8;
file->size = *(uint32_t *)(ptr + len + 4);
return file;
}
}
return 0;
}
int initrd_close(vfs_file_info *info)
{
free(info->fsinfo);
return 0;
}
uint32_t initrd_read(vfs_file_info *info, uint32_t count, uint8_t *buffer)
{
// Confirm this is a valid file
initrd_info *iinfo = (initrd_info *)info->fsinfo;
if (iinfo == 0 || iinfo->address == 0)
return 0;
// Attempt to read 'count' bytes, breaking if we reach the end of the file
uint32_t i;
for (i = 0; i < count; i++) {
if (info->pos >= iinfo->size)
break;
buffer[info->pos] = iinfo->address[info->pos];
info->pos++;
}
return i;
}
int initrd_seek(vfs_file_info *info, uint32_t offset, int whence)
{
initrd_info *iinfo = (initrd_info *)info->fsinfo;
if (iinfo == 0 || iinfo->address == 0)
return 0;
if (whence == SEEK_SET)
info->pos = offset;
else if (whence == SEEK_CUR)
info->pos += offset;
else if (whence == SEEK_END)
info->pos = iinfo->size + offset;
else
return -1;
return 0;
}
char *initrd_getfile(uint32_t offset)
{
char *ptr = (char *)initrd_start;
for (uint32_t i = 0; i < offset; i++) {
// Move to the next file
uint32_t len = *((uint32_t *)ptr);
uint32_t datalen = *((uint32_t *)(ptr + 4 + len));
ptr += len + datalen + 8;
if (ptr >= (char *)(initrd_start + initrd_size))
return 0;
}
return ptr;
}