• Immutable Page
  • Info
  • Attachments

NdevfsSource

/*
 * 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);
Tell others about this page:

last edited 2005-06-29 20:29:25 by ARennes-252-1-13-71