/*
* tyler@agat.net
*
* Nano DevFS (intended to Linux Kernel 2.6(.12))
*
* This module implements the nano dev file system from Greg KH.
* This code is included in the main tree but I've writtent this module
* because it's a very simple file system.
*
* ndevfs is a simple ramfs based filesystem. It creates/deletes
* nodes in the /dev directory dynamically in the format seen in devfs.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/device.h>
#define DRIVER_AUTHOR "tyler@agat.net"
#define DRIVER_DESC "Ram File System"
#define MAGIC 0x64756d62
struct entry {
struct list_head node;
struct dentry *dentry;
char name[BUS_ID_SIZE];
};
static LIST_HEAD(entries);
static struct vfsmount *mount;
static int mount_count;
static struct file_operations file_ops = {
read: generic_file_read,
write: generic_file_write,
mmap: generic_file_mmap,
fsync: simple_sync_file,
llseek: generic_file_llseek,
};
static struct inode *get_inode(struct super_block *sb, int mode, dev_t dev)
{
struct inode *inode = new_inode(sb);
if (inode) {
inode->i_mode = mode;
inode->i_uid = 0;
inode->i_gid = 0;
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
case S_IFREG:
inode->i_fop = &file_ops;
break;
case S_IFDIR:
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inode->i_nlink++;
break;
}
}
return inode;
}
static int mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
struct inode *inode = get_inode(dir->i_sb, mode, dev);
if (dentry->d_inode) {
return -EEXIST;
}
if (! inode) {
return -EPERM;
}
d_instantiate(dentry, inode);
dget(dentry);
return 0;
}
static inline int positive(struct dentry *dentry)
{
return dentry->d_inode && !d_unhashed(dentry);
}
static void remove(struct dentry *dentry)
{
struct dentry *parent;
if (!dentry) {
return;
}
parent = dentry->d_parent;
if (!parent || !parent->d_inode) {
return;
}
down(&parent->d_inode->i_sem);
if (positive(dentry)) {
if (dentry->d_inode) {
if (S_ISDIR(dentry->d_inode->i_mode)) {
simple_rmdir(parent->d_inode, dentry);
}
else {
simple_unlink(parent->d_inode, dentry);
}
}
}
up(&parent->d_inode->i_sem);
simple_release_fs(&mount, &mount_count);
}
void mfs_create(const char *name, dev_t dev)
{
struct dentry *dentry;
struct dentry *parent;
struct entry *entry;
int err;
int mode = S_IRUSR | S_IWUSR;
mode |= S_IFREG;
err = simple_pin_fs("mfs", &mount, &mount_count);
if (err) {
return;
}
if (mount && mount->mnt_sb) {
parent = mount->mnt_sb->s_root;
}
else {
pr_debug("%s: no parent?\n", __FUNCTION__);
goto error;
}
down(&parent->d_inode->i_sem);
dentry = lookup_one_len(name, parent, strlen(name));
if (!IS_ERR(dentry)) {
err = mknod(parent->d_inode, dentry, mode, dev);
}
else {
err = PTR_ERR(dentry);
}
up(&parent->d_inode->i_sem);
if (err) {
goto error;
}
entry = kmalloc(sizeof(struct entry), GFP_KERNEL);
if (!entry) {
remove(dentry);
err = -ENOMEM;
goto error;
}
entry->dentry = dentry;
strcpy(&entry->name[0], name);
list_add(&entry->node, &entries);
return;
error:
pr_debug("%s failed with error %d\n", __FUNCTION__, err);
simple_release_fs(&mount, &mount_count);
}
EXPORT_SYMBOL_GPL(mfs_create);
void mfs_remove(const char *name)
{
struct entry *entry;
struct dentry *dentry = NULL;
list_for_each_entry(entry, &entries, node) {
if (strcmp(&entry->name[0], name) == 0) {
dentry = entry->dentry;
break;
}
}
if (!dentry) {
pr_debug("%s: can't find %s\n", __FUNCTION__, name);
return;
}
remove(dentry);
}
EXPORT_SYMBOL_GPL(mfs_remove);
static int fill_super(struct super_block *sb, void *data, int silent)
{
static struct tree_descr files[] = {{""}};
return simple_fill_super(sb, MAGIC, files);
}
static struct super_block *get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return get_sb_single(fs_type, flags, data, fill_super);
}
static struct file_system_type mfs = {
owner: THIS_MODULE,
name: "mfs",
get_sb: get_sb,
kill_sb: kill_litter_super,
};
int __init loading(void)
{
return register_filesystem(&mfs);
}
void __exit unloading(void)
{
simple_release_fs(&mount, &mount_count);
unregister_filesystem(&mfs);
}
module_init(loading);
module_exit(unloading);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);