9
0
Fork 0
barebox/fs/ramfs.c

648 lines
13 KiB
C
Raw Normal View History

2007-07-05 16:02:19 +00:00
/*
* ramfs.c - a malloc based filesystem
*
* Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* 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.
*
*/
2007-07-05 16:01:37 +00:00
#include <common.h>
#include <driver.h>
#include <init.h>
#include <malloc.h>
#include <fs.h>
#include <command.h>
#include <errno.h>
#include <linux/stat.h>
2007-07-05 16:01:43 +00:00
#include <xfuncs.h>
2007-07-05 16:01:37 +00:00
#define CHUNK_SIZE (4096 * 2)
2007-07-05 16:01:38 +00:00
2007-07-05 16:01:38 +00:00
struct ramfs_chunk {
2007-07-05 16:01:37 +00:00
char *data;
2007-07-05 16:01:38 +00:00
struct ramfs_chunk *next;
2007-07-05 16:01:37 +00:00
};
2007-07-05 16:01:38 +00:00
struct ramfs_inode {
2007-07-05 16:01:37 +00:00
char *name;
2007-07-05 16:01:43 +00:00
struct ramfs_inode *parent;
2007-07-05 16:01:38 +00:00
struct ramfs_inode *next;
struct ramfs_inode *child;
char *symlink;
2007-07-05 16:01:37 +00:00
ulong mode;
struct handle_d *handle;
2007-07-05 16:01:38 +00:00
ulong size;
2007-07-05 16:01:38 +00:00
struct ramfs_chunk *data;
/* Points to recently used chunk */
int recent_chunk;
struct ramfs_chunk *recent_chunkp;
2007-07-05 16:01:37 +00:00
};
struct ramfs_priv {
2007-07-05 16:01:38 +00:00
struct ramfs_inode root;
2007-07-05 16:01:37 +00:00
};
/* ---------------------------------------------------------------*/
2007-09-28 08:07:26 +00:00
static struct ramfs_inode * lookup(struct ramfs_inode *node, const char *name)
2007-07-05 16:01:37 +00:00
{
debug("lookup: %s in %p\n",name, node);
2007-10-03 17:57:46 +00:00
if(!S_ISDIR(node->mode))
2007-07-05 16:01:38 +00:00
return NULL;
2007-07-05 16:01:37 +00:00
node = node->child;
2007-07-05 16:01:38 +00:00
if (!node)
2007-07-05 16:01:37 +00:00
return NULL;
while (node) {
debug("lookup: %s\n", node->name);
2007-07-05 16:01:43 +00:00
if (!strcmp(node->name, name)) {
debug("lookup: found: 0x%p\n",node);
2007-07-05 16:01:37 +00:00
return node;
2007-07-05 16:01:43 +00:00
}
2007-07-05 16:01:37 +00:00
node = node->next;
}
return NULL;
}
2007-09-28 08:07:26 +00:00
static struct ramfs_inode* rlookup(struct ramfs_priv *priv, const char *path)
2007-07-05 16:01:37 +00:00
{
2007-07-05 16:01:43 +00:00
struct ramfs_inode *node = &priv->root;
2007-07-05 16:01:37 +00:00
static char *buf;
char *part;
debug("rlookup %s in %p\n",path, node);
2007-07-05 16:01:37 +00:00
buf = strdup(path);
part = strtok(buf, "/");
if (!part)
goto out;
do {
2007-07-05 16:01:43 +00:00
node = lookup(node, part);
2007-07-05 16:01:37 +00:00
if (!node)
goto out;
part = strtok(NULL, "/");
} while(part);
out:
free(buf);
return node;
}
2007-09-28 08:07:26 +00:00
static struct ramfs_inode* rlookup_parent(struct ramfs_priv *priv, const char *pathname, char **file)
2007-07-05 16:01:43 +00:00
{
char *path;
struct ramfs_inode *node;
pathname++;
path = strdup(pathname);
if ((*file = strrchr((char *) pathname, '/'))) {
2007-07-05 16:01:43 +00:00
char *tmp;
(*file)++;
tmp = strrchr(path, '/');
*tmp = 0;
node = rlookup(priv, path);
if (!node) {
errno = -ENOENT;
goto out;
}
} else {
*file = (char *)pathname;
node = &priv->root;
}
out:
free(path);
return node;
}
static int chunks = 0;
2007-09-28 08:07:26 +00:00
static struct ramfs_chunk *ramfs_get_chunk(void)
2007-07-05 16:01:38 +00:00
{
struct ramfs_chunk *data = malloc(sizeof(struct ramfs_chunk));
if (!data)
return NULL;
2007-07-05 16:01:38 +00:00
data->data = malloc(CHUNK_SIZE);
if (!data->data) {
free(data);
return NULL;
}
2007-07-05 16:01:38 +00:00
data->next = NULL;
2007-07-05 16:01:43 +00:00
chunks++;
2007-07-05 16:01:38 +00:00
return data;
}
2007-09-28 08:07:26 +00:00
static void ramfs_put_chunk(struct ramfs_chunk *data)
2007-07-05 16:01:38 +00:00
{
free(data->data);
free(data);
2007-07-05 16:01:43 +00:00
chunks--;
2007-07-05 16:01:38 +00:00
}
2007-07-05 16:01:43 +00:00
static struct ramfs_inode* ramfs_get_inode(void)
2007-07-05 16:01:37 +00:00
{
2007-07-05 16:01:43 +00:00
struct ramfs_inode *node = xzalloc(sizeof(struct ramfs_inode));
return node;
}
static void ramfs_put_inode(struct ramfs_inode *node)
{
struct ramfs_chunk *data = node->data;
while (data) {
struct ramfs_chunk *tmp = data->next;
ramfs_put_chunk(data);
data = tmp;
}
free(node->symlink);
2007-07-05 16:01:43 +00:00
free(node->name);
free(node);
}
2007-09-28 08:07:26 +00:00
static struct ramfs_inode* node_insert(struct ramfs_inode *parent_node, const char *filename, ulong mode)
2007-07-05 16:01:43 +00:00
{
struct ramfs_inode *node, *new_node = ramfs_get_inode();
2007-07-05 16:01:37 +00:00
new_node->name = strdup(filename);
new_node->mode = mode;
2007-07-05 16:01:43 +00:00
node = parent_node->child;
2007-10-03 17:57:46 +00:00
if (S_ISDIR(mode)) {
2007-07-05 16:01:43 +00:00
struct ramfs_inode *n = ramfs_get_inode();
n->name = strdup(".");
2007-10-03 17:57:46 +00:00
n->mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
2007-07-05 16:01:43 +00:00
n->child = n;
n->parent = new_node;
new_node->child = n;
n = ramfs_get_inode();
n->name = strdup("..");
2007-10-03 17:57:46 +00:00
n->mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
2007-07-05 16:01:43 +00:00
n->parent = new_node;
n->child = parent_node->child;
new_node->child->next = n;
2007-07-05 16:01:37 +00:00
}
while (node->next)
node = node->next;
node->next = new_node;
2007-07-05 16:01:38 +00:00
return new_node;
2007-07-05 16:01:37 +00:00
}
/* ---------------------------------------------------------------*/
static int __ramfs_create(struct device_d *dev, const char *pathname,
mode_t mode, const char *symlink)
2007-07-05 16:01:37 +00:00
{
struct ramfs_priv *priv = dev->priv;
2007-07-05 16:01:38 +00:00
struct ramfs_inode *node;
2007-07-05 16:01:43 +00:00
char *file;
char *__symlink = NULL;
2007-07-05 16:01:43 +00:00
node = rlookup_parent(priv, pathname, &file);
if (!node)
return -ENOENT;
if (symlink) {
__symlink = strdup(symlink);
if (!__symlink)
return -ENOMEM;
}
node = node_insert(node, file, mode);
if (!node) {
free(__symlink);
return -ENOMEM;
}
node->symlink = __symlink;
return 0;
}
static int ramfs_create(struct device_d *dev, const char *pathname, mode_t mode)
{
return __ramfs_create(dev, pathname, mode, NULL);
2007-07-05 16:01:43 +00:00
}
2007-07-05 16:01:37 +00:00
2007-09-28 08:07:26 +00:00
static int ramfs_unlink(struct device_d *dev, const char *pathname)
2007-07-05 16:01:43 +00:00
{
struct ramfs_priv *priv = dev->priv;
struct ramfs_inode *node, *lastnode;
char *file;
2007-07-05 16:01:37 +00:00
2007-07-05 16:01:43 +00:00
node = rlookup_parent(priv, pathname, &file);
2007-07-05 16:01:37 +00:00
2007-07-05 16:01:43 +00:00
lastnode = node->child->next;
node = lastnode->next;
2007-07-05 16:01:37 +00:00
2007-07-05 16:01:43 +00:00
while (node) {
if (!strcmp(node->name, file)) {
struct ramfs_inode *tmp;
tmp = node;
lastnode->next = node->next;
ramfs_put_inode(tmp);
return 0;
}
lastnode = node;
node = node->next;
};
return -ENOENT;
2007-07-05 16:01:37 +00:00
}
2007-09-28 08:07:26 +00:00
static int ramfs_mkdir(struct device_d *dev, const char *pathname)
2007-07-05 16:01:37 +00:00
{
2007-10-04 11:15:09 +00:00
return ramfs_create(dev, pathname, S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
2007-07-05 16:01:37 +00:00
}
2007-09-28 08:07:26 +00:00
static int ramfs_rmdir(struct device_d *dev, const char *pathname)
2007-07-05 16:01:43 +00:00
{
struct ramfs_priv *priv = dev->priv;
struct ramfs_inode *node, *lastnode;
char *file;
node = rlookup_parent(priv, pathname, &file);
lastnode = node->child->next;
node = lastnode->next;
while (node) {
if (!strcmp(node->name, file)) {
struct ramfs_inode *tmp;
tmp = node;
lastnode->next = node->next;
ramfs_put_inode(tmp->child->next);
ramfs_put_inode(tmp->child);
ramfs_put_inode(tmp);
return 0;
}
lastnode = node;
node = node->next;
};
return -ENOENT;
}
2007-07-05 16:01:38 +00:00
static int ramfs_open(struct device_d *dev, FILE *file, const char *filename)
{
struct ramfs_priv *priv = dev->priv;
2007-07-05 16:01:43 +00:00
struct ramfs_inode *node = rlookup(priv, filename);
2007-07-05 16:01:38 +00:00
if (!node)
return -ENOENT;
2007-07-05 16:01:38 +00:00
file->size = node->size;
2007-07-05 16:01:38 +00:00
file->inode = node;
return 0;
}
static int ramfs_close(struct device_d *dev, FILE *f)
{
2007-07-05 16:01:37 +00:00
return 0;
}
static struct ramfs_chunk *ramfs_find_chunk(struct ramfs_inode *node, int chunk)
{
struct ramfs_chunk *data;
int left = chunk;
if (chunk == 0)
return node->data;
if (node->recent_chunk == chunk)
return node->recent_chunkp;
if (node->recent_chunk < chunk && node->recent_chunk != 0) {
/* Start at last known chunk */
data = node->recent_chunkp;
left -= node->recent_chunk;
} else {
/* Start at first chunk */
data = node->data;
}
while (left--)
data = data->next;
node->recent_chunkp = data;
node->recent_chunk = chunk;
return data;
}
2007-07-05 16:01:39 +00:00
static int ramfs_read(struct device_d *_dev, FILE *f, void *buf, size_t insize)
2007-07-05 16:01:37 +00:00
{
2007-07-05 16:01:39 +00:00
struct ramfs_inode *node = (struct ramfs_inode *)f->inode;
int chunk;
struct ramfs_chunk *data;
int ofs;
int now;
int pos = f->pos;
int size = insize;
chunk = f->pos / CHUNK_SIZE;
debug("%s: reading from chunk %d\n", __FUNCTION__, chunk);
2007-07-05 16:01:39 +00:00
/* Position ourself in stream */
data = ramfs_find_chunk(node, chunk);
2007-07-05 16:01:39 +00:00
ofs = f->pos % CHUNK_SIZE;
/* Read till end of current chunk */
if (ofs) {
now = min(size, CHUNK_SIZE - ofs);
debug("Reading till end of node. size: %d\n", size);
2007-07-05 16:01:39 +00:00
memcpy(buf, data->data + ofs, now);
size -= now;
pos += now;
buf += now;
if (pos > node->size)
node->size = now;
2007-07-05 16:01:43 +00:00
data = data->next;
2007-07-05 16:01:39 +00:00
}
/* Do full chunks */
while (size >= CHUNK_SIZE) {
debug("do full chunk. size: %d\n", size);
2007-07-05 16:01:39 +00:00
memcpy(buf, data->data, CHUNK_SIZE);
2007-07-05 16:01:43 +00:00
data = data->next;
2007-07-05 16:01:39 +00:00
size -= CHUNK_SIZE;
pos += CHUNK_SIZE;
buf += CHUNK_SIZE;
}
/* And the rest */
if (size) {
debug("do rest. size: %d\n", size);
2007-07-05 16:01:39 +00:00
memcpy(buf, data->data, size);
}
return insize;
2007-07-05 16:01:38 +00:00
}
2007-07-05 16:01:38 +00:00
static int ramfs_write(struct device_d *_dev, FILE *f, const void *buf, size_t insize)
2007-07-05 16:01:38 +00:00
{
struct ramfs_inode *node = (struct ramfs_inode *)f->inode;
int chunk;
2007-07-05 16:01:38 +00:00
struct ramfs_chunk *data;
2007-07-05 16:01:38 +00:00
int ofs;
int now;
2007-07-05 16:01:38 +00:00
int pos = f->pos;
int size = insize;
2007-07-05 16:01:38 +00:00
chunk = f->pos / CHUNK_SIZE;
debug("%s: writing to chunk %d\n", __FUNCTION__, chunk);
2007-07-05 16:01:38 +00:00
2007-07-05 16:01:38 +00:00
/* Position ourself in stream */
data = ramfs_find_chunk(node, chunk);
2007-07-05 16:01:38 +00:00
ofs = f->pos % CHUNK_SIZE;
2007-07-05 16:01:38 +00:00
/* Write till end of current chunk */
if (ofs) {
2007-07-05 16:01:38 +00:00
now = min(size, CHUNK_SIZE - ofs);
debug("writing till end of node. size: %d\n", size);
2007-07-05 16:01:43 +00:00
memcpy(data->data + ofs, buf, now);
2007-07-05 16:01:38 +00:00
size -= now;
2007-07-05 16:01:38 +00:00
pos += now;
2007-07-05 16:01:38 +00:00
buf += now;
2007-07-05 16:01:38 +00:00
if (pos > node->size)
node->size = now;
2007-07-05 16:01:43 +00:00
data = data->next;
2007-07-05 16:01:38 +00:00
}
/* Do full chunks */
while (size >= CHUNK_SIZE) {
debug("do full chunk. size: %d\n", size);
2007-07-05 16:01:38 +00:00
memcpy(data->data, buf, CHUNK_SIZE);
2007-07-05 16:01:43 +00:00
data = data->next;
2007-07-05 16:01:38 +00:00
size -= CHUNK_SIZE;
pos += CHUNK_SIZE;
buf += CHUNK_SIZE;
2007-07-05 16:01:38 +00:00
}
2007-07-05 16:01:38 +00:00
/* And the rest */
if (size) {
debug("do rest. size: %d\n", size);
2007-07-05 16:01:38 +00:00
memcpy(data->data, buf, size);
}
return insize;
}
static loff_t ramfs_lseek(struct device_d *dev, FILE *f, loff_t pos)
{
f->pos = pos;
return f->pos;
}
2007-09-28 08:07:26 +00:00
static int ramfs_truncate(struct device_d *dev, FILE *f, ulong size)
2007-07-05 16:01:38 +00:00
{
struct ramfs_inode *node = (struct ramfs_inode *)f->inode;
int oldchunks, newchunks;
struct ramfs_chunk *data = node->data;
newchunks = (size + CHUNK_SIZE - 1) / CHUNK_SIZE;
oldchunks = (node->size + CHUNK_SIZE - 1) / CHUNK_SIZE;
if (newchunks < oldchunks) {
2007-07-05 16:01:48 +00:00
if (!newchunks)
node->data = NULL;
2007-07-05 16:01:38 +00:00
while (newchunks--)
data = data->next;
2007-07-05 16:01:48 +00:00
while (data) {
2007-07-05 16:01:38 +00:00
struct ramfs_chunk *tmp;
tmp = data->next;
ramfs_put_chunk(data);
data = tmp;
}
if (node->recent_chunk > newchunks)
node->recent_chunk = 0;
2007-07-05 16:01:38 +00:00
}
if (newchunks > oldchunks) {
2007-07-05 16:01:48 +00:00
if (!data) {
node->data = ramfs_get_chunk();
if (!node->data)
return -ENOMEM;
2007-07-05 16:01:48 +00:00
data = node->data;
}
newchunks--;
2007-07-05 16:01:38 +00:00
while (data->next) {
newchunks--;
data = data->next;
}
while (newchunks--) {
data->next = ramfs_get_chunk();
if (!data->next)
return -ENOMEM;
data = data->next;
2007-07-05 16:01:38 +00:00
}
}
node->size = size;
return 0;
2007-07-05 16:01:37 +00:00
}
2007-09-28 08:07:26 +00:00
static DIR* ramfs_opendir(struct device_d *dev, const char *pathname)
2007-07-05 16:01:37 +00:00
{
DIR *dir;
2007-07-05 16:01:37 +00:00
struct ramfs_priv *priv = dev->priv;
2007-07-05 16:01:43 +00:00
struct ramfs_inode *node;
debug("opendir: %s\n", pathname);
2007-07-05 16:01:40 +00:00
2007-07-05 16:01:43 +00:00
node = rlookup(priv, pathname);
2007-07-05 16:01:37 +00:00
if (!node)
return NULL;
2007-10-03 17:57:46 +00:00
if (!S_ISDIR(node->mode))
2007-07-05 16:01:37 +00:00
return NULL;
dir = xmalloc(sizeof(DIR));
2007-07-05 16:01:37 +00:00
2007-07-05 16:01:38 +00:00
dir->priv = node->child;
2007-07-05 16:01:37 +00:00
return dir;
}
2007-09-28 08:07:26 +00:00
static struct dirent* ramfs_readdir(struct device_d *dev, DIR *dir)
2007-07-05 16:01:37 +00:00
{
2007-07-05 16:01:38 +00:00
struct ramfs_inode *node = dir->priv;
if (node) {
strcpy(dir->d.d_name, node->name);
2007-07-05 16:01:38 +00:00
dir->priv = node->next;
2007-07-05 16:01:37 +00:00
return &dir->d;
}
return NULL;
}
2007-09-28 08:07:26 +00:00
static int ramfs_closedir(struct device_d *dev, DIR *dir)
2007-07-05 16:01:37 +00:00
{
free(dir);
return 0;
}
2007-09-28 08:07:26 +00:00
static int ramfs_stat(struct device_d *dev, const char *filename, struct stat *s)
2007-07-05 16:01:38 +00:00
{
struct ramfs_priv *priv = dev->priv;
2007-07-05 16:01:43 +00:00
struct ramfs_inode *node = rlookup(priv, filename);
2007-07-05 16:01:38 +00:00
2007-07-05 16:01:40 +00:00
if (!node)
2007-07-05 16:01:38 +00:00
return -ENOENT;
s->st_size = node->symlink ? strlen(node->symlink) : node->size;
2007-07-05 16:01:38 +00:00
s->st_mode = node->mode;
return 0;
}
static int ramfs_symlink(struct device_d *dev, const char *pathname,
const char *newpath)
{
mode_t mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
return __ramfs_create(dev, newpath, mode, pathname);
}
static int ramfs_readlink(struct device_d *dev, const char *pathname,
char *buf, size_t bufsiz)
{
struct ramfs_priv *priv = dev->priv;
struct ramfs_inode *node = rlookup(priv, pathname);
int len;
if (!node || !node->symlink)
return -ENOENT;
len = min(bufsiz, strlen(node->symlink));
memcpy(buf, node->symlink, len);
return 0;
}
2007-09-28 08:07:26 +00:00
static int ramfs_probe(struct device_d *dev)
2007-07-05 16:01:40 +00:00
{
2007-07-05 16:01:43 +00:00
struct ramfs_inode *n;
struct ramfs_priv *priv = xzalloc(sizeof(struct ramfs_priv));
2007-07-05 16:01:40 +00:00
dev->priv = priv;
priv->root.name = "/";
2007-10-03 17:57:46 +00:00
priv->root.mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
2007-07-05 16:01:43 +00:00
priv->root.parent = &priv->root;
n = ramfs_get_inode();
n->name = strdup(".");
n->mode = S_IFDIR;
n->parent = &priv->root;
n->child = n;
priv->root.child = n;
n = ramfs_get_inode();
n->name = strdup("..");
2007-10-03 17:57:46 +00:00
n->mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
2007-07-05 16:01:43 +00:00
n->parent = &priv->root;
n->child = priv->root.child;
priv->root.child->next = n;
2007-07-05 16:01:40 +00:00
return 0;
}
static void ramfs_remove(struct device_d *dev)
{
free(dev->priv);
}
2007-07-05 16:01:37 +00:00
static struct fs_driver_d ramfs_driver = {
2007-07-05 16:01:38 +00:00
.create = ramfs_create,
2007-07-05 16:01:43 +00:00
.unlink = ramfs_unlink,
2007-07-05 16:01:38 +00:00
.open = ramfs_open,
.close = ramfs_close,
.truncate = ramfs_truncate,
.read = ramfs_read,
.write = ramfs_write,
.lseek = ramfs_lseek,
2007-07-05 16:01:38 +00:00
.mkdir = ramfs_mkdir,
2007-07-05 16:01:43 +00:00
.rmdir = ramfs_rmdir,
2007-07-05 16:01:38 +00:00
.opendir = ramfs_opendir,
.readdir = ramfs_readdir,
.closedir = ramfs_closedir,
.stat = ramfs_stat,
.symlink = ramfs_symlink,
.readlink = ramfs_readlink,
2007-07-05 16:01:38 +00:00
.flags = FS_DRIVER_NO_DEV,
2007-07-05 16:01:37 +00:00
.drv = {
.probe = ramfs_probe,
.remove = ramfs_remove,
2007-07-05 16:01:37 +00:00
.name = "ramfs",
}
};
2007-09-28 08:07:26 +00:00
static int ramfs_init(void)
2007-07-05 16:01:37 +00:00
{
return register_fs_driver(&ramfs_driver);
2007-07-05 16:01:37 +00:00
}
coredevice_initcall(ramfs_init);
2007-07-05 16:01:37 +00:00