- fs/inode.c | 21 ++++++++++++++-------
- fs/super.c | 4 ++--
- include/linux/fs.h | 2 +-
- 3 files changed, 17 insertions(+), 10 deletions(-)
+ fs/block_dev.c | 695 ++++++++++
+ fs/devfs/base.c | 3544 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ fs/exec.c | 2
+ fs/inode.c | 1247 ++++++++++++++++++
+ fs/super.c | 926 +++++++++++++
+ include/linux/fs.h | 1709 +++++++++++++++++++++++++
+ 6 files changed, 8122 insertions(+), 1 deletion(-)
---- linux-2.4.20/fs/inode.c~invalidate_show 2003-04-08 23:34:36.000000000 -0600
-+++ linux-2.4.20-braam/fs/inode.c 2003-04-08 23:34:36.000000000 -0600
-@@ -553,7 +553,8 @@ static void dispose_list(struct list_hea
- /*
- * Invalidate all inodes for a device.
- */
--static int invalidate_list(struct list_head *head, struct super_block * sb, struct list_head * dispose)
+--- /dev/null 2003-01-30 18:24:37.000000000 +0800
++++ linux-2.4.19-hp3_pnnl1-root/fs/inode.c 2003-04-15 13:21:57.000000000 +0800
+@@ -0,0 +1,1247 @@
++/*
++ * linux/fs/inode.c
++ *
++ * (C) 1997 Linus Torvalds
++ */
++
++#include <linux/config.h>
++#include <linux/fs.h>
++#include <linux/string.h>
++#include <linux/mm.h>
++#include <linux/dcache.h>
++#include <linux/init.h>
++#include <linux/quotaops.h>
++#include <linux/slab.h>
++#include <linux/cache.h>
++#include <linux/swap.h>
++#include <linux/swapctl.h>
++#include <linux/prefetch.h>
++#include <linux/locks.h>
++
++/*
++ * New inode.c implementation.
++ *
++ * This implementation has the basic premise of trying
++ * to be extremely low-overhead and SMP-safe, yet be
++ * simple enough to be "obviously correct".
++ *
++ * Famous last words.
++ */
++
++/* inode dynamic allocation 1999, Andrea Arcangeli <andrea@suse.de> */
++
++/* #define INODE_PARANOIA 1 */
++/* #define INODE_DEBUG 1 */
++
++/*
++ * Inode lookup is no longer as critical as it used to be:
++ * most of the lookups are going to be through the dcache.
++ */
++#define I_HASHBITS i_hash_shift
++#define I_HASHMASK i_hash_mask
++
++static unsigned int i_hash_mask;
++static unsigned int i_hash_shift;
++
++/*
++ * Each inode can be on two separate lists. One is
++ * the hash list of the inode, used for lookups. The
++ * other linked list is the "type" list:
++ * "in_use" - valid inode, i_count > 0, i_nlink > 0
++ * "dirty" - as "in_use" but also dirty
++ * "unused" - valid inode, i_count = 0
++ *
++ * A "dirty" list is maintained for each super block,
++ * allowing for low-overhead inode sync() operations.
++ */
++
++static LIST_HEAD(inode_in_use);
++static LIST_HEAD(inode_unused);
++static struct list_head *inode_hashtable;
++static LIST_HEAD(anon_hash_chain); /* for inodes with NULL i_sb */
++
++/*
++ * A simple spinlock to protect the list manipulations.
++ *
++ * NOTE! You also have to own the lock if you change
++ * the i_state of an inode while it is in use..
++ */
++static spinlock_t inode_lock = SPIN_LOCK_UNLOCKED;
++
++/*
++ * Statistics gathering..
++ */
++struct inodes_stat_t inodes_stat;
++
++static kmem_cache_t * inode_cachep;
++
++static struct inode *alloc_inode(struct super_block *sb)
++{
++ static struct address_space_operations empty_aops;
++ static struct inode_operations empty_iops;
++ static struct file_operations empty_fops;
++ struct inode *inode;
++
++ if (sb->s_op->alloc_inode)
++ inode = sb->s_op->alloc_inode(sb);
++ else {
++ inode = (struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL);
++ /* will die */
++ if (inode)
++ memset(&inode->u, 0, sizeof(inode->u));
++ }
++
++ if (inode) {
++ struct address_space * const mapping = &inode->i_data;
++
++ inode->i_sb = sb;
++ inode->i_dev = sb->s_dev;
++ inode->i_blkbits = sb->s_blocksize_bits;
++ inode->i_flags = 0;
++ atomic_set(&inode->i_count, 1);
++ inode->i_sock = 0;
++ inode->i_op = &empty_iops;
++ inode->i_fop = &empty_fops;
++ inode->i_nlink = 1;
++ atomic_set(&inode->i_writecount, 0);
++ inode->i_size = 0;
++ inode->i_blocks = 0;
++ inode->i_bytes = 0;
++ inode->i_generation = 0;
++ memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
++ inode->i_pipe = NULL;
++ inode->i_bdev = NULL;
++ inode->i_cdev = NULL;
++
++ mapping->a_ops = &empty_aops;
++ mapping->host = inode;
++ mapping->gfp_mask = GFP_HIGHUSER;
++ inode->i_mapping = mapping;
++ }
++ return inode;
++}
++
++static void destroy_inode(struct inode *inode)
++{
++ if (inode_has_buffers(inode))
++ BUG();
++ if (inode->i_sb->s_op->destroy_inode)
++ inode->i_sb->s_op->destroy_inode(inode);
++ else
++ kmem_cache_free(inode_cachep, inode);
++}
++
++
++/*
++ * These are initializations that only need to be done
++ * once, because the fields are idempotent across use
++ * of the inode, so let the slab aware of that.
++ */
++void inode_init_once(struct inode *inode)
++{
++ memset(inode, 0, sizeof(*inode));
++ init_waitqueue_head(&inode->i_wait);
++ INIT_LIST_HEAD(&inode->i_hash);
++ INIT_LIST_HEAD(&inode->i_data.clean_pages);
++ INIT_LIST_HEAD(&inode->i_data.dirty_pages);
++ INIT_LIST_HEAD(&inode->i_data.locked_pages);
++ INIT_LIST_HEAD(&inode->i_dentry);
++ INIT_LIST_HEAD(&inode->i_dirty_buffers);
++ INIT_LIST_HEAD(&inode->i_dirty_data_buffers);
++ INIT_LIST_HEAD(&inode->i_devices);
++ sema_init(&inode->i_sem, 1);
++ sema_init(&inode->i_zombie, 1);
++ spin_lock_init(&inode->i_data.i_shared_lock);
++}
++
++static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
++{
++ struct inode * inode = (struct inode *) foo;
++
++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++ SLAB_CTOR_CONSTRUCTOR)
++ inode_init_once(inode);
++}
++
++/*
++ * Put the inode on the super block's dirty list.
++ *
++ * CAREFUL! We mark it dirty unconditionally, but
++ * move it onto the dirty list only if it is hashed.
++ * If it was not hashed, it will never be added to
++ * the dirty list even if it is later hashed, as it
++ * will have been marked dirty already.
++ *
++ * In short, make sure you hash any inodes _before_
++ * you start marking them dirty..
++ */
++
++/**
++ * __mark_inode_dirty - internal function
++ * @inode: inode to mark
++ * @flags: what kind of dirty (i.e. I_DIRTY_SYNC)
++ * Mark an inode as dirty. Callers should use mark_inode_dirty or
++ * mark_inode_dirty_sync.
++ */
++
++void __mark_inode_dirty(struct inode *inode, int flags)
++{
++ struct super_block * sb = inode->i_sb;
++
++ if (!sb)
++ return;
++
++ /* Don't do this for I_DIRTY_PAGES - that doesn't actually dirty the inode itself */
++ if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
++ if (sb->s_op && sb->s_op->dirty_inode)
++ sb->s_op->dirty_inode(inode);
++ }
++
++ /* avoid the locking if we can */
++ if ((inode->i_state & flags) == flags)
++ return;
++
++ spin_lock(&inode_lock);
++ if ((inode->i_state & flags) != flags) {
++ inode->i_state |= flags;
++ /* Only add valid (ie hashed) inodes to the dirty list */
++ if (!(inode->i_state & I_LOCK) && !list_empty(&inode->i_hash)) {
++ list_del(&inode->i_list);
++ list_add(&inode->i_list, &sb->s_dirty);
++ }
++ }
++ spin_unlock(&inode_lock);
++}
++
++static void __wait_on_inode(struct inode * inode)
++{
++ DECLARE_WAITQUEUE(wait, current);
++
++ add_wait_queue(&inode->i_wait, &wait);
++repeat:
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ if (inode->i_state & I_LOCK) {
++ schedule();
++ goto repeat;
++ }
++ remove_wait_queue(&inode->i_wait, &wait);
++ current->state = TASK_RUNNING;
++}
++
++static inline void wait_on_inode(struct inode *inode)
++{
++ if (inode->i_state & I_LOCK)
++ __wait_on_inode(inode);
++}
++
++
++static inline void write_inode(struct inode *inode, int sync)
++{
++ if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode && !is_bad_inode(inode))
++ inode->i_sb->s_op->write_inode(inode, sync);
++}
++
++static inline void __iget(struct inode * inode)
++{
++ if (atomic_read(&inode->i_count)) {
++ atomic_inc(&inode->i_count);
++ return;
++ }
++ atomic_inc(&inode->i_count);
++ if (!(inode->i_state & (I_DIRTY|I_LOCK))) {
++ list_del(&inode->i_list);
++ list_add(&inode->i_list, &inode_in_use);
++ }
++ inodes_stat.nr_unused--;
++}
++
++static inline void __sync_one(struct inode *inode, int sync)
++{
++ unsigned dirty;
++
++ list_del(&inode->i_list);
++ list_add(&inode->i_list, &inode->i_sb->s_locked_inodes);
++
++ if (inode->i_state & I_LOCK)
++ BUG();
++
++ /* Set I_LOCK, reset I_DIRTY */
++ dirty = inode->i_state & I_DIRTY;
++ inode->i_state |= I_LOCK;
++ inode->i_state &= ~I_DIRTY;
++ spin_unlock(&inode_lock);
++
++ filemap_fdatasync(inode->i_mapping);
++
++ /* Don't write the inode if only I_DIRTY_PAGES was set */
++ if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC))
++ write_inode(inode, sync);
++
++ filemap_fdatawait(inode->i_mapping);
++
++ spin_lock(&inode_lock);
++ inode->i_state &= ~I_LOCK;
++ if (!(inode->i_state & I_FREEING)) {
++ struct list_head *to;
++ if (inode->i_state & I_DIRTY)
++ to = &inode->i_sb->s_dirty;
++ else if (atomic_read(&inode->i_count))
++ to = &inode_in_use;
++ else
++ to = &inode_unused;
++ list_del(&inode->i_list);
++ list_add(&inode->i_list, to);
++ }
++ wake_up(&inode->i_wait);
++}
++
++static inline void sync_one(struct inode *inode, int sync)
++{
++ while (inode->i_state & I_LOCK) {
++ __iget(inode);
++ spin_unlock(&inode_lock);
++ __wait_on_inode(inode);
++ iput(inode);
++ spin_lock(&inode_lock);
++ }
++
++ __sync_one(inode, sync);
++}
++
++static inline void sync_list(struct list_head *head)
++{
++ struct list_head * tmp;
++
++ while ((tmp = head->prev) != head)
++ __sync_one(list_entry(tmp, struct inode, i_list), 0);
++}
++
++static inline void wait_on_locked(struct list_head *head)
++{
++ struct list_head * tmp;
++ while ((tmp = head->prev) != head) {
++ struct inode *inode = list_entry(tmp, struct inode, i_list);
++ __iget(inode);
++ spin_unlock(&inode_lock);
++ __wait_on_inode(inode);
++ iput(inode);
++ spin_lock(&inode_lock);
++ }
++}
++
++static inline int try_to_sync_unused_list(struct list_head *head, int nr_inodes)
++{
++ struct list_head *tmp = head;
++ struct inode *inode;
++
++ while (nr_inodes && (tmp = tmp->prev) != head) {
++ inode = list_entry(tmp, struct inode, i_list);
++
++ if (!atomic_read(&inode->i_count)) {
++ __sync_one(inode, 0);
++ nr_inodes--;
++
++ /*
++ * __sync_one moved the inode to another list,
++ * so we have to start looking from the list head.
++ */
++ tmp = head;
++ }
++ }
++
++ return nr_inodes;
++}
++
++void sync_inodes_sb(struct super_block *sb)
++{
++ spin_lock(&inode_lock);
++ while (!list_empty(&sb->s_dirty)||!list_empty(&sb->s_locked_inodes)) {
++ sync_list(&sb->s_dirty);
++ wait_on_locked(&sb->s_locked_inodes);
++ }
++ spin_unlock(&inode_lock);
++}
++
++/*
++ * Note:
++ * We don't need to grab a reference to superblock here. If it has non-empty
++ * ->s_dirty it's hadn't been killed yet and kill_super() won't proceed
++ * past sync_inodes_sb() until both ->s_dirty and ->s_locked_inodes are
++ * empty. Since __sync_one() regains inode_lock before it finally moves
++ * inode from superblock lists we are OK.
++ */
++
++void sync_unlocked_inodes(void)
++{
++ struct super_block * sb;
++ spin_lock(&inode_lock);
++ spin_lock(&sb_lock);
++ sb = sb_entry(super_blocks.next);
++ for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
++ if (!list_empty(&sb->s_dirty)) {
++ spin_unlock(&sb_lock);
++ sync_list(&sb->s_dirty);
++ spin_lock(&sb_lock);
++ }
++ }
++ spin_unlock(&sb_lock);
++ spin_unlock(&inode_lock);
++}
++
++/*
++ * Find a superblock with inodes that need to be synced
++ */
++
++static struct super_block *get_super_to_sync(void)
++{
++ struct list_head *p;
++restart:
++ spin_lock(&inode_lock);
++ spin_lock(&sb_lock);
++ list_for_each(p, &super_blocks) {
++ struct super_block *s = list_entry(p,struct super_block,s_list);
++ if (list_empty(&s->s_dirty) && list_empty(&s->s_locked_inodes))
++ continue;
++ s->s_count++;
++ spin_unlock(&sb_lock);
++ spin_unlock(&inode_lock);
++ down_read(&s->s_umount);
++ if (!s->s_root) {
++ drop_super(s);
++ goto restart;
++ }
++ return s;
++ }
++ spin_unlock(&sb_lock);
++ spin_unlock(&inode_lock);
++ return NULL;
++}
++
++/**
++ * sync_inodes
++ * @dev: device to sync the inodes from.
++ *
++ * sync_inodes goes through the super block's dirty list,
++ * writes them out, and puts them back on the normal list.
++ */
++
++void sync_inodes(kdev_t dev)
++{
++ struct super_block * s;
++
++ /*
++ * Search the super_blocks array for the device(s) to sync.
++ */
++ if (dev) {
++ if ((s = get_super(dev)) != NULL) {
++ sync_inodes_sb(s);
++ drop_super(s);
++ }
++ } else {
++ while ((s = get_super_to_sync()) != NULL) {
++ sync_inodes_sb(s);
++ drop_super(s);
++ }
++ }
++}
++
++static void try_to_sync_unused_inodes(void * arg)
++{
++ struct super_block * sb;
++ int nr_inodes = inodes_stat.nr_unused;
++
++ spin_lock(&inode_lock);
++ spin_lock(&sb_lock);
++ sb = sb_entry(super_blocks.next);
++ for (; nr_inodes && sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
++ if (list_empty(&sb->s_dirty))
++ continue;
++ spin_unlock(&sb_lock);
++ nr_inodes = try_to_sync_unused_list(&sb->s_dirty, nr_inodes);
++ spin_lock(&sb_lock);
++ }
++ spin_unlock(&sb_lock);
++ spin_unlock(&inode_lock);
++}
++
++static struct tq_struct unused_inodes_flush_task;
++
++/**
++ * write_inode_now - write an inode to disk
++ * @inode: inode to write to disk
++ * @sync: whether the write should be synchronous or not
++ *
++ * This function commits an inode to disk immediately if it is
++ * dirty. This is primarily needed by knfsd.
++ */
++
++void write_inode_now(struct inode *inode, int sync)
++{
++ struct super_block * sb = inode->i_sb;
++
++ if (sb) {
++ spin_lock(&inode_lock);
++ while (inode->i_state & I_DIRTY)
++ sync_one(inode, sync);
++ spin_unlock(&inode_lock);
++ if (sync)
++ wait_on_inode(inode);
++ }
++ else
++ printk(KERN_ERR "write_inode_now: no super block\n");
++}
++
++/**
++ * generic_osync_inode - flush all dirty data for a given inode to disk
++ * @inode: inode to write
++ * @datasync: if set, don't bother flushing timestamps
++ *
++ * This can be called by file_write functions for files which have the
++ * O_SYNC flag set, to flush dirty writes to disk.
++ */
++
++int generic_osync_inode(struct inode *inode, int what)
++{
++ int err = 0, err2 = 0, need_write_inode_now = 0;
++
++ /*
++ * WARNING
++ *
++ * Currently, the filesystem write path does not pass the
++ * filp down to the low-level write functions. Therefore it
++ * is impossible for (say) __block_commit_write to know if
++ * the operation is O_SYNC or not.
++ *
++ * Ideally, O_SYNC writes would have the filesystem call
++ * ll_rw_block as it went to kick-start the writes, and we
++ * could call osync_inode_buffers() here to wait only for
++ * those IOs which have already been submitted to the device
++ * driver layer. As it stands, if we did this we'd not write
++ * anything to disk since our writes have not been queued by
++ * this point: they are still on the dirty LRU.
++ *
++ * So, currently we will call fsync_inode_buffers() instead,
++ * to flush _all_ dirty buffers for this inode to disk on
++ * every O_SYNC write, not just the synchronous I/Os. --sct
++ */
++
++ if (what & OSYNC_METADATA)
++ err = fsync_inode_buffers(inode);
++ if (what & OSYNC_DATA)
++ err2 = fsync_inode_data_buffers(inode);
++ if (!err)
++ err = err2;
++
++ spin_lock(&inode_lock);
++ if ((inode->i_state & I_DIRTY) &&
++ ((what & OSYNC_INODE) || (inode->i_state & I_DIRTY_DATASYNC)))
++ need_write_inode_now = 1;
++ spin_unlock(&inode_lock);
++
++ if (need_write_inode_now)
++ write_inode_now(inode, 1);
++ else
++ wait_on_inode(inode);
++
++ return err;
++}
++
++/**
++ * clear_inode - clear an inode
++ * @inode: inode to clear
++ *
++ * This is called by the filesystem to tell us
++ * that the inode is no longer useful. We just
++ * terminate it with extreme prejudice.
++ */
++
++void clear_inode(struct inode *inode)
++{
++ invalidate_inode_buffers(inode);
++
++ if (inode->i_data.nrpages)
++ BUG();
++ if (!(inode->i_state & I_FREEING))
++ BUG();
++ if (inode->i_state & I_CLEAR)
++ BUG();
++ wait_on_inode(inode);
++ DQUOT_DROP(inode);
++ if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->clear_inode)
++ inode->i_sb->s_op->clear_inode(inode);
++ if (inode->i_bdev)
++ bd_forget(inode);
++ else if (inode->i_cdev) {
++ cdput(inode->i_cdev);
++ inode->i_cdev = NULL;
++ }
++ inode->i_state = I_CLEAR;
++}
++
++/*
++ * Dispose-list gets a local list with local inodes in it, so it doesn't
++ * need to worry about list corruption and SMP locks.
++ */
++static void dispose_list(struct list_head * head)
++{
++ struct list_head * inode_entry;
++ struct inode * inode;
++
++ while ((inode_entry = head->next) != head)
++ {
++ list_del(inode_entry);
++
++ inode = list_entry(inode_entry, struct inode, i_list);
++ if (inode->i_data.nrpages)
++ truncate_inode_pages(&inode->i_data, 0);
++ clear_inode(inode);
++ destroy_inode(inode);
++ inodes_stat.nr_inodes--;
++ }
++}
++
++/*
++ * Invalidate all inodes for a device.
++ */
+static int invalidate_list(struct list_head *head, struct super_block * sb,
+ struct list_head * dispose, int show)
- {
- struct list_head *next;
- int busy = 0, count = 0;
-@@ -578,6 +579,11 @@ static int invalidate_list(struct list_h
- count++;
- continue;
- }
++{
++ struct list_head *next;
++ int busy = 0, count = 0;
++
++ next = head->next;
++ for (;;) {
++ struct list_head * tmp = next;
++ struct inode * inode;
++
++ next = next->next;
++ if (tmp == head)
++ break;
++ inode = list_entry(tmp, struct inode, i_list);
++ if (inode->i_sb != sb)
++ continue;
++ invalidate_inode_buffers(inode);
++ if (!atomic_read(&inode->i_count)) {
++ list_del_init(&inode->i_hash);
++ list_del(&inode->i_list);
++ list_add(&inode->i_list, dispose);
++ inode->i_state |= I_FREEING;
++ count++;
++ continue;
++ }
+ if (show)
+ printk(KERN_ERR
+ "inode busy: dev %s:%lu (%p) mode %o count %u\n",
+ kdevname(sb->s_dev), inode->i_ino, inode,
+ inode->i_mode, atomic_read(&inode->i_count));
- busy = 1;
- }
- /* only unused inodes may be cached with i_count zero */
-@@ -596,22 +602,23 @@ static int invalidate_list(struct list_h
- /**
- * invalidate_inodes - discard the inodes on a device
- * @sb: superblock
++ busy = 1;
++ }
++ /* only unused inodes may be cached with i_count zero */
++ inodes_stat.nr_unused -= count;
++ return busy;
++}
++
++/*
++ * This is a two-stage process. First we collect all
++ * offending inodes onto the throw-away list, and in
++ * the second stage we actually dispose of them. This
++ * is because we don't want to sleep while messing
++ * with the global lists..
++ */
++
++/**
++ * invalidate_inodes - discard the inodes on a device
++ * @sb: superblock
+ * @show: whether we should display any busy inodes found
- *
- * Discard all of the inodes for a given superblock. If the discard
- * fails because there are busy inodes then a non zero value is returned.
- * If the discard is successful all the inodes have been discarded.
- */
-
--int invalidate_inodes(struct super_block * sb)
++ *
++ * Discard all of the inodes for a given superblock. If the discard
++ * fails because there are busy inodes then a non zero value is returned.
++ * If the discard is successful all the inodes have been discarded.
++ */
++
+int invalidate_inodes(struct super_block * sb, int show)
- {
- int busy;
- LIST_HEAD(throw_away);
-
- spin_lock(&inode_lock);
-- busy = invalidate_list(&inode_in_use, sb, &throw_away);
-- busy |= invalidate_list(&inode_unused, sb, &throw_away);
-- busy |= invalidate_list(&sb->s_dirty, sb, &throw_away);
-- busy |= invalidate_list(&sb->s_locked_inodes, sb, &throw_away);
++{
++ int busy;
++ LIST_HEAD(throw_away);
++
++ spin_lock(&inode_lock);
+ busy = invalidate_list(&inode_in_use, sb, &throw_away, show);
+ busy |= invalidate_list(&inode_unused, sb, &throw_away, show);
+ busy |= invalidate_list(&sb->s_dirty, sb, &throw_away, show);
+ busy |= invalidate_list(&sb->s_locked_inodes, sb, &throw_away, show);
- spin_unlock(&inode_lock);
-
- dispose_list(&throw_away);
-@@ -637,7 +644,7 @@ int invalidate_device(kdev_t dev, int do
- * hold).
- */
- shrink_dcache_sb(sb);
-- res = invalidate_inodes(sb);
++ spin_unlock(&inode_lock);
++
++ dispose_list(&throw_away);
++
++ return busy;
++}
++
++int invalidate_device(kdev_t dev, int do_sync)
++{
++ struct super_block *sb;
++ int res;
++
++ if (do_sync)
++ fsync_dev(dev);
++
++ res = 0;
++ sb = get_super(dev);
++ if (sb) {
++ /*
++ * no need to lock the super, get_super holds the
++ * read semaphore so the filesystem cannot go away
++ * under us (->put_super runs with the write lock
++ * hold).
++ */
++ shrink_dcache_sb(sb);
+ res = invalidate_inodes(sb, 0);
- drop_super(sb);
- }
- invalidate_buffers(dev);
---- linux-2.4.20/fs/super.c~invalidate_show 2003-04-08 23:34:36.000000000 -0600
-+++ linux-2.4.20-braam/fs/super.c 2003-04-08 23:34:36.000000000 -0600
-@@ -835,7 +835,7 @@ void kill_super(struct super_block *sb)
- lock_super(sb);
- lock_kernel();
- sb->s_flags &= ~MS_ACTIVE;
-- invalidate_inodes(sb); /* bad name - it should be evict_inodes() */
++ drop_super(sb);
++ }
++ invalidate_buffers(dev);
++ return res;
++}
++
++
++/*
++ * This is called with the inode lock held. It searches
++ * the in-use for freeable inodes, which are moved to a
++ * temporary list and then placed on the unused list by
++ * dispose_list.
++ *
++ * We don't expect to have to call this very often.
++ *
++ * N.B. The spinlock is released during the call to
++ * dispose_list.
++ */
++#define CAN_UNUSE(inode) \
++ ((((inode)->i_state | (inode)->i_data.nrpages) == 0) && \
++ !inode_has_buffers(inode))
++#define INODE(entry) (list_entry(entry, struct inode, i_list))
++
++void prune_icache(int goal)
++{
++ LIST_HEAD(list);
++ struct list_head *entry, *freeable = &list;
++ int count;
++ struct inode * inode;
++
++ spin_lock(&inode_lock);
++
++ count = 0;
++ entry = inode_unused.prev;
++ while (entry != &inode_unused)
++ {
++ struct list_head *tmp = entry;
++
++ entry = entry->prev;
++ inode = INODE(tmp);
++ if (inode->i_state & (I_FREEING|I_CLEAR|I_LOCK))
++ continue;
++ if (!CAN_UNUSE(inode))
++ continue;
++ if (atomic_read(&inode->i_count))
++ continue;
++ list_del(tmp);
++ list_del(&inode->i_hash);
++ INIT_LIST_HEAD(&inode->i_hash);
++ list_add(tmp, freeable);
++ inode->i_state |= I_FREEING;
++ count++;
++ if (!--goal)
++ break;
++ }
++ inodes_stat.nr_unused -= count;
++ spin_unlock(&inode_lock);
++
++ dispose_list(freeable);
++
++ /*
++ * If we didn't freed enough clean inodes schedule
++ * a sync of the dirty inodes, we cannot do it
++ * from here or we're either synchronously dogslow
++ * or we deadlock with oom.
++ */
++ if (goal)
++ schedule_task(&unused_inodes_flush_task);
++}
++
++int shrink_icache_memory(int priority, int gfp_mask)
++{
++ int count = 0;
++
++ /*
++ * Nasty deadlock avoidance..
++ *
++ * We may hold various FS locks, and we don't
++ * want to recurse into the FS that called us
++ * in clear_inode() and friends..
++ */
++ if (!(gfp_mask & __GFP_FS))
++ return 0;
++
++ count = inodes_stat.nr_unused / priority;
++
++ prune_icache(count);
++ return kmem_cache_shrink(inode_cachep);
++}
++
++/*
++ * Called with the inode lock held.
++ * NOTE: we are not increasing the inode-refcount, you must call __iget()
++ * by hand after calling find_inode now! This simplifies iunique and won't
++ * add any additional branch in the common code.
++ */
++static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
++{
++ struct list_head *tmp;
++ struct inode * inode;
++
++ tmp = head;
++ for (;;) {
++ tmp = tmp->next;
++ inode = NULL;
++ if (tmp == head)
++ break;
++ inode = list_entry(tmp, struct inode, i_hash);
++ if (inode->i_ino != ino)
++ continue;
++ if (inode->i_sb != sb)
++ continue;
++ if (find_actor && !find_actor(inode, ino, opaque))
++ continue;
++ break;
++ }
++ return inode;
++}
++
++/**
++ * new_inode - obtain an inode
++ * @sb: superblock
++ *
++ * Allocates a new inode for given superblock.
++ */
++
++struct inode * new_inode(struct super_block *sb)
++{
++ static unsigned long last_ino;
++ struct inode * inode;
++
++ spin_lock_prefetch(&inode_lock);
++
++ inode = alloc_inode(sb);
++ if (inode) {
++ spin_lock(&inode_lock);
++ inodes_stat.nr_inodes++;
++ list_add(&inode->i_list, &inode_in_use);
++ inode->i_ino = ++last_ino;
++ inode->i_state = 0;
++ spin_unlock(&inode_lock);
++ }
++ return inode;
++}
++
++void unlock_new_inode(struct inode *inode)
++{
++ /*
++ * This is special! We do not need the spinlock
++ * when clearing I_LOCK, because we're guaranteed
++ * that nobody else tries to do anything about the
++ * state of the inode when it is locked, as we
++ * just created it (so there can be no old holders
++ * that haven't tested I_LOCK).
++ */
++ inode->i_state &= ~(I_LOCK|I_NEW);
++ wake_up(&inode->i_wait);
++}
++
++/*
++ * This is called without the inode lock held.. Be careful.
++ *
++ * We no longer cache the sb_flags in i_flags - see fs.h
++ * -- rmk@arm.uk.linux.org
++ */
++static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
++{
++ struct inode * inode;
++
++ inode = alloc_inode(sb);
++ if (inode) {
++ struct inode * old;
++
++ spin_lock(&inode_lock);
++ /* We released the lock, so.. */
++ old = find_inode(sb, ino, head, find_actor, opaque);
++ if (!old) {
++ inodes_stat.nr_inodes++;
++ list_add(&inode->i_list, &inode_in_use);
++ list_add(&inode->i_hash, head);
++ inode->i_ino = ino;
++ inode->i_state = I_LOCK|I_NEW;
++ spin_unlock(&inode_lock);
++
++ /*
++ * Return the locked inode with I_NEW set, the
++ * caller is responsible for filling in the contents
++ */
++ return inode;
++ }
++
++ /*
++ * Uhhuh, somebody else created the same inode under
++ * us. Use the old inode instead of the one we just
++ * allocated.
++ */
++ __iget(old);
++ spin_unlock(&inode_lock);
++ destroy_inode(inode);
++ inode = old;
++ wait_on_inode(inode);
++ }
++ return inode;
++}
++
++static inline unsigned long hash(struct super_block *sb, unsigned long i_ino)
++{
++ unsigned long tmp = i_ino + ((unsigned long) sb / L1_CACHE_BYTES);
++ tmp = tmp + (tmp >> I_HASHBITS);
++ return tmp & I_HASHMASK;
++}
++
++/* Yeah, I know about quadratic hash. Maybe, later. */
++
++/**
++ * iunique - get a unique inode number
++ * @sb: superblock
++ * @max_reserved: highest reserved inode number
++ *
++ * Obtain an inode number that is unique on the system for a given
++ * superblock. This is used by file systems that have no natural
++ * permanent inode numbering system. An inode number is returned that
++ * is higher than the reserved limit but unique.
++ *
++ * BUGS:
++ * With a large number of inodes live on the file system this function
++ * currently becomes quite slow.
++ */
++
++ino_t iunique(struct super_block *sb, ino_t max_reserved)
++{
++ static ino_t counter = 0;
++ struct inode *inode;
++ struct list_head * head;
++ ino_t res;
++ spin_lock(&inode_lock);
++retry:
++ if (counter > max_reserved) {
++ head = inode_hashtable + hash(sb,counter);
++ inode = find_inode(sb, res = counter++, head, NULL, NULL);
++ if (!inode) {
++ spin_unlock(&inode_lock);
++ return res;
++ }
++ } else {
++ counter = max_reserved + 1;
++ }
++ goto retry;
++
++}
++
++struct inode *igrab(struct inode *inode)
++{
++ spin_lock(&inode_lock);
++ if (!(inode->i_state & I_FREEING))
++ __iget(inode);
++ else
++ /*
++ * Handle the case where s_op->clear_inode is not been
++ * called yet, and somebody is calling igrab
++ * while the inode is getting freed.
++ */
++ inode = NULL;
++ spin_unlock(&inode_lock);
++ return inode;
++}
++
++struct inode *iget4_locked(struct super_block *sb, unsigned long ino, find_inode_t find_actor, void *opaque)
++{
++ struct list_head * head = inode_hashtable + hash(sb,ino);
++ struct inode * inode;
++
++ spin_lock(&inode_lock);
++ inode = find_inode(sb, ino, head, find_actor, opaque);
++ if (inode) {
++ __iget(inode);
++ spin_unlock(&inode_lock);
++ wait_on_inode(inode);
++ return inode;
++ }
++ spin_unlock(&inode_lock);
++
++ /*
++ * get_new_inode() will do the right thing, re-trying the search
++ * in case it had to block at any point.
++ */
++ return get_new_inode(sb, ino, head, find_actor, opaque);
++}
++
++/**
++ * insert_inode_hash - hash an inode
++ * @inode: unhashed inode
++ *
++ * Add an inode to the inode hash for this superblock. If the inode
++ * has no superblock it is added to a separate anonymous chain.
++ */
++
++void insert_inode_hash(struct inode *inode)
++{
++ struct list_head *head = &anon_hash_chain;
++ if (inode->i_sb)
++ head = inode_hashtable + hash(inode->i_sb, inode->i_ino);
++ spin_lock(&inode_lock);
++ list_add(&inode->i_hash, head);
++ spin_unlock(&inode_lock);
++}
++
++/**
++ * remove_inode_hash - remove an inode from the hash
++ * @inode: inode to unhash
++ *
++ * Remove an inode from the superblock or anonymous hash.
++ */
++
++void remove_inode_hash(struct inode *inode)
++{
++ spin_lock(&inode_lock);
++ list_del(&inode->i_hash);
++ INIT_LIST_HEAD(&inode->i_hash);
++ spin_unlock(&inode_lock);
++}
++
++/**
++ * iput - put an inode
++ * @inode: inode to put
++ *
++ * Puts an inode, dropping its usage count. If the inode use count hits
++ * zero the inode is also then freed and may be destroyed.
++ */
++
++void iput(struct inode *inode)
++{
++ if (inode) {
++ struct super_block *sb = inode->i_sb;
++ struct super_operations *op = NULL;
++
++ if (inode->i_state == I_CLEAR)
++ BUG();
++
++ if (sb && sb->s_op)
++ op = sb->s_op;
++ if (op && op->put_inode)
++ op->put_inode(inode);
++
++ if (!atomic_dec_and_lock(&inode->i_count, &inode_lock))
++ return;
++
++ if (!inode->i_nlink) {
++ list_del(&inode->i_hash);
++ INIT_LIST_HEAD(&inode->i_hash);
++ list_del(&inode->i_list);
++ INIT_LIST_HEAD(&inode->i_list);
++ inode->i_state|=I_FREEING;
++ inodes_stat.nr_inodes--;
++ spin_unlock(&inode_lock);
++
++ if (inode->i_data.nrpages)
++ truncate_inode_pages(&inode->i_data, 0);
++
++ if (op && op->delete_inode) {
++ void (*delete)(struct inode *) = op->delete_inode;
++ if (!is_bad_inode(inode))
++ DQUOT_INIT(inode);
++ /* s_op->delete_inode internally recalls clear_inode() */
++ delete(inode);
++ } else
++ clear_inode(inode);
++ if (inode->i_state != I_CLEAR)
++ BUG();
++ } else {
++ if (!list_empty(&inode->i_hash)) {
++ if (!(inode->i_state & (I_DIRTY|I_LOCK))) {
++ list_del(&inode->i_list);
++ list_add(&inode->i_list, &inode_unused);
++ }
++ inodes_stat.nr_unused++;
++ spin_unlock(&inode_lock);
++ if (!sb || (sb->s_flags & MS_ACTIVE))
++ return;
++ write_inode_now(inode, 1);
++ spin_lock(&inode_lock);
++ inodes_stat.nr_unused--;
++ list_del_init(&inode->i_hash);
++ }
++ list_del_init(&inode->i_list);
++ inode->i_state|=I_FREEING;
++ inodes_stat.nr_inodes--;
++ spin_unlock(&inode_lock);
++ if (inode->i_data.nrpages)
++ truncate_inode_pages(&inode->i_data, 0);
++ clear_inode(inode);
++ }
++ destroy_inode(inode);
++ }
++}
++
++void force_delete(struct inode *inode)
++{
++ /*
++ * Kill off unused inodes ... iput() will unhash and
++ * delete the inode if we set i_nlink to zero.
++ */
++ if (atomic_read(&inode->i_count) == 1)
++ inode->i_nlink = 0;
++}
++
++/**
++ * bmap - find a block number in a file
++ * @inode: inode of file
++ * @block: block to find
++ *
++ * Returns the block number on the device holding the inode that
++ * is the disk block number for the block of the file requested.
++ * That is, asked for block 4 of inode 1 the function will return the
++ * disk block relative to the disk start that holds that block of the
++ * file.
++ */
++
++int bmap(struct inode * inode, int block)
++{
++ int res = 0;
++ if (inode->i_mapping->a_ops->bmap)
++ res = inode->i_mapping->a_ops->bmap(inode->i_mapping, block);
++ return res;
++}
++
++/*
++ * Initialize the hash tables.
++ */
++void __init inode_init(unsigned long mempages)
++{
++ struct list_head *head;
++ unsigned long order;
++ unsigned int nr_hash;
++ int i;
++
++ mempages >>= (14 - PAGE_SHIFT);
++ mempages *= sizeof(struct list_head);
++ for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++)
++ ;
++
++ do {
++ unsigned long tmp;
++
++ nr_hash = (1UL << order) * PAGE_SIZE /
++ sizeof(struct list_head);
++ i_hash_mask = (nr_hash - 1);
++
++ tmp = nr_hash;
++ i_hash_shift = 0;
++ while ((tmp >>= 1UL) != 0UL)
++ i_hash_shift++;
++
++ inode_hashtable = (struct list_head *)
++ __get_free_pages(GFP_ATOMIC, order);
++ } while (inode_hashtable == NULL && --order >= 0);
++
++ printk(KERN_INFO "Inode cache hash table entries: %d (order: %ld, %ld bytes)\n",
++ nr_hash, order, (PAGE_SIZE << order));
++
++ if (!inode_hashtable)
++ panic("Failed to allocate inode hash table\n");
++
++ head = inode_hashtable;
++ i = nr_hash;
++ do {
++ INIT_LIST_HEAD(head);
++ head++;
++ i--;
++ } while (i);
++
++ /* inode slab cache */
++ inode_cachep = kmem_cache_create("inode_cache", sizeof(struct inode),
++ 0, SLAB_HWCACHE_ALIGN, init_once,
++ NULL);
++ if (!inode_cachep)
++ panic("cannot create inode slab cache");
++
++ unused_inodes_flush_task.routine = try_to_sync_unused_inodes;
++}
++
++/**
++ * update_atime - update the access time
++ * @inode: inode accessed
++ *
++ * Update the accessed time on an inode and mark it for writeback.
++ * This function automatically handles read only file systems and media,
++ * as well as the "noatime" flag and inode specific "noatime" markers.
++ */
++
++void update_atime (struct inode *inode)
++{
++ if (inode->i_atime == CURRENT_TIME)
++ return;
++ if ( IS_NOATIME (inode) ) return;
++ if ( IS_NODIRATIME (inode) && S_ISDIR (inode->i_mode) ) return;
++ if ( IS_RDONLY (inode) ) return;
++ inode->i_atime = CURRENT_TIME;
++ mark_inode_dirty_sync (inode);
++} /* End Function update_atime */
++
++
++/*
++ * Quota functions that want to walk the inode lists..
++ */
++#ifdef CONFIG_QUOTA
++
++/* Functions back in dquot.c */
++void put_dquot_list(struct list_head *);
++int remove_inode_dquot_ref(struct inode *, int, struct list_head *);
++
++void remove_dquot_ref(struct super_block *sb, int type)
++{
++ struct inode *inode;
++ struct list_head *act_head;
++ LIST_HEAD(tofree_head);
++
++ if (!sb->dq_op)
++ return; /* nothing to do */
++ /* We have to be protected against other CPUs */
++ lock_kernel(); /* This lock is for quota code */
++ spin_lock(&inode_lock); /* This lock is for inodes code */
++
++ list_for_each(act_head, &inode_in_use) {
++ inode = list_entry(act_head, struct inode, i_list);
++ if (inode->i_sb == sb && IS_QUOTAINIT(inode))
++ remove_inode_dquot_ref(inode, type, &tofree_head);
++ }
++ list_for_each(act_head, &inode_unused) {
++ inode = list_entry(act_head, struct inode, i_list);
++ if (inode->i_sb == sb && IS_QUOTAINIT(inode))
++ remove_inode_dquot_ref(inode, type, &tofree_head);
++ }
++ list_for_each(act_head, &sb->s_dirty) {
++ inode = list_entry(act_head, struct inode, i_list);
++ if (IS_QUOTAINIT(inode))
++ remove_inode_dquot_ref(inode, type, &tofree_head);
++ }
++ list_for_each(act_head, &sb->s_locked_inodes) {
++ inode = list_entry(act_head, struct inode, i_list);
++ if (IS_QUOTAINIT(inode))
++ remove_inode_dquot_ref(inode, type, &tofree_head);
++ }
++ spin_unlock(&inode_lock);
++ unlock_kernel();
++
++ put_dquot_list(&tofree_head);
++}
++
++#endif
+--- /dev/null 2003-01-30 18:24:37.000000000 +0800
++++ linux-2.4.19-hp3_pnnl1-root/fs/block_dev.c 2002-08-03 08:39:45.000000000 +0800
+@@ -0,0 +1,695 @@
++/*
++ * linux/fs/block_dev.c
++ *
++ * Copyright (C) 1991, 1992 Linus Torvalds
++ * Copyright (C) 2001 Andrea Arcangeli <andrea@suse.de> SuSE
++ */
++
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/mm.h>
++#include <linux/locks.h>
++#include <linux/fcntl.h>
++#include <linux/slab.h>
++#include <linux/kmod.h>
++#include <linux/major.h>
++#include <linux/devfs_fs_kernel.h>
++#include <linux/smp_lock.h>
++#include <linux/iobuf.h>
++#include <linux/highmem.h>
++#include <linux/blkdev.h>
++#include <linux/module.h>
++
++#include <asm/uaccess.h>
++
++static unsigned long max_block(kdev_t dev)
++{
++ unsigned int retval = ~0U;
++ int major = MAJOR(dev);
++
++ if (blk_size[major]) {
++ int minor = MINOR(dev);
++ unsigned int blocks = blk_size[major][minor];
++ if (blocks) {
++ unsigned int size = block_size(dev);
++ unsigned int sizebits = blksize_bits(size);
++ blocks += (size-1) >> BLOCK_SIZE_BITS;
++ retval = blocks << (BLOCK_SIZE_BITS - sizebits);
++ if (sizebits > BLOCK_SIZE_BITS)
++ retval = blocks >> (sizebits - BLOCK_SIZE_BITS);
++ }
++ }
++ return retval;
++}
++
++static loff_t blkdev_size(kdev_t dev)
++{
++ unsigned int blocks = ~0U;
++ int major = MAJOR(dev);
++
++ if (blk_size[major]) {
++ int minor = MINOR(dev);
++ blocks = blk_size[major][minor];
++ }
++ return (loff_t) blocks << BLOCK_SIZE_BITS;
++}
++
++/* Kill _all_ buffers, dirty or not.. */
++static void kill_bdev(struct block_device *bdev)
++{
++ invalidate_bdev(bdev, 1);
++ truncate_inode_pages(bdev->bd_inode->i_mapping, 0);
++}
++
++int set_blocksize(kdev_t dev, int size)
++{
++ int oldsize;
++ struct block_device *bdev;
++
++ /* Size must be a power of two, and between 512 and PAGE_SIZE */
++ if (size > PAGE_SIZE || size < 512 || (size & (size-1)))
++ return -EINVAL;
++
++ /* Size cannot be smaller than the size supported by the device */
++ if (size < get_hardsect_size(dev))
++ return -EINVAL;
++
++ /* No blocksize array? Implies hardcoded BLOCK_SIZE */
++ if (!blksize_size[MAJOR(dev)]) {
++ if (size == BLOCK_SIZE)
++ return 0;
++ return -EINVAL;
++ }
++
++ oldsize = blksize_size[MAJOR(dev)][MINOR(dev)];
++ if (oldsize == size)
++ return 0;
++
++ if (!oldsize && size == BLOCK_SIZE) {
++ blksize_size[MAJOR(dev)][MINOR(dev)] = size;
++ return 0;
++ }
++
++ /* Ok, we're actually changing the blocksize.. */
++ bdev = bdget(dev);
++ sync_buffers(dev, 2);
++ blksize_size[MAJOR(dev)][MINOR(dev)] = size;
++ bdev->bd_inode->i_blkbits = blksize_bits(size);
++ kill_bdev(bdev);
++ bdput(bdev);
++ return 0;
++}
++
++int sb_set_blocksize(struct super_block *sb, int size)
++{
++ int bits;
++ if (set_blocksize(sb->s_dev, size) < 0)
++ return 0;
++ sb->s_blocksize = size;
++ for (bits = 9, size >>= 9; size >>= 1; bits++)
++ ;
++ sb->s_blocksize_bits = bits;
++ return sb->s_blocksize;
++}
++
++int sb_min_blocksize(struct super_block *sb, int size)
++{
++ int minsize = get_hardsect_size(sb->s_dev);
++ if (size < minsize)
++ size = minsize;
++ return sb_set_blocksize(sb, size);
++}
++
++static int blkdev_get_block(struct inode * inode, long iblock, struct buffer_head * bh, int create)
++{
++ if (iblock >= max_block(inode->i_rdev))
++ return -EIO;
++
++ bh->b_dev = inode->i_rdev;
++ bh->b_blocknr = iblock;
++ bh->b_state |= 1UL << BH_Mapped;
++ return 0;
++}
++
++static int blkdev_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
++{
++ return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, blkdev_get_block);
++}
++
++static int blkdev_writepage(struct page * page)
++{
++ return block_write_full_page(page, blkdev_get_block);
++}
++
++static int blkdev_readpage(struct file * file, struct page * page)
++{
++ return block_read_full_page(page, blkdev_get_block);
++}
++
++static int blkdev_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
++{
++ return block_prepare_write(page, from, to, blkdev_get_block);
++}
++
++static int blkdev_commit_write(struct file *file, struct page *page, unsigned from, unsigned to)
++{
++ return block_commit_write(page, from, to);
++}
++
++/*
++ * private llseek:
++ * for a block special file file->f_dentry->d_inode->i_size is zero
++ * so we compute the size by hand (just as in block_read/write above)
++ */
++static loff_t block_llseek(struct file *file, loff_t offset, int origin)
++{
++ /* ewww */
++ loff_t size = file->f_dentry->d_inode->i_bdev->bd_inode->i_size;
++ loff_t retval;
++
++ switch (origin) {
++ case 2:
++ offset += size;
++ break;
++ case 1:
++ offset += file->f_pos;
++ }
++ retval = -EINVAL;
++ if (offset >= 0 && offset <= size) {
++ if (offset != file->f_pos) {
++ file->f_pos = offset;
++ file->f_reada = 0;
++ file->f_version = ++event;
++ }
++ retval = offset;
++ }
++ return retval;
++}
++
++
++static int __block_fsync(struct inode * inode)
++{
++ int ret, err;
++
++ ret = filemap_fdatasync(inode->i_mapping);
++ err = sync_buffers(inode->i_rdev, 1);
++ if (err && !ret)
++ ret = err;
++ err = filemap_fdatawait(inode->i_mapping);
++ if (err && !ret)
++ ret = err;
++
++ return ret;
++}
++
++/*
++ * Filp may be NULL when we are called by an msync of a vma
++ * since the vma has no handle.
++ */
++
++static int block_fsync(struct file *filp, struct dentry *dentry, int datasync)
++{
++ struct inode * inode = dentry->d_inode;
++
++ return __block_fsync(inode);
++}
++
++/*
++ * pseudo-fs
++ */
++
++static struct super_block *bd_read_super(struct super_block *sb, void *data, int silent)
++{
++ static struct super_operations sops = {};
++ struct inode *root = new_inode(sb);
++ if (!root)
++ return NULL;
++ root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
++ root->i_uid = root->i_gid = 0;
++ root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME;
++ sb->s_maxbytes = ~0ULL;
++ sb->s_blocksize = 1024;
++ sb->s_blocksize_bits = 10;
++ sb->s_magic = 0x62646576;
++ sb->s_op = &sops;
++ sb->s_root = d_alloc(NULL, &(const struct qstr) { "bdev:", 5, 0 });
++ if (!sb->s_root) {
++ iput(root);
++ return NULL;
++ }
++ sb->s_root->d_sb = sb;
++ sb->s_root->d_parent = sb->s_root;
++ d_instantiate(sb->s_root, root);
++ return sb;
++}
++
++static DECLARE_FSTYPE(bd_type, "bdev", bd_read_super, FS_NOMOUNT);
++
++static struct vfsmount *bd_mnt;
++
++/*
++ * bdev cache handling - shamelessly stolen from inode.c
++ * We use smaller hashtable, though.
++ */
++
++#define HASH_BITS 6
++#define HASH_SIZE (1UL << HASH_BITS)
++#define HASH_MASK (HASH_SIZE-1)
++static struct list_head bdev_hashtable[HASH_SIZE];
++static spinlock_t bdev_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
++static kmem_cache_t * bdev_cachep;
++
++#define alloc_bdev() \
++ ((struct block_device *) kmem_cache_alloc(bdev_cachep, SLAB_KERNEL))
++#define destroy_bdev(bdev) kmem_cache_free(bdev_cachep, (bdev))
++
++static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
++{
++ struct block_device * bdev = (struct block_device *) foo;
++
++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++ SLAB_CTOR_CONSTRUCTOR)
++ {
++ memset(bdev, 0, sizeof(*bdev));
++ sema_init(&bdev->bd_sem, 1);
++ INIT_LIST_HEAD(&bdev->bd_inodes);
++ }
++}
++
++void __init bdev_cache_init(void)
++{
++ int i, err;
++ struct list_head *head = bdev_hashtable;
++
++ i = HASH_SIZE;
++ do {
++ INIT_LIST_HEAD(head);
++ head++;
++ i--;
++ } while (i);
++
++ bdev_cachep = kmem_cache_create("bdev_cache",
++ sizeof(struct block_device),
++ 0, SLAB_HWCACHE_ALIGN, init_once,
++ NULL);
++ if (!bdev_cachep)
++ panic("Cannot create bdev_cache SLAB cache");
++ err = register_filesystem(&bd_type);
++ if (err)
++ panic("Cannot register bdev pseudo-fs");
++ bd_mnt = kern_mount(&bd_type);
++ err = PTR_ERR(bd_mnt);
++ if (IS_ERR(bd_mnt))
++ panic("Cannot create bdev pseudo-fs");
++}
++
++/*
++ * Most likely _very_ bad one - but then it's hardly critical for small
++ * /dev and can be fixed when somebody will need really large one.
++ */
++static inline unsigned long hash(dev_t dev)
++{
++ unsigned long tmp = dev;
++ tmp = tmp + (tmp >> HASH_BITS) + (tmp >> HASH_BITS*2);
++ return tmp & HASH_MASK;
++}
++
++static struct block_device *bdfind(dev_t dev, struct list_head *head)
++{
++ struct list_head *p;
++ struct block_device *bdev;
++ for (p=head->next; p!=head; p=p->next) {
++ bdev = list_entry(p, struct block_device, bd_hash);
++ if (bdev->bd_dev != dev)
++ continue;
++ atomic_inc(&bdev->bd_count);
++ return bdev;
++ }
++ return NULL;
++}
++
++struct block_device *bdget(dev_t dev)
++{
++ struct list_head * head = bdev_hashtable + hash(dev);
++ struct block_device *bdev, *new_bdev;
++ spin_lock(&bdev_lock);
++ bdev = bdfind(dev, head);
++ spin_unlock(&bdev_lock);
++ if (bdev)
++ return bdev;
++ new_bdev = alloc_bdev();
++ if (new_bdev) {
++ struct inode *inode = new_inode(bd_mnt->mnt_sb);
++ if (inode) {
++ kdev_t kdev = to_kdev_t(dev);
++ atomic_set(&new_bdev->bd_count,1);
++ new_bdev->bd_dev = dev;
++ new_bdev->bd_op = NULL;
++ new_bdev->bd_inode = inode;
++ inode->i_rdev = kdev;
++ inode->i_dev = kdev;
++ inode->i_bdev = new_bdev;
++ inode->i_data.a_ops = &def_blk_aops;
++ inode->i_data.gfp_mask = GFP_USER;
++ inode->i_mode = S_IFBLK;
++ spin_lock(&bdev_lock);
++ bdev = bdfind(dev, head);
++ if (!bdev) {
++ list_add(&new_bdev->bd_hash, head);
++ spin_unlock(&bdev_lock);
++ return new_bdev;
++ }
++ spin_unlock(&bdev_lock);
++ iput(new_bdev->bd_inode);
++ }
++ destroy_bdev(new_bdev);
++ }
++ return bdev;
++}
++
++static inline void __bd_forget(struct inode *inode)
++{
++ list_del_init(&inode->i_devices);
++ inode->i_bdev = NULL;
++ inode->i_mapping = &inode->i_data;
++}
++
++void bdput(struct block_device *bdev)
++{
++ if (atomic_dec_and_lock(&bdev->bd_count, &bdev_lock)) {
++ struct list_head *p;
++ if (bdev->bd_openers)
++ BUG();
++ list_del(&bdev->bd_hash);
++ while ( (p = bdev->bd_inodes.next) != &bdev->bd_inodes ) {
++ __bd_forget(list_entry(p, struct inode, i_devices));
++ }
++ spin_unlock(&bdev_lock);
++ iput(bdev->bd_inode);
++ destroy_bdev(bdev);
++ }
++}
++
++int bd_acquire(struct inode *inode)
++{
++ struct block_device *bdev;
++ spin_lock(&bdev_lock);
++ if (inode->i_bdev) {
++ atomic_inc(&inode->i_bdev->bd_count);
++ spin_unlock(&bdev_lock);
++ return 0;
++ }
++ spin_unlock(&bdev_lock);
++ bdev = bdget(kdev_t_to_nr(inode->i_rdev));
++ if (!bdev)
++ return -ENOMEM;
++ spin_lock(&bdev_lock);
++ if (!inode->i_bdev) {
++ inode->i_bdev = bdev;
++ inode->i_mapping = bdev->bd_inode->i_mapping;
++ list_add(&inode->i_devices, &bdev->bd_inodes);
++ } else if (inode->i_bdev != bdev)
++ BUG();
++ spin_unlock(&bdev_lock);
++ return 0;
++}
++
++/* Call when you free inode */
++
++void bd_forget(struct inode *inode)
++{
++ spin_lock(&bdev_lock);
++ if (inode->i_bdev)
++ __bd_forget(inode);
++ spin_unlock(&bdev_lock);
++}
++
++static struct {
++ const char *name;
++ struct block_device_operations *bdops;
++} blkdevs[MAX_BLKDEV];
++
++int get_blkdev_list(char * p)
++{
++ int i;
++ int len;
++
++ len = sprintf(p, "\nBlock devices:\n");
++ for (i = 0; i < MAX_BLKDEV ; i++) {
++ if (blkdevs[i].bdops) {
++ len += sprintf(p+len, "%3d %s\n", i, blkdevs[i].name);
++ }
++ }
++ return len;
++}
++
++/*
++ Return the function table of a device.
++ Load the driver if needed.
++*/
++const struct block_device_operations * get_blkfops(unsigned int major)
++{
++ const struct block_device_operations *ret = NULL;
++
++ /* major 0 is used for non-device mounts */
++ if (major && major < MAX_BLKDEV) {
++#ifdef CONFIG_KMOD
++ if (!blkdevs[major].bdops) {
++ char name[20];
++ sprintf(name, "block-major-%d", major);
++ request_module(name);
++ }
++#endif
++ ret = blkdevs[major].bdops;
++ }
++ return ret;
++}
++
++int register_blkdev(unsigned int major, const char * name, struct block_device_operations *bdops)
++{
++ if (major == 0) {
++ for (major = MAX_BLKDEV-1; major > 0; major--) {
++ if (blkdevs[major].bdops == NULL) {
++ blkdevs[major].name = name;
++ blkdevs[major].bdops = bdops;
++ return major;
++ }
++ }
++ return -EBUSY;
++ }
++ if (major >= MAX_BLKDEV)
++ return -EINVAL;
++ if (blkdevs[major].bdops && blkdevs[major].bdops != bdops)
++ return -EBUSY;
++ blkdevs[major].name = name;
++ blkdevs[major].bdops = bdops;
++ return 0;
++}
++
++int unregister_blkdev(unsigned int major, const char * name)
++{
++ if (major >= MAX_BLKDEV)
++ return -EINVAL;
++ if (!blkdevs[major].bdops)
++ return -EINVAL;
++ if (strcmp(blkdevs[major].name, name))
++ return -EINVAL;
++ blkdevs[major].name = NULL;
++ blkdevs[major].bdops = NULL;
++ return 0;
++}
++
++/*
++ * This routine checks whether a removable media has been changed,
++ * and invalidates all buffer-cache-entries in that case. This
++ * is a relatively slow routine, so we have to try to minimize using
++ * it. Thus it is called only upon a 'mount' or 'open'. This
++ * is the best way of combining speed and utility, I think.
++ * People changing diskettes in the middle of an operation deserve
++ * to lose :-)
++ */
++int check_disk_change(kdev_t dev)
++{
++ int i;
++ const struct block_device_operations * bdops = NULL;
++
++ i = MAJOR(dev);
++ if (i < MAX_BLKDEV)
++ bdops = blkdevs[i].bdops;
++ if (bdops == NULL) {
++ devfs_handle_t de;
++
++ de = devfs_find_handle (NULL, NULL, i, MINOR (dev),
++ DEVFS_SPECIAL_BLK, 0);
++ if (de) {
++ bdops = devfs_get_ops (de);
++ devfs_put_ops (de); /* We're running in owner module */
++ }
++ }
++ if (bdops == NULL)
++ return 0;
++ if (bdops->check_media_change == NULL)
++ return 0;
++ if (!bdops->check_media_change(dev))
++ return 0;
++
++ if (invalidate_device(dev, 0))
++ printk("VFS: busy inodes on changed media.\n");
++
++ if (bdops->revalidate)
++ bdops->revalidate(dev);
++ return 1;
++}
++
++int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg)
++{
++ int res;
++ mm_segment_t old_fs = get_fs();
++
++ if (!bdev->bd_op->ioctl)
++ return -EINVAL;
++ set_fs(KERNEL_DS);
++ res = bdev->bd_op->ioctl(bdev->bd_inode, NULL, cmd, arg);
++ set_fs(old_fs);
++ return res;
++}
++
++static int do_open(struct block_device *bdev, struct inode *inode, struct file *file)
++{
++ int ret = -ENXIO;
++ kdev_t dev = to_kdev_t(bdev->bd_dev);
++
++ down(&bdev->bd_sem);
++ lock_kernel();
++ if (!bdev->bd_op)
++ bdev->bd_op = get_blkfops(MAJOR(dev));
++ if (bdev->bd_op) {
++ ret = 0;
++ if (bdev->bd_op->owner)
++ __MOD_INC_USE_COUNT(bdev->bd_op->owner);
++ if (bdev->bd_op->open)
++ ret = bdev->bd_op->open(inode, file);
++ if (!ret) {
++ bdev->bd_openers++;
++ bdev->bd_inode->i_size = blkdev_size(dev);
++ bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
++ } else {
++ if (bdev->bd_op->owner)
++ __MOD_DEC_USE_COUNT(bdev->bd_op->owner);
++ if (!bdev->bd_openers)
++ bdev->bd_op = NULL;
++ }
++ }
++ unlock_kernel();
++ up(&bdev->bd_sem);
++ if (ret)
++ bdput(bdev);
++ return ret;
++}
++
++int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags, int kind)
++{
++ /*
++ * This crockload is due to bad choice of ->open() type.
++ * It will go away.
++ * For now, block device ->open() routine must _not_
++ * examine anything in 'inode' argument except ->i_rdev.
++ */
++ struct file fake_file = {};
++ struct dentry fake_dentry = {};
++ fake_file.f_mode = mode;
++ fake_file.f_flags = flags;
++ fake_file.f_dentry = &fake_dentry;
++ fake_dentry.d_inode = bdev->bd_inode;
++
++ return do_open(bdev, bdev->bd_inode, &fake_file);
++}
++
++int blkdev_open(struct inode * inode, struct file * filp)
++{
++ struct block_device *bdev;
++
++ /*
++ * Preserve backwards compatibility and allow large file access
++ * even if userspace doesn't ask for it explicitly. Some mkfs
++ * binary needs it. We might want to drop this workaround
++ * during an unstable branch.
++ */
++ filp->f_flags |= O_LARGEFILE;
++
++ bd_acquire(inode);
++ bdev = inode->i_bdev;
++
++ return do_open(bdev, inode, filp);
++}
++
++int blkdev_put(struct block_device *bdev, int kind)
++{
++ int ret = 0;
++ kdev_t rdev = to_kdev_t(bdev->bd_dev); /* this should become bdev */
++ struct inode *bd_inode = bdev->bd_inode;
++
++ down(&bdev->bd_sem);
++ lock_kernel();
++ if (kind == BDEV_FILE && bdev->bd_openers == 1)
++ __block_fsync(bd_inode);
++ else if (kind == BDEV_FS)
++ fsync_no_super(rdev);
++ if (!--bdev->bd_openers)
++ kill_bdev(bdev);
++ if (bdev->bd_op->release)
++ ret = bdev->bd_op->release(bd_inode, NULL);
++ if (bdev->bd_op->owner)
++ __MOD_DEC_USE_COUNT(bdev->bd_op->owner);
++ if (!bdev->bd_openers)
++ bdev->bd_op = NULL;
++ unlock_kernel();
++ up(&bdev->bd_sem);
++ bdput(bdev);
++ return ret;
++}
++
++int blkdev_close(struct inode * inode, struct file * filp)
++{
++ return blkdev_put(inode->i_bdev, BDEV_FILE);
++}
++
++static int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
++ unsigned long arg)
++{
++ if (inode->i_bdev->bd_op->ioctl)
++ return inode->i_bdev->bd_op->ioctl(inode, file, cmd, arg);
++ return -EINVAL;
++}
++
++struct address_space_operations def_blk_aops = {
++ readpage: blkdev_readpage,
++ writepage: blkdev_writepage,
++ sync_page: block_sync_page,
++ prepare_write: blkdev_prepare_write,
++ commit_write: blkdev_commit_write,
++ direct_IO: blkdev_direct_IO,
++};
++
++struct file_operations def_blk_fops = {
++ open: blkdev_open,
++ release: blkdev_close,
++ llseek: block_llseek,
++ read: generic_file_read,
++ write: generic_file_write,
++ mmap: generic_file_mmap,
++ fsync: block_fsync,
++ ioctl: blkdev_ioctl,
++};
++
++const char * bdevname(kdev_t dev)
++{
++ static char buffer[32];
++ const char * name = blkdevs[MAJOR(dev)].name;
++
++ if (!name)
++ name = "unknown-block";
++
++ sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev));
++ return buffer;
++}
+--- /dev/null 2003-01-30 18:24:37.000000000 +0800
++++ linux-2.4.19-hp3_pnnl1-root/fs/devfs/base.c 2002-08-03 08:39:45.000000000 +0800
+@@ -0,0 +1,3544 @@
++/* devfs (Device FileSystem) driver.
++
++ Copyright (C) 1998-2002 Richard Gooch
++
++ This library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Library General Public
++ License as published by the Free Software Foundation; either
++ version 2 of the License, or (at your option) any later version.
++
++ This library 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
++ Library General Public License for more details.
++
++ You should have received a copy of the GNU Library General Public
++ License along with this library; if not, write to the Free
++ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++
++ Richard Gooch may be reached by email at rgooch@atnf.csiro.au
++ The postal address is:
++ Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
++
++ ChangeLog
++
++ 19980110 Richard Gooch <rgooch@atnf.csiro.au>
++ Original version.
++ v0.1
++ 19980111 Richard Gooch <rgooch@atnf.csiro.au>
++ Created per-fs inode table rather than using inode->u.generic_ip
++ v0.2
++ 19980111 Richard Gooch <rgooch@atnf.csiro.au>
++ Created .epoch inode which has a ctime of 0.
++ Fixed loss of named pipes when dentries lost.
++ Fixed loss of inode data when devfs_register() follows mknod().
++ v0.3
++ 19980111 Richard Gooch <rgooch@atnf.csiro.au>
++ Fix for when compiling with CONFIG_KERNELD.
++ 19980112 Richard Gooch <rgooch@atnf.csiro.au>
++ Fix for readdir() which sometimes didn't show entries.
++ Added <<tolerant>> option to <devfs_register>.
++ v0.4
++ 19980113 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_fill_file> function.
++ v0.5
++ 19980115 Richard Gooch <rgooch@atnf.csiro.au>
++ Added subdirectory support. Major restructuring.
++ 19980116 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed <find_by_dev> to not search major=0,minor=0.
++ Added symlink support.
++ v0.6
++ 19980120 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_mk_dir> function and support directory unregister
++ 19980120 Richard Gooch <rgooch@atnf.csiro.au>
++ Auto-ownership uses real uid/gid rather than effective uid/gid.
++ v0.7
++ 19980121 Richard Gooch <rgooch@atnf.csiro.au>
++ Supported creation of sockets.
++ v0.8
++ 19980122 Richard Gooch <rgooch@atnf.csiro.au>
++ Added DEVFS_FL_HIDE_UNREG flag.
++ Interface change to <devfs_mk_symlink>.
++ Created <devfs_symlink> to support symlink(2).
++ v0.9
++ 19980123 Richard Gooch <rgooch@atnf.csiro.au>
++ Added check to <devfs_fill_file> to check inode is in devfs.
++ Added optional traversal of symlinks.
++ v0.10
++ 19980124 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_get_flags> and <devfs_set_flags>.
++ v0.11
++ 19980125 C. Scott Ananian <cananian@alumni.princeton.edu>
++ Created <devfs_find_handle>.
++ 19980125 Richard Gooch <rgooch@atnf.csiro.au>
++ Allow removal of symlinks.
++ v0.12
++ 19980125 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_set_symlink_destination>.
++ 19980126 Richard Gooch <rgooch@atnf.csiro.au>
++ Moved DEVFS_SUPER_MAGIC into header file.
++ Added DEVFS_FL_HIDE flag.
++ Created <devfs_get_maj_min>.
++ Created <devfs_get_handle_from_inode>.
++ Fixed minor bug in <find_by_dev>.
++ 19980127 Richard Gooch <rgooch@atnf.csiro.au>
++ Changed interface to <find_by_dev>, <find_entry>,
++ <devfs_unregister>, <devfs_fill_file> and <devfs_find_handle>.
++ Fixed inode times when symlink created with symlink(2).
++ v0.13
++ 19980129 C. Scott Ananian <cananian@alumni.princeton.edu>
++ Exported <devfs_set_symlink_destination>, <devfs_get_maj_min>
++ and <devfs_get_handle_from_inode>.
++ 19980129 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_unlink> to support unlink(2).
++ v0.14
++ 19980129 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed kerneld support for entries in devfs subdirectories.
++ 19980130 Richard Gooch <rgooch@atnf.csiro.au>
++ Bugfixes in <call_kerneld>.
++ v0.15
++ 19980207 Richard Gooch <rgooch@atnf.csiro.au>
++ Call kerneld when looking up unregistered entries.
++ v0.16
++ 19980326 Richard Gooch <rgooch@atnf.csiro.au>
++ Modified interface to <devfs_find_handle> for symlink traversal.
++ v0.17
++ 19980331 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed persistence bug with device numbers for manually created
++ device files.
++ Fixed problem with recreating symlinks with different content.
++ v0.18
++ 19980401 Richard Gooch <rgooch@atnf.csiro.au>
++ Changed to CONFIG_KMOD.
++ Hide entries which are manually unlinked.
++ Always invalidate devfs dentry cache when registering entries.
++ Created <devfs_rmdir> to support rmdir(2).
++ Ensure directories created by <devfs_mk_dir> are visible.
++ v0.19
++ 19980402 Richard Gooch <rgooch@atnf.csiro.au>
++ Invalidate devfs dentry cache when making directories.
++ Invalidate devfs dentry cache when removing entries.
++ Fixed persistence bug with fifos.
++ v0.20
++ 19980421 Richard Gooch <rgooch@atnf.csiro.au>
++ Print process command when debugging kerneld/kmod.
++ Added debugging for register/unregister/change operations.
++ 19980422 Richard Gooch <rgooch@atnf.csiro.au>
++ Added "devfs=" boot options.
++ v0.21
++ 19980426 Richard Gooch <rgooch@atnf.csiro.au>
++ No longer lock/unlock superblock in <devfs_put_super>.
++ Drop negative dentries when they are released.
++ Manage dcache more efficiently.
++ v0.22
++ 19980427 Richard Gooch <rgooch@atnf.csiro.au>
++ Added DEVFS_FL_AUTO_DEVNUM flag.
++ v0.23
++ 19980430 Richard Gooch <rgooch@atnf.csiro.au>
++ No longer set unnecessary methods.
++ v0.24
++ 19980504 Richard Gooch <rgooch@atnf.csiro.au>
++ Added PID display to <call_kerneld> debugging message.
++ Added "after" debugging message to <call_kerneld>.
++ 19980519 Richard Gooch <rgooch@atnf.csiro.au>
++ Added "diread" and "diwrite" boot options.
++ 19980520 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed persistence problem with permissions.
++ v0.25
++ 19980602 Richard Gooch <rgooch@atnf.csiro.au>
++ Support legacy device nodes.
++ Fixed bug where recreated inodes were hidden.
++ v0.26
++ 19980602 Richard Gooch <rgooch@atnf.csiro.au>
++ Improved debugging in <get_vfs_inode>.
++ 19980607 Richard Gooch <rgooch@atnf.csiro.au>
++ No longer free old dentries in <devfs_mk_dir>.
++ Free all dentries for a given entry when deleting inodes.
++ v0.27
++ 19980627 Richard Gooch <rgooch@atnf.csiro.au>
++ Limit auto-device numbering to majors 128 to 239.
++ v0.28
++ 19980629 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed inode times persistence problem.
++ v0.29
++ 19980704 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed spelling in <devfs_readlink> debug.
++ Fixed bug in <devfs_setup> parsing "dilookup".
++ v0.30
++ 19980705 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed devfs inode leak when manually recreating inodes.
++ Fixed permission persistence problem when recreating inodes.
++ v0.31
++ 19980727 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed harmless "unused variable" compiler warning.
++ Fixed modes for manually recreated device nodes.
++ v0.32
++ 19980728 Richard Gooch <rgooch@atnf.csiro.au>
++ Added NULL devfs inode warning in <devfs_read_inode>.
++ Force all inode nlink values to 1.
++ v0.33
++ 19980730 Richard Gooch <rgooch@atnf.csiro.au>
++ Added "dimknod" boot option.
++ Set inode nlink to 0 when freeing dentries.
++ Fixed modes for manually recreated symlinks.
++ v0.34
++ 19980802 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed bugs in recreated directories and symlinks.
++ v0.35
++ 19980806 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed bugs in recreated device nodes.
++ 19980807 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed bug in currently unused <devfs_get_handle_from_inode>.
++ Defined new <devfs_handle_t> type.
++ Improved debugging when getting entries.
++ Fixed bug where directories could be emptied.
++ v0.36
++ 19980809 Richard Gooch <rgooch@atnf.csiro.au>
++ Replaced dummy .epoch inode with .devfsd character device.
++ 19980810 Richard Gooch <rgooch@atnf.csiro.au>
++ Implemented devfsd protocol revision 0.
++ v0.37
++ 19980819 Richard Gooch <rgooch@atnf.csiro.au>
++ Added soothing message to warning in <devfs_d_iput>.
++ v0.38
++ 19980829 Richard Gooch <rgooch@atnf.csiro.au>
++ Use GCC extensions for structure initialisations.
++ Implemented async open notification.
++ Incremented devfsd protocol revision to 1.
++ v0.39
++ 19980908 Richard Gooch <rgooch@atnf.csiro.au>
++ Moved async open notification to end of <devfs_open>.
++ v0.40
++ 19980910 Richard Gooch <rgooch@atnf.csiro.au>
++ Prepended "/dev/" to module load request.
++ Renamed <call_kerneld> to <call_kmod>.
++ v0.41
++ 19980910 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed typo "AYSNC" -> "ASYNC".
++ v0.42
++ 19980910 Richard Gooch <rgooch@atnf.csiro.au>
++ Added open flag for files.
++ v0.43
++ 19980927 Richard Gooch <rgooch@atnf.csiro.au>
++ Set i_blocks=0 and i_blksize=1024 in <devfs_read_inode>.
++ v0.44
++ 19981005 Richard Gooch <rgooch@atnf.csiro.au>
++ Added test for empty <<name>> in <devfs_find_handle>.
++ Renamed <generate_path> to <devfs_generate_path> and published.
++ v0.45
++ 19981006 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_get_fops>.
++ v0.46
++ 19981007 Richard Gooch <rgooch@atnf.csiro.au>
++ Limit auto-device numbering to majors 144 to 239.
++ v0.47
++ 19981010 Richard Gooch <rgooch@atnf.csiro.au>
++ Updated <devfs_follow_link> for VFS change in 2.1.125.
++ v0.48
++ 19981022 Richard Gooch <rgooch@atnf.csiro.au>
++ Created DEVFS_ FL_COMPAT flag.
++ v0.49
++ 19981023 Richard Gooch <rgooch@atnf.csiro.au>
++ Created "nocompat" boot option.
++ v0.50
++ 19981025 Richard Gooch <rgooch@atnf.csiro.au>
++ Replaced "mount" boot option with "nomount".
++ v0.51
++ 19981110 Richard Gooch <rgooch@atnf.csiro.au>
++ Created "only" boot option.
++ v0.52
++ 19981112 Richard Gooch <rgooch@atnf.csiro.au>
++ Added DEVFS_FL_REMOVABLE flag.
++ v0.53
++ 19981114 Richard Gooch <rgooch@atnf.csiro.au>
++ Only call <scan_dir_for_removable> on first call to
++ <devfs_readdir>.
++ v0.54
++ 19981205 Richard Gooch <rgooch@atnf.csiro.au>
++ Updated <devfs_rmdir> for VFS change in 2.1.131.
++ v0.55
++ 19981218 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_mk_compat>.
++ 19981220 Richard Gooch <rgooch@atnf.csiro.au>
++ Check for partitions on removable media in <devfs_lookup>.
++ v0.56
++ 19990118 Richard Gooch <rgooch@atnf.csiro.au>
++ Added support for registering regular files.
++ Created <devfs_set_file_size>.
++ Update devfs inodes from entries if not changed through FS.
++ v0.57
++ 19990124 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed <devfs_fill_file> to only initialise temporary inodes.
++ Trap for NULL fops in <devfs_register>.
++ Return -ENODEV in <devfs_fill_file> for non-driver inodes.
++ v0.58
++ 19990126 Richard Gooch <rgooch@atnf.csiro.au>
++ Switched from PATH_MAX to DEVFS_PATHLEN.
++ v0.59
++ 19990127 Richard Gooch <rgooch@atnf.csiro.au>
++ Created "nottycompat" boot option.
++ v0.60
++ 19990318 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed <devfsd_read> to not overrun event buffer.
++ v0.61
++ 19990329 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_auto_unregister>.
++ v0.62
++ 19990330 Richard Gooch <rgooch@atnf.csiro.au>
++ Don't return unregistred entries in <devfs_find_handle>.
++ Panic in <devfs_unregister> if entry unregistered.
++ 19990401 Richard Gooch <rgooch@atnf.csiro.au>
++ Don't panic in <devfs_auto_unregister> for duplicates.
++ v0.63
++ 19990402 Richard Gooch <rgooch@atnf.csiro.au>
++ Don't unregister already unregistered entries in <unregister>.
++ v0.64
++ 19990510 Richard Gooch <rgooch@atnf.csiro.au>
++ Disable warning messages when unable to read partition table for
++ removable media.
++ v0.65
++ 19990512 Richard Gooch <rgooch@atnf.csiro.au>
++ Updated <devfs_lookup> for VFS change in 2.3.1-pre1.
++ Created "oops-on-panic" boot option.
++ Improved debugging in <devfs_register> and <devfs_unregister>.
++ v0.66
++ 19990519 Richard Gooch <rgooch@atnf.csiro.au>
++ Added documentation for some functions.
++ 19990525 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed "oops-on-panic" boot option: now always Oops.
++ v0.67
++ 19990531 Richard Gooch <rgooch@atnf.csiro.au>
++ Improved debugging in <devfs_register>.
++ v0.68
++ 19990604 Richard Gooch <rgooch@atnf.csiro.au>
++ Added "diunlink" and "nokmod" boot options.
++ Removed superfluous warning message in <devfs_d_iput>.
++ v0.69
++ 19990611 Richard Gooch <rgooch@atnf.csiro.au>
++ Took account of change to <d_alloc_root>.
++ v0.70
++ 19990614 Richard Gooch <rgooch@atnf.csiro.au>
++ Created separate event queue for each mounted devfs.
++ Removed <devfs_invalidate_dcache>.
++ Created new ioctl()s.
++ Incremented devfsd protocol revision to 3.
++ Fixed bug when re-creating directories: contents were lost.
++ Block access to inodes until devfsd updates permissions.
++ 19990615 Richard Gooch <rgooch@atnf.csiro.au>
++ Support 2.2.x kernels.
++ v0.71
++ 19990623 Richard Gooch <rgooch@atnf.csiro.au>
++ Switched to sending process uid/gid to devfsd.
++ Renamed <call_kmod> to <try_modload>.
++ Added DEVFSD_NOTIFY_LOOKUP event.
++ 19990624 Richard Gooch <rgooch@atnf.csiro.au>
++ Added DEVFSD_NOTIFY_CHANGE event.
++ Incremented devfsd protocol revision to 4.
++ v0.72
++ 19990713 Richard Gooch <rgooch@atnf.csiro.au>
++ Return EISDIR rather than EINVAL for read(2) on directories.
++ v0.73
++ 19990809 Richard Gooch <rgooch@atnf.csiro.au>
++ Changed <devfs_setup> to new __init scheme.
++ v0.74
++ 19990901 Richard Gooch <rgooch@atnf.csiro.au>
++ Changed remaining function declarations to new __init scheme.
++ v0.75
++ 19991013 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_get_info>, <devfs_set_info>,
++ <devfs_get_first_child> and <devfs_get_next_sibling>.
++ Added <<dir>> parameter to <devfs_register>, <devfs_mk_compat>,
++ <devfs_mk_dir> and <devfs_find_handle>.
++ Work sponsored by SGI.
++ v0.76
++ 19991017 Richard Gooch <rgooch@atnf.csiro.au>
++ Allow multiple unregistrations.
++ Work sponsored by SGI.
++ v0.77
++ 19991026 Richard Gooch <rgooch@atnf.csiro.au>
++ Added major and minor number to devfsd protocol.
++ Incremented devfsd protocol revision to 5.
++ Work sponsored by SGI.
++ v0.78
++ 19991030 Richard Gooch <rgooch@atnf.csiro.au>
++ Support info pointer for all devfs entry types.
++ Added <<info>> parameter to <devfs_mk_dir> and
++ <devfs_mk_symlink>.
++ Work sponsored by SGI.
++ v0.79
++ 19991031 Richard Gooch <rgooch@atnf.csiro.au>
++ Support "../" when searching devfs namespace.
++ Work sponsored by SGI.
++ v0.80
++ 19991101 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_get_unregister_slave>.
++ Work sponsored by SGI.
++ v0.81
++ 19991103 Richard Gooch <rgooch@atnf.csiro.au>
++ Exported <devfs_get_parent>.
++ Work sponsored by SGI.
++ v0.82
++ 19991104 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed unused <devfs_set_symlink_destination>.
++ 19991105 Richard Gooch <rgooch@atnf.csiro.au>
++ Do not hide entries from devfsd or children.
++ Removed DEVFS_ FL_TTY_COMPAT flag.
++ Removed "nottycompat" boot option.
++ Removed <devfs_mk_compat>.
++ Work sponsored by SGI.
++ v0.83
++ 19991107 Richard Gooch <rgooch@atnf.csiro.au>
++ Added DEVFS_FL_WAIT flag.
++ Work sponsored by SGI.
++ v0.84
++ 19991107 Richard Gooch <rgooch@atnf.csiro.au>
++ Support new "disc" naming scheme in <get_removable_partition>.
++ Allow NULL fops in <devfs_register>.
++ Work sponsored by SGI.
++ v0.85
++ 19991110 Richard Gooch <rgooch@atnf.csiro.au>
++ Fall back to major table if NULL fops given to <devfs_register>.
++ Work sponsored by SGI.
++ v0.86
++ 19991204 Richard Gooch <rgooch@atnf.csiro.au>
++ Support fifos when unregistering.
++ Work sponsored by SGI.
++ v0.87
++ 19991209 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed obsolete DEVFS_ FL_COMPAT and DEVFS_ FL_TOLERANT flags.
++ Work sponsored by SGI.
++ v0.88
++ 19991214 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed kmod support.
++ Work sponsored by SGI.
++ v0.89
++ 19991216 Richard Gooch <rgooch@atnf.csiro.au>
++ Improved debugging in <get_vfs_inode>.
++ Ensure dentries created by devfsd will be cleaned up.
++ Work sponsored by SGI.
++ v0.90
++ 19991223 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_get_name>.
++ Work sponsored by SGI.
++ v0.91
++ 20000203 Richard Gooch <rgooch@atnf.csiro.au>
++ Ported to kernel 2.3.42.
++ Removed <devfs_fill_file>.
++ Work sponsored by SGI.
++ v0.92
++ 20000306 Richard Gooch <rgooch@atnf.csiro.au>
++ Added DEVFS_ FL_NO_PERSISTENCE flag.
++ Removed unnecessary call to <update_devfs_inode_from_entry> in
++ <devfs_readdir>.
++ Work sponsored by SGI.
++ v0.93
++ 20000413 Richard Gooch <rgooch@atnf.csiro.au>
++ Set inode->i_size to correct size for symlinks.
++ 20000414 Richard Gooch <rgooch@atnf.csiro.au>
++ Only give lookup() method to directories to comply with new VFS
++ assumptions.
++ Work sponsored by SGI.
++ 20000415 Richard Gooch <rgooch@atnf.csiro.au>
++ Remove unnecessary tests in symlink methods.
++ Don't kill existing block ops in <devfs_read_inode>.
++ Work sponsored by SGI.
++ v0.94
++ 20000424 Richard Gooch <rgooch@atnf.csiro.au>
++ Don't create missing directories in <devfs_find_handle>.
++ Work sponsored by SGI.
++ v0.95
++ 20000430 Richard Gooch <rgooch@atnf.csiro.au>
++ Added CONFIG_DEVFS_MOUNT.
++ Work sponsored by SGI.
++ v0.96
++ 20000608 Richard Gooch <rgooch@atnf.csiro.au>
++ Disabled multi-mount capability (use VFS bindings instead).
++ Work sponsored by SGI.
++ v0.97
++ 20000610 Richard Gooch <rgooch@atnf.csiro.au>
++ Switched to FS_SINGLE to disable multi-mounts.
++ 20000612 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed module support.
++ Removed multi-mount code.
++ Removed compatibility macros: VFS has changed too much.
++ Work sponsored by SGI.
++ v0.98
++ 20000614 Richard Gooch <rgooch@atnf.csiro.au>
++ Merged devfs inode into devfs entry.
++ Work sponsored by SGI.
++ v0.99
++ 20000619 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed dead code in <devfs_register> which used to call
++ <free_dentries>.
++ Work sponsored by SGI.
++ v0.100
++ 20000621 Richard Gooch <rgooch@atnf.csiro.au>
++ Changed interface to <devfs_register>.
++ Work sponsored by SGI.
++ v0.101
++ 20000622 Richard Gooch <rgooch@atnf.csiro.au>
++ Simplified interface to <devfs_mk_symlink> and <devfs_mk_dir>.
++ Simplified interface to <devfs_find_handle>.
++ Work sponsored by SGI.
++ v0.102
++ 20010519 Richard Gooch <rgooch@atnf.csiro.au>
++ Ensure <devfs_generate_path> terminates string for root entry.
++ Exported <devfs_get_name> to modules.
++ 20010520 Richard Gooch <rgooch@atnf.csiro.au>
++ Make <devfs_mk_symlink> send events to devfsd.
++ Cleaned up option processing in <devfs_setup>.
++ 20010521 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed bugs in handling symlinks: could leak or cause Oops.
++ 20010522 Richard Gooch <rgooch@atnf.csiro.au>
++ Cleaned up directory handling by separating fops.
++ v0.103
++ 20010601 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed handling of inverted options in <devfs_setup>.
++ v0.104
++ 20010604 Richard Gooch <rgooch@atnf.csiro.au>
++ Adjusted <try_modload> to account for <devfs_generate_path> fix.
++ v0.105
++ 20010617 Richard Gooch <rgooch@atnf.csiro.au>
++ Answered question posed by Al Viro and removed his comments.
++ Moved setting of registered flag after other fields are changed.
++ Fixed race between <devfsd_close> and <devfsd_notify_one>.
++ Global VFS changes added bogus BKL to <devfsd_close>: removed.
++ Widened locking in <devfs_readlink> and <devfs_follow_link>.
++ Replaced <devfsd_read> stack usage with <devfsd_ioctl> kmalloc.
++ Simplified locking in <devfsd_ioctl> and fixed memory leak.
++ v0.106
++ 20010709 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed broken devnum allocation and use <devfs_alloc_devnum>.
++ Fixed old devnum leak by calling new <devfs_dealloc_devnum>.
++ v0.107
++ 20010712 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed bug in <devfs_setup> which could hang boot process.
++ v0.108
++ 20010730 Richard Gooch <rgooch@atnf.csiro.au>
++ Added DEVFSD_NOTIFY_DELETE event.
++ 20010801 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed #include <asm/segment.h>.
++ v0.109
++ 20010807 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed inode table races by removing it and using
++ inode->u.generic_ip instead.
++ Moved <devfs_read_inode> into <get_vfs_inode>.
++ Moved <devfs_write_inode> into <devfs_notify_change>.
++ v0.110
++ 20010808 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed race in <devfs_do_symlink> for uni-processor.
++ v0.111
++ 20010818 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed remnant of multi-mount support in <devfs_mknod>.
++ Removed unused DEVFS_FL_SHOW_UNREG flag.
++ v0.112
++ 20010820 Richard Gooch <rgooch@atnf.csiro.au>
++ Removed nlink field from struct devfs_inode.
++ v0.113
++ 20010823 Richard Gooch <rgooch@atnf.csiro.au>
++ Replaced BKL with global rwsem to protect symlink data (quick
++ and dirty hack).
++ v0.114
++ 20010827 Richard Gooch <rgooch@atnf.csiro.au>
++ Replaced global rwsem for symlink with per-link refcount.
++ v0.115
++ 20010919 Richard Gooch <rgooch@atnf.csiro.au>
++ Set inode->i_mapping->a_ops for block nodes in <get_vfs_inode>.
++ v0.116
++ 20011008 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed overrun in <devfs_link> by removing function (not needed).
++ 20011009 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed buffer underrun in <try_modload>.
++ 20011029 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed race in <devfsd_ioctl> when setting event mask.
++ 20011114 Richard Gooch <rgooch@atnf.csiro.au>
++ First release of new locking code.
++ v1.0
++ 20011117 Richard Gooch <rgooch@atnf.csiro.au>
++ Discard temporary buffer, now use "%s" for dentry names.
++ 20011118 Richard Gooch <rgooch@atnf.csiro.au>
++ Don't generate path in <try_modload>: use fake entry instead.
++ Use "existing" directory in <_devfs_make_parent_for_leaf>.
++ 20011122 Richard Gooch <rgooch@atnf.csiro.au>
++ Use slab cache rather than fixed buffer for devfsd events.
++ v1.1
++ 20011125 Richard Gooch <rgooch@atnf.csiro.au>
++ Send DEVFSD_NOTIFY_REGISTERED events in <devfs_mk_dir>.
++ 20011127 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed locking bug in <devfs_d_revalidate_wait> due to typo.
++ Do not send CREATE, CHANGE, ASYNC_OPEN or DELETE events from
++ devfsd or children.
++ v1.2
++ 20011202 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed bug in <devfsd_read>: was dereferencing freed pointer.
++ v1.3
++ 20011203 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed bug in <devfsd_close>: was dereferencing freed pointer.
++ Added process group check for devfsd privileges.
++ v1.4
++ 20011204 Richard Gooch <rgooch@atnf.csiro.au>
++ Use SLAB_ATOMIC in <devfsd_notify_de> from <devfs_d_delete>.
++ v1.5
++ 20011211 Richard Gooch <rgooch@atnf.csiro.au>
++ Return old entry in <devfs_mk_dir> for 2.4.x kernels.
++ 20011212 Richard Gooch <rgooch@atnf.csiro.au>
++ Increment refcount on module in <check_disc_changed>.
++ 20011215 Richard Gooch <rgooch@atnf.csiro.au>
++ Created <devfs_get_handle> and exported <devfs_put>.
++ Increment refcount on module in <devfs_get_ops>.
++ Created <devfs_put_ops>.
++ v1.6
++ 20011216 Richard Gooch <rgooch@atnf.csiro.au>
++ Added poisoning to <devfs_put>.
++ Improved debugging messages.
++ v1.7
++ 20011221 Richard Gooch <rgooch@atnf.csiro.au>
++ Corrected (made useful) debugging message in <unregister>.
++ Moved <kmem_cache_create> in <mount_devfs_fs> to <init_devfs_fs>
++ 20011224 Richard Gooch <rgooch@atnf.csiro.au>
++ Added magic number to guard against scribbling drivers.
++ 20011226 Richard Gooch <rgooch@atnf.csiro.au>
++ Only return old entry in <devfs_mk_dir> if a directory.
++ Defined macros for error and debug messages.
++ v1.8
++ 20020113 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed (rare, old) race in <devfs_lookup>.
++ v1.9
++ 20020120 Richard Gooch <rgooch@atnf.csiro.au>
++ Fixed deadlock bug in <devfs_d_revalidate_wait>.
++ Tag VFS deletable in <devfs_mk_symlink> if handle ignored.
++ v1.10
++ 20020129 Richard Gooch <rgooch@atnf.csiro.au>
++ Added KERN_* to remaining messages.
++ Cleaned up declaration of <stat_read>.
++ v1.11
++ 20020219 Richard Gooch <rgooch@atnf.csiro.au>
++ Changed <devfs_rmdir> to allow later additions if not yet empty.
++ v1.12
++ 20020514 Richard Gooch <rgooch@atnf.csiro.au>
++ Added BKL to <devfs_open> because drivers still need it.
++ Protected <scan_dir_for_removable> and <get_removable_partition>
++ from changing directory contents.
++ v1.12a
++*/
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/tty.h>
++#include <linux/timer.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/wait.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/ioport.h>
++#include <linux/delay.h>
++#include <linux/ctype.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/devfs_fs.h>
++#include <linux/devfs_fs_kernel.h>
++#include <linux/smp_lock.h>
++#include <linux/smp.h>
++#include <linux/version.h>
++#include <linux/rwsem.h>
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/processor.h>
++#include <asm/system.h>
++#include <asm/pgtable.h>
++#include <asm/bitops.h>
++#include <asm/atomic.h>
++
++#define DEVFS_VERSION "1.12a (20020514)"
++
++#define DEVFS_NAME "devfs"
++
++#define FIRST_INODE 1
++
++#define STRING_LENGTH 256
++#define FAKE_BLOCK_SIZE 1024
++#define POISON_PTR ( *(void **) poison_array )
++#define MAGIC_VALUE 0x327db823
++
++#ifndef TRUE
++# define TRUE 1
++# define FALSE 0
++#endif
++
++#define MODE_DIR (S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO)
++
++#define IS_HIDDEN(de) ( (de)->hide && !is_devfsd_or_child(fs_info) )
++
++#define DEBUG_NONE 0x0000000
++#define DEBUG_MODULE_LOAD 0x0000001
++#define DEBUG_REGISTER 0x0000002
++#define DEBUG_UNREGISTER 0x0000004
++#define DEBUG_FREE 0x0000008
++#define DEBUG_SET_FLAGS 0x0000010
++#define DEBUG_S_READ 0x0000100 /* Break */
++#define DEBUG_I_LOOKUP 0x0001000 /* Break */
++#define DEBUG_I_CREATE 0x0002000
++#define DEBUG_I_GET 0x0004000
++#define DEBUG_I_CHANGE 0x0008000
++#define DEBUG_I_UNLINK 0x0010000
++#define DEBUG_I_RLINK 0x0020000
++#define DEBUG_I_FLINK 0x0040000
++#define DEBUG_I_MKNOD 0x0080000
++#define DEBUG_F_READDIR 0x0100000 /* Break */
++#define DEBUG_D_DELETE 0x1000000 /* Break */
++#define DEBUG_D_RELEASE 0x2000000
++#define DEBUG_D_IPUT 0x4000000
++#define DEBUG_ALL 0xfffffff
++#define DEBUG_DISABLED DEBUG_NONE
++
++#define OPTION_NONE 0x00
++#define OPTION_MOUNT 0x01
++#define OPTION_ONLY 0x02
++
++#define PRINTK(format, args...) \
++ {printk (KERN_ERR "%s" format, __FUNCTION__ , ## args);}
++
++#define OOPS(format, args...) \
++ {printk (KERN_CRIT "%s" format, __FUNCTION__ , ## args); \
++ printk ("Forcing Oops\n"); \
++ BUG();}
++
++#ifdef CONFIG_DEVFS_DEBUG
++# define VERIFY_ENTRY(de) \
++ {if ((de) && (de)->magic_number != MAGIC_VALUE) \
++ OOPS ("(%p): bad magic value: %x\n", (de), (de)->magic_number);}
++# define WRITE_ENTRY_MAGIC(de,magic) (de)->magic_number = (magic)
++# define DPRINTK(flag, format, args...) \
++ {if (devfs_debug & flag) \
++ printk (KERN_INFO "%s" format, __FUNCTION__ , ## args);}
++#else
++# define VERIFY_ENTRY(de)
++# define WRITE_ENTRY_MAGIC(de,magic)
++# define DPRINTK(flag, format, args...)
++#endif
++
++
++struct directory_type
++{
++ rwlock_t lock; /* Lock for searching(R)/updating(W) */
++ struct devfs_entry *first;
++ struct devfs_entry *last;
++ unsigned short num_removable; /* Lock for writing but not reading */
++ unsigned char no_more_additions:1;
++};
++
++struct file_type
++{
++ unsigned long size;
++};
++
++struct device_type
++{
++ unsigned short major;
++ unsigned short minor;
++};
++
++struct fcb_type /* File, char, block type */
++{
++ void *ops;
++ union
++ {
++ struct file_type file;
++ struct device_type device;
++ }
++ u;
++ unsigned char auto_owner:1;
++ unsigned char aopen_notify:1;
++ unsigned char removable:1; /* Belongs in device_type, but save space */
++ unsigned char open:1; /* Not entirely correct */
++ unsigned char autogen:1; /* Belongs in device_type, but save space */
++};
++
++struct symlink_type
++{
++ unsigned int length; /* Not including the NULL-termimator */
++ char *linkname; /* This is NULL-terminated */
++};
++
++struct devfs_inode /* This structure is for "persistent" inode storage */
++{
++ struct dentry *dentry;
++ time_t atime;
++ time_t mtime;
++ time_t ctime;
++ unsigned int ino; /* Inode number as seen in the VFS */
++ uid_t uid;
++ gid_t gid;
++};
++
++struct devfs_entry
++{
++#ifdef CONFIG_DEVFS_DEBUG
++ unsigned int magic_number;
++#endif
++ void *info;
++ atomic_t refcount; /* When this drops to zero, it's unused */
++ union
++ {
++ struct directory_type dir;
++ struct fcb_type fcb;
++ struct symlink_type symlink;
++ const char *name; /* Only used for (mode == 0) */
++ }
++ u;
++ struct devfs_entry *prev; /* Previous entry in the parent directory */
++ struct devfs_entry *next; /* Next entry in the parent directory */
++ struct devfs_entry *parent; /* The parent directory */
++ struct devfs_entry *slave; /* Another entry to unregister */
++ struct devfs_inode inode;
++ umode_t mode;
++ unsigned short namelen; /* I think 64k+ filenames are a way off... */
++ unsigned char hide:1;
++ unsigned char vfs_deletable:1;/* Whether the VFS may delete the entry */
++ char name[1]; /* This is just a dummy: the allocated array
++ is bigger. This is NULL-terminated */
++};
++
++/* The root of the device tree */
++static struct devfs_entry *root_entry;
++
++struct devfsd_buf_entry
++{
++ struct devfs_entry *de; /* The name is generated with this */
++ unsigned short type; /* The type of event */
++ umode_t mode;
++ uid_t uid;
++ gid_t gid;
++ struct devfsd_buf_entry *next;
++};
++
++struct fs_info /* This structure is for the mounted devfs */
++{
++ struct super_block *sb;
++ spinlock_t devfsd_buffer_lock; /* Lock when inserting/deleting events */
++ struct devfsd_buf_entry *devfsd_first_event;
++ struct devfsd_buf_entry *devfsd_last_event;
++ volatile int devfsd_sleeping;
++ volatile struct task_struct *devfsd_task;
++ volatile pid_t devfsd_pgrp;
++ volatile struct file *devfsd_file;
++ struct devfsd_notify_struct *devfsd_info;
++ volatile unsigned long devfsd_event_mask;
++ atomic_t devfsd_overrun_count;
++ wait_queue_head_t devfsd_wait_queue; /* Wake devfsd on input */
++ wait_queue_head_t revalidate_wait_queue; /* Wake when devfsd sleeps */
++};
++
++static struct fs_info fs_info = {devfsd_buffer_lock: SPIN_LOCK_UNLOCKED};
++static kmem_cache_t *devfsd_buf_cache;
++#ifdef CONFIG_DEVFS_DEBUG
++static unsigned int devfs_debug_init __initdata = DEBUG_NONE;
++static unsigned int devfs_debug = DEBUG_NONE;
++static spinlock_t stat_lock = SPIN_LOCK_UNLOCKED;
++static unsigned int stat_num_entries;
++static unsigned int stat_num_bytes;
++#endif
++static unsigned char poison_array[8] =
++ {0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a};
++
++#ifdef CONFIG_DEVFS_MOUNT
++static unsigned int boot_options = OPTION_MOUNT;
++#else
++static unsigned int boot_options = OPTION_NONE;
++#endif
++
++/* Forward function declarations */
++static devfs_handle_t _devfs_walk_path (struct devfs_entry *dir,
++ const char *name, int namelen,
++ int traverse_symlink);
++static ssize_t devfsd_read (struct file *file, char *buf, size_t len,
++ loff_t *ppos);
++static int devfsd_ioctl (struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg);
++static int devfsd_close (struct inode *inode, struct file *file);
++#ifdef CONFIG_DEVFS_DEBUG
++static ssize_t stat_read (struct file *file, char *buf, size_t len,
++ loff_t *ppos);
++static struct file_operations stat_fops =
++{
++ read: stat_read,
++};
++#endif
++
++
++/* Devfs daemon file operations */
++static struct file_operations devfsd_fops =
++{
++ read: devfsd_read,
++ ioctl: devfsd_ioctl,
++ release: devfsd_close,
++};
++
++
++/* Support functions follow */
++
++
++/**
++ * devfs_get - Get a reference to a devfs entry.
++ * @de: The devfs entry.
++ */
++
++static struct devfs_entry *devfs_get (struct devfs_entry *de)
++{
++ VERIFY_ENTRY (de);
++ if (de) atomic_inc (&de->refcount);
++ return de;
++} /* End Function devfs_get */
++
++/**
++ * devfs_put - Put (release) a reference to a devfs entry.
++ * @de: The handle to the devfs entry.
++ */
++
++void devfs_put (devfs_handle_t de)
++{
++ if (!de) return;
++ VERIFY_ENTRY (de);
++ if (de->info == POISON_PTR) OOPS ("(%p): poisoned pointer\n", de);
++ if ( !atomic_dec_and_test (&de->refcount) ) return;
++ if (de == root_entry) OOPS ("(%p): root entry being freed\n", de);
++ DPRINTK (DEBUG_FREE, "(%s): de: %p, parent: %p \"%s\"\n",
++ de->name, de, de->parent,
++ de->parent ? de->parent->name : "no parent");
++ if ( S_ISLNK (de->mode) ) kfree (de->u.symlink.linkname);
++ if ( ( S_ISCHR (de->mode) || S_ISBLK (de->mode) ) && de->u.fcb.autogen )
++ {
++ devfs_dealloc_devnum ( S_ISCHR (de->mode) ? DEVFS_SPECIAL_CHR :
++ DEVFS_SPECIAL_BLK,
++ mk_kdev (de->u.fcb.u.device.major,
++ de->u.fcb.u.device.minor) );
++ }
++ WRITE_ENTRY_MAGIC (de, 0);
++#ifdef CONFIG_DEVFS_DEBUG
++ spin_lock (&stat_lock);
++ --stat_num_entries;
++ stat_num_bytes -= sizeof *de + de->namelen;
++ if ( S_ISLNK (de->mode) ) stat_num_bytes -= de->u.symlink.length + 1;
++ spin_unlock (&stat_lock);
++#endif
++ de->info = POISON_PTR;
++ kfree (de);
++} /* End Function devfs_put */
++
++/**
++ * _devfs_search_dir - Search for a devfs entry in a directory.
++ * @dir: The directory to search.
++ * @name: The name of the entry to search for.
++ * @namelen: The number of characters in @name.
++ *
++ * Search for a devfs entry in a directory and returns a pointer to the entry
++ * on success, else %NULL. The directory must be locked already.
++ * An implicit devfs_get() is performed on the returned entry.
++ */
++
++static struct devfs_entry *_devfs_search_dir (struct devfs_entry *dir,
++ const char *name,
++ unsigned int namelen)
++{
++ struct devfs_entry *curr;
++
++ if ( !S_ISDIR (dir->mode) )
++ {
++ PRINTK ("(%s): not a directory\n", dir->name);
++ return NULL;
++ }
++ for (curr = dir->u.dir.first; curr != NULL; curr = curr->next)
++ {
++ if (curr->namelen != namelen) continue;
++ if (memcmp (curr->name, name, namelen) == 0) break;
++ /* Not found: try the next one */
++ }
++ return devfs_get (curr);
++} /* End Function _devfs_search_dir */
++
++
++/**
++ * _devfs_alloc_entry - Allocate a devfs entry.
++ * @name: The name of the entry.
++ * @namelen: The number of characters in @name.
++ *
++ * Allocate a devfs entry and returns a pointer to the entry on success, else
++ * %NULL.
++ */
++
++static struct devfs_entry *_devfs_alloc_entry (const char *name,
++ unsigned int namelen,
++ umode_t mode)
++{
++ struct devfs_entry *new;
++ static unsigned long inode_counter = FIRST_INODE;
++ static spinlock_t counter_lock = SPIN_LOCK_UNLOCKED;
++
++ if ( name && (namelen < 1) ) namelen = strlen (name);
++ if ( ( new = kmalloc (sizeof *new + namelen, GFP_KERNEL) ) == NULL )
++ return NULL;
++ memset (new, 0, sizeof *new + namelen); /* Will set '\0' on name */
++ new->mode = mode;
++ if ( S_ISDIR (mode) ) rwlock_init (&new->u.dir.lock);
++ atomic_set (&new->refcount, 1);
++ spin_lock (&counter_lock);
++ new->inode.ino = inode_counter++;
++ spin_unlock (&counter_lock);
++ if (name) memcpy (new->name, name, namelen);
++ new->namelen = namelen;
++ WRITE_ENTRY_MAGIC (new, MAGIC_VALUE);
++#ifdef CONFIG_DEVFS_DEBUG
++ spin_lock (&stat_lock);
++ ++stat_num_entries;
++ stat_num_bytes += sizeof *new + namelen;
++ spin_unlock (&stat_lock);
++#endif
++ return new;
++} /* End Function _devfs_alloc_entry */
++
++
++/**
++ * _devfs_append_entry - Append a devfs entry to a directory's child list.
++ * @dir: The directory to add to.
++ * @de: The devfs entry to append.
++ * @removable: If TRUE, increment the count of removable devices for %dir.
++ * @old_de: If an existing entry exists, it will be written here. This may
++ * be %NULL. An implicit devfs_get() is performed on this entry.
++ *
++ * Append a devfs entry to a directory's list of children, checking first to
++ * see if an entry of the same name exists. The directory will be locked.
++ * The value 0 is returned on success, else a negative error code.
++ * On failure, an implicit devfs_put() is performed on %de.
++ */
++
++static int _devfs_append_entry (devfs_handle_t dir, devfs_handle_t de,
++ int removable, devfs_handle_t *old_de)
++{
++ int retval;
++
++ if (old_de) *old_de = NULL;
++ if ( !S_ISDIR (dir->mode) )
++ {
++ PRINTK ("(%s): dir: \"%s\" is not a directory\n", de->name, dir->name);
++ devfs_put (de);
++ return -ENOTDIR;
++ }
++ write_lock (&dir->u.dir.lock);
++ if (dir->u.dir.no_more_additions) retval = -ENOENT;
++ else
++ {
++ struct devfs_entry *old;
++
++ old = _devfs_search_dir (dir, de->name, de->namelen);
++ if (old_de) *old_de = old;
++ else devfs_put (old);
++ if (old == NULL)
++ {
++ de->parent = dir;
++ de->prev = dir->u.dir.last;
++ /* Append to the directory's list of children */
++ if (dir->u.dir.first == NULL) dir->u.dir.first = de;
++ else dir->u.dir.last->next = de;
++ dir->u.dir.last = de;
++ if (removable) ++dir->u.dir.num_removable;
++ retval = 0;
++ }
++ else retval = -EEXIST;
++ }
++ write_unlock (&dir->u.dir.lock);
++ if (retval) devfs_put (de);
++ return retval;
++} /* End Function _devfs_append_entry */
++
++
++/**
++ * _devfs_get_root_entry - Get the root devfs entry.
++ *
++ * Returns the root devfs entry on success, else %NULL.
++ */
++
++static struct devfs_entry *_devfs_get_root_entry (void)
++{
++ kdev_t devnum;
++ struct devfs_entry *new;
++ static spinlock_t root_lock = SPIN_LOCK_UNLOCKED;
++
++ /* Always ensure the root is created */
++ if (root_entry) return root_entry;
++ if ( ( new = _devfs_alloc_entry (NULL, 0,MODE_DIR) ) == NULL ) return NULL;
++ spin_lock (&root_lock);
++ if (root_entry)
++ {
++ spin_unlock (&root_lock);
++ devfs_put (new);
++ return (root_entry);
++ }
++ root_entry = new;
++ spin_unlock (&root_lock);
++ /* And create the entry for ".devfsd" */
++ if ( ( new = _devfs_alloc_entry (".devfsd", 0, S_IFCHR |S_IRUSR |S_IWUSR) )
++ == NULL ) return NULL;
++ devnum = devfs_alloc_devnum (DEVFS_SPECIAL_CHR);
++ new->u.fcb.u.device.major = major (devnum);
++ new->u.fcb.u.device.minor = minor (devnum);
++ new->u.fcb.ops = &devfsd_fops;
++ _devfs_append_entry (root_entry, new, FALSE, NULL);
++#ifdef CONFIG_DEVFS_DEBUG
++ if ( ( new = _devfs_alloc_entry (".stat", 0, S_IFCHR | S_IRUGO | S_IWUGO) )
++ == NULL ) return NULL;
++ devnum = devfs_alloc_devnum (DEVFS_SPECIAL_CHR);
++ new->u.fcb.u.device.major = major (devnum);
++ new->u.fcb.u.device.minor = minor (devnum);
++ new->u.fcb.ops = &stat_fops;
++ _devfs_append_entry (root_entry, new, FALSE, NULL);
++#endif
++ return root_entry;
++} /* End Function _devfs_get_root_entry */
++
++
++/**
++ * _devfs_descend - Descend down a tree using the next component name.
++ * @dir: The directory to search.
++ * @name: The component name to search for.
++ * @namelen: The length of %name.
++ * @next_pos: The position of the next '/' or '\0' is written here.
++ *
++ * Descend into a directory, searching for a component. This function forms
++ * the core of a tree-walking algorithm. The directory will be locked.
++ * The devfs entry corresponding to the component is returned. If there is
++ * no matching entry, %NULL is returned.
++ * An implicit devfs_get() is performed on the returned entry.
++ */
++
++static struct devfs_entry *_devfs_descend (struct devfs_entry *dir,
++ const char *name, int namelen,
++ int *next_pos)
++{
++ const char *stop, *ptr;
++ struct devfs_entry *entry;
++
++ if ( (namelen >= 3) && (strncmp (name, "../", 3) == 0) )
++ { /* Special-case going to parent directory */
++ *next_pos = 3;
++ return devfs_get (dir->parent);
++ }
++ stop = name + namelen;
++ /* Search for a possible '/' */
++ for (ptr = name; (ptr < stop) && (*ptr != '/'); ++ptr);
++ *next_pos = ptr - name;
++ read_lock (&dir->u.dir.lock);
++ entry = _devfs_search_dir (dir, name, *next_pos);
++ read_unlock (&dir->u.dir.lock);
++ return entry;
++} /* End Function _devfs_descend */
++
++
++static devfs_handle_t _devfs_make_parent_for_leaf (struct devfs_entry *dir,
++ const char *name,
++ int namelen, int *leaf_pos)
++{
++ int next_pos = 0;
++
++ if (dir == NULL) dir = _devfs_get_root_entry ();
++ if (dir == NULL) return NULL;
++ devfs_get (dir);
++ /* Search for possible trailing component and ignore it */
++ for (--namelen; (namelen > 0) && (name[namelen] != '/'); --namelen);
++ *leaf_pos = (name[namelen] == '/') ? (namelen + 1) : 0;
++ for (; namelen > 0; name += next_pos, namelen -= next_pos)
++ {
++ struct devfs_entry *de, *old;
++
++ if ( ( de = _devfs_descend (dir, name, namelen, &next_pos) ) == NULL )
++ {
++ de = _devfs_alloc_entry (name, next_pos, MODE_DIR);
++ devfs_get (de);
++ if ( !de || _devfs_append_entry (dir, de, FALSE, &old) )
++ {
++ devfs_put (de);
++ if ( !old || !S_ISDIR (old->mode) )
++ {
++ devfs_put (old);
++ devfs_put (dir);
++ return NULL;
++ }
++ de = old; /* Use the existing directory */
++ }
++ }
++ if (de == dir->parent)
++ {
++ devfs_put (dir);
++ devfs_put (de);
++ return NULL;
++ }
++ devfs_put (dir);
++ dir = de;
++ if (name[next_pos] == '/') ++next_pos;
++ }
++ return dir;
++} /* End Function _devfs_make_parent_for_leaf */
++
++
++static devfs_handle_t _devfs_prepare_leaf (devfs_handle_t *dir,
++ const char *name, umode_t mode)
++{
++ int namelen, leaf_pos;
++ struct devfs_entry *de;
++
++ namelen = strlen (name);
++ if ( ( *dir = _devfs_make_parent_for_leaf (*dir, name, namelen,
++ &leaf_pos) ) == NULL )
++ {
++ PRINTK ("(%s): could not create parent path\n", name);
++ return NULL;
++ }
++ if ( ( de = _devfs_alloc_entry (name + leaf_pos, namelen - leaf_pos,mode) )
++ == NULL )
++ {
++ PRINTK ("(%s): could not allocate entry\n", name);
++ devfs_put (*dir);
++ return NULL;
++ }
++ return de;
++} /* End Function _devfs_prepare_leaf */
++
++
++static devfs_handle_t _devfs_walk_path (struct devfs_entry *dir,
++ const char *name, int namelen,
++ int traverse_symlink)
++{
++ int next_pos = 0;
++
++ if (dir == NULL) dir = _devfs_get_root_entry ();
++ if (dir == NULL) return NULL;
++ devfs_get (dir);
++ for (; namelen > 0; name += next_pos, namelen -= next_pos)
++ {
++ struct devfs_entry *de, *link;
++
++ if ( ( de = _devfs_descend (dir, name, namelen, &next_pos) ) == NULL )
++ {
++ devfs_put (dir);
++ return NULL;
++ }
++ if (S_ISLNK (de->mode) && traverse_symlink)
++ { /* Need to follow the link: this is a stack chomper */
++ link = _devfs_walk_path (dir, de->u.symlink.linkname,
++ de->u.symlink.length, TRUE);
++ devfs_put (de);
++ if (!link)
++ {
++ devfs_put (dir);
++ return NULL;
++ }
++ de = link;
++ }
++ devfs_put (dir);
++ dir = de;
++ if (name[next_pos] == '/') ++next_pos;
++ }
++ return dir;
++} /* End Function _devfs_walk_path */
++
++
++/**
++ * _devfs_find_by_dev - Find a devfs entry in a directory.
++ * @dir: The directory where to search
++ * @major: The major number to search for.
++ * @minor: The minor number to search for.
++ * @type: The type of special file to search for. This may be either
++ * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK.
++ *
++ * Returns the devfs_entry pointer on success, else %NULL. An implicit
++ * devfs_get() is performed.
++ */
++
++static struct devfs_entry *_devfs_find_by_dev (struct devfs_entry *dir,
++ unsigned int major,
++ unsigned int minor, char type)
++{
++ struct devfs_entry *entry, *de;
++
++ devfs_get (dir);
++ if (dir == NULL) return NULL;
++ if ( !S_ISDIR (dir->mode) )
++ {
++ PRINTK ("(%p): not a directory\n", dir);
++ devfs_put (dir);
++ return NULL;
++ }
++ /* First search files in this directory */
++ read_lock (&dir->u.dir.lock);
++ for (entry = dir->u.dir.first; entry != NULL; entry = entry->next)
++ {
++ if ( !S_ISCHR (entry->mode) && !S_ISBLK (entry->mode) ) continue;
++ if ( S_ISCHR (entry->mode) && (type != DEVFS_SPECIAL_CHR) ) continue;
++ if ( S_ISBLK (entry->mode) && (type != DEVFS_SPECIAL_BLK) ) continue;
++ if ( (entry->u.fcb.u.device.major == major) &&
++ (entry->u.fcb.u.device.minor == minor) )
++ {
++ devfs_get (entry);
++ read_unlock (&dir->u.dir.lock);
++ devfs_put (dir);
++ return entry;
++ }
++ /* Not found: try the next one */
++ }
++ /* Now recursively search the subdirectories: this is a stack chomper */
++ for (entry = dir->u.dir.first; entry != NULL; entry = entry->next)
++ {
++ if ( !S_ISDIR (entry->mode) ) continue;
++ de = _devfs_find_by_dev (entry, major, minor, type);
++ if (de)
++ {
++ read_unlock (&dir->u.dir.lock);
++ devfs_put (dir);
++ return de;
++ }
++ }
++ read_unlock (&dir->u.dir.lock);
++ devfs_put (dir);
++ return NULL;
++} /* End Function _devfs_find_by_dev */
++
++
++/**
++ * _devfs_find_entry - Find a devfs entry.
++ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
++ * name is relative to the root of the devfs.
++ * @name: The name of the entry. This may be %NULL.
++ * @major: The major number. This is used if lookup by @name fails.
++ * @minor: The minor number. This is used if lookup by @name fails.
++ * NOTE: If @major and @minor are both 0, searching by major and minor
++ * numbers is disabled.
++ * @type: The type of special file to search for. This may be either
++ * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK.
++ * @traverse_symlink: If %TRUE then symbolic links are traversed.
++ *
++ * Returns the devfs_entry pointer on success, else %NULL. An implicit
++ * devfs_get() is performed.
++ */
++
++static struct devfs_entry *_devfs_find_entry (devfs_handle_t dir,
++ const char *name,
++ unsigned int major,
++ unsigned int minor,
++ char type, int traverse_symlink)
++{
++ struct devfs_entry *entry;
++
++ if (name != NULL)
++ {
++ unsigned int namelen = strlen (name);
++
++ if (name[0] == '/')
++ {
++ /* Skip leading pathname component */
++ if (namelen < 2)
++ {
++ PRINTK ("(%s): too short\n", name);
++ return NULL;
++ }
++ for (++name, --namelen; (*name != '/') && (namelen > 0);
++ ++name, --namelen);
++ if (namelen < 2)
++ {
++ PRINTK ("(%s): too short\n", name);
++ return NULL;
++ }
++ ++name;
++ --namelen;
++ }
++ entry = _devfs_walk_path (dir, name, namelen, traverse_symlink);
++ if (entry != NULL) return entry;
++ }
++ /* Have to search by major and minor: slow */
++ if ( (major == 0) && (minor == 0) ) return NULL;
++ return _devfs_find_by_dev (root_entry, major, minor, type);
++} /* End Function _devfs_find_entry */
++
++static struct devfs_entry *get_devfs_entry_from_vfs_inode (struct inode *inode)
++{
++ if (inode == NULL) return NULL;
++ VERIFY_ENTRY ( (struct devfs_entry *) inode->u.generic_ip );
++ return inode->u.generic_ip;
++} /* End Function get_devfs_entry_from_vfs_inode */
++
++
++/**
++ * free_dentry - Free the dentry for a device entry and invalidate inode.
++ * @de: The entry.
++ *
++ * This must only be called after the entry has been unhooked from it's
++ * parent directory.
++ */
++
++static void free_dentry (struct devfs_entry *de)
++{
++ struct dentry *dentry = de->inode.dentry;
++
++ if (!dentry) return;
++ spin_lock (&dcache_lock);
++ dget_locked (dentry);
++ spin_unlock (&dcache_lock);
++ /* Forcefully remove the inode */
++ if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0;
++ d_drop (dentry);
++ dput (dentry);
++} /* End Function free_dentry */
++
++
++/**
++ * is_devfsd_or_child - Test if the current process is devfsd or one of its children.
++ * @fs_info: The filesystem information.
++ *
++ * Returns %TRUE if devfsd or child, else %FALSE.
++ */
++
++static int is_devfsd_or_child (struct fs_info *fs_info)
++{
++ struct task_struct *p;
++
++ if (current == fs_info->devfsd_task) return (TRUE);
++ if (current->pgrp == fs_info->devfsd_pgrp) return (TRUE);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,1)
++ for (p = current->p_opptr; p != &init_task; p = p->p_opptr)
++ {
++ if (p == fs_info->devfsd_task) return (TRUE);
++ }
++#endif
++ return (FALSE);
++} /* End Function is_devfsd_or_child */
++
++
++/**
++ * devfsd_queue_empty - Test if devfsd has work pending in its event queue.
++ * @fs_info: The filesystem information.
++ *
++ * Returns %TRUE if the queue is empty, else %FALSE.
++ */
++
++static inline int devfsd_queue_empty (struct fs_info *fs_info)
++{
++ return (fs_info->devfsd_last_event) ? FALSE : TRUE;
++} /* End Function devfsd_queue_empty */
++
++
++/**
++ * wait_for_devfsd_finished - Wait for devfsd to finish processing its event queue.
++ * @fs_info: The filesystem information.
++ *
++ * Returns %TRUE if no more waiting will be required, else %FALSE.
++ */
++
++static int wait_for_devfsd_finished (struct fs_info *fs_info)
++{
++ DECLARE_WAITQUEUE (wait, current);
++
++ if (fs_info->devfsd_task == NULL) return (TRUE);
++ if (devfsd_queue_empty (fs_info) && fs_info->devfsd_sleeping) return TRUE;
++ if ( is_devfsd_or_child (fs_info) ) return (FALSE);
++ add_wait_queue (&fs_info->revalidate_wait_queue, &wait);
++ current->state = TASK_UNINTERRUPTIBLE;
++ if (!devfsd_queue_empty (fs_info) || !fs_info->devfsd_sleeping)
++ if (fs_info->devfsd_task) schedule ();
++ remove_wait_queue (&fs_info->revalidate_wait_queue, &wait);
++ current->state = TASK_RUNNING;
++ return (TRUE);
++} /* End Function wait_for_devfsd_finished */
++
++
++/**
++ * devfsd_notify_de - Notify the devfsd daemon of a change.
++ * @de: The devfs entry that has changed. This and all parent entries will
++ * have their reference counts incremented if the event was queued.
++ * @type: The type of change.
++ * @mode: The mode of the entry.
++ * @uid: The user ID.
++ * @gid: The group ID.
++ * @fs_info: The filesystem info.
++ *
++ * Returns %TRUE if an event was queued and devfsd woken up, else %FALSE.
++ */
++
++static int devfsd_notify_de (struct devfs_entry *de,
++ unsigned short type, umode_t mode,
++ uid_t uid, gid_t gid, struct fs_info *fs_info,
++ int atomic)
++{
++ struct devfsd_buf_entry *entry;
++ struct devfs_entry *curr;
++
++ if ( !( fs_info->devfsd_event_mask & (1 << type) ) ) return (FALSE);
++ if ( ( entry = kmem_cache_alloc (devfsd_buf_cache,
++ atomic ? SLAB_ATOMIC : SLAB_KERNEL) )
++ == NULL )
++ {
++ atomic_inc (&fs_info->devfsd_overrun_count);
++ return (FALSE);
++ }
++ for (curr = de; curr != NULL; curr = curr->parent) devfs_get (curr);
++ entry->de = de;
++ entry->type = type;
++ entry->mode = mode;
++ entry->uid = uid;
++ entry->gid = gid;
++ entry->next = NULL;
++ spin_lock (&fs_info->devfsd_buffer_lock);
++ if (!fs_info->devfsd_first_event) fs_info->devfsd_first_event = entry;
++ if (fs_info->devfsd_last_event) fs_info->devfsd_last_event->next = entry;
++ fs_info->devfsd_last_event = entry;
++ spin_unlock (&fs_info->devfsd_buffer_lock);
++ wake_up_interruptible (&fs_info->devfsd_wait_queue);
++ return (TRUE);
++} /* End Function devfsd_notify_de */
++
++
++/**
++ * devfsd_notify - Notify the devfsd daemon of a change.
++ * @de: The devfs entry that has changed.
++ * @type: The type of change event.
++ * @wait: If TRUE, the function waits for the daemon to finish processing
++ * the event.
++ */
++
++static void devfsd_notify (struct devfs_entry *de,unsigned short type,int wait)
++{
++ if (devfsd_notify_de (de, type, de->mode, current->euid,
++ current->egid, &fs_info, 0) && wait)
++ wait_for_devfsd_finished (&fs_info);
++} /* End Function devfsd_notify */
++
++
++/**
++ * devfs_register - Register a device entry.
++ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
++ * new name is relative to the root of the devfs.
++ * @name: The name of the entry.
++ * @flags: A set of bitwise-ORed flags (DEVFS_FL_*).
++ * @major: The major number. Not needed for regular files.
++ * @minor: The minor number. Not needed for regular files.
++ * @mode: The default file mode.
++ * @ops: The &file_operations or &block_device_operations structure.
++ * This must not be externally deallocated.
++ * @info: An arbitrary pointer which will be written to the @private_data
++ * field of the &file structure passed to the device driver. You can set
++ * this to whatever you like, and change it once the file is opened (the next
++ * file opened will not see this change).
++ *
++ * Returns a handle which may later be used in a call to devfs_unregister().
++ * On failure %NULL is returned.
++ */
++
++devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
++ unsigned int flags,
++ unsigned int major, unsigned int minor,
++ umode_t mode, void *ops, void *info)
++{
++ char devtype = S_ISCHR (mode) ? DEVFS_SPECIAL_CHR : DEVFS_SPECIAL_BLK;
++ int err;
++ kdev_t devnum = NODEV;
++ struct devfs_entry *de;
++
++ if (name == NULL)
++ {
++ PRINTK ("(): NULL name pointer\n");
++ return NULL;
++ }
++ if (ops == NULL)
++ {
++ if ( S_ISBLK (mode) ) ops = (void *) get_blkfops (major);
++ if (ops == NULL)
++ {
++ PRINTK ("(%s): NULL ops pointer\n", name);
++ return NULL;
++ }
++ PRINTK ("(%s): NULL ops, got %p from major table\n", name, ops);
++ }
++ if ( S_ISDIR (mode) )
++ {
++ PRINTK ("(%s): creating directories is not allowed\n", name);
++ return NULL;
++ }
++ if ( S_ISLNK (mode) )
++ {
++ PRINTK ("(%s): creating symlinks is not allowed\n", name);
++ return NULL;
++ }
++ if ( ( S_ISCHR (mode) || S_ISBLK (mode) ) &&
++ (flags & DEVFS_FL_AUTO_DEVNUM) )
++ {
++ if ( kdev_none ( devnum = devfs_alloc_devnum (devtype) ) )
++ {
++ PRINTK ("(%s): exhausted %s device numbers\n",
++ name, S_ISCHR (mode) ? "char" : "block");
++ return NULL;
++ }
++ major = major (devnum);
++ minor = minor (devnum);
++ }
++ if ( ( de = _devfs_prepare_leaf (&dir, name, mode) ) == NULL )
++ {
++ PRINTK ("(%s): could not prepare leaf\n", name);
++ if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum);
++ return NULL;
++ }
++ if ( S_ISCHR (mode) || S_ISBLK (mode) )
++ {
++ de->u.fcb.u.device.major = major;
++ de->u.fcb.u.device.minor = minor;
++ de->u.fcb.autogen = kdev_none (devnum) ? FALSE : TRUE;
++ }
++ else if ( !S_ISREG (mode) )
++ {
++ PRINTK ("(%s): illegal mode: %x\n", name, mode);
++ devfs_put (de);
++ devfs_put (dir);
++ return (NULL);
++ }
++ de->info = info;
++ if (flags & DEVFS_FL_CURRENT_OWNER)
++ {
++ de->inode.uid = current->uid;
++ de->inode.gid = current->gid;
++ }
++ else
++ {
++ de->inode.uid = 0;
++ de->inode.gid = 0;
++ }
++ de->u.fcb.ops = ops;
++ de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE;
++ de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE : FALSE;
++ de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
++ if (flags & DEVFS_FL_REMOVABLE) de->u.fcb.removable = TRUE;
++ if ( ( err = _devfs_append_entry (dir, de, de->u.fcb.removable, NULL) )
++ != 0 )
++ {
++ PRINTK ("(%s): could not append to parent, err: %d\n", name, err);
++ devfs_put (dir);
++ if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum);
++ return NULL;
++ }
++ DPRINTK (DEBUG_REGISTER, "(%s): de: %p dir: %p \"%s\" pp: %p\n",
++ name, de, dir, dir->name, dir->parent);
++ devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
++ devfs_put (dir);
++ return de;
++} /* End Function devfs_register */
++
++
++/**
++ * _devfs_unhook - Unhook a device entry from its parents list
++ * @de: The entry to unhook.
++ *
++ * Returns %TRUE if the entry was unhooked, else %FALSE if it was
++ * previously unhooked.
++ * The caller must have a write lock on the parent directory.
++ */
++
++static int _devfs_unhook (struct devfs_entry *de)
++{
++ struct devfs_entry *parent;
++
++ if ( !de || (de->prev == de) ) return FALSE;
++ parent = de->parent;
++ if (de->prev == NULL) parent->u.dir.first = de->next;
++ else de->prev->next = de->next;
++ if (de->next == NULL) parent->u.dir.last = de->prev;
++ else de->next->prev = de->prev;
++ de->prev = de; /* Indicate we're unhooked */
++ de->next = NULL; /* Force early termination for <devfs_readdir> */
++ if ( ( S_ISREG (de->mode) || S_ISCHR (de->mode) || S_ISBLK (de->mode) ) &&
++ de->u.fcb.removable )
++ --parent->u.dir.num_removable;
++ return TRUE;
++} /* End Function _devfs_unhook */
++
++
++/**
++ * _devfs_unregister - Unregister a device entry from it's parent.
++ * @dir: The parent directory.
++ * @de: The entry to unregister.
++ *
++ * The caller must have a write lock on the parent directory, which is
++ * unlocked by this function.
++ */
++
++static void _devfs_unregister (struct devfs_entry *dir, struct devfs_entry *de)
++{
++ int unhooked = _devfs_unhook (de);
++
++ write_unlock (&dir->u.dir.lock);
++ if (!unhooked) return;
++ devfs_get (dir);
++ devfs_unregister (de->slave); /* Let it handle the locking */
++ devfsd_notify (de, DEVFSD_NOTIFY_UNREGISTERED, 0);
++ free_dentry (de);
++ devfs_put (dir);
++ if ( !S_ISDIR (de->mode) ) return;
++ while (TRUE) /* Recursively unregister: this is a stack chomper */
++ {
++ struct devfs_entry *child;
++
++ write_lock (&de->u.dir.lock);
++ de->u.dir.no_more_additions = TRUE;
++ child = de->u.dir.first;
++ VERIFY_ENTRY (child);
++ _devfs_unregister (de, child);
++ if (!child) break;
++ DPRINTK (DEBUG_UNREGISTER, "(%s): child: %p refcount: %d\n",
++ child->name, child, atomic_read (&child->refcount) );
++ devfs_put (child);
++ }
++} /* End Function _devfs_unregister */
++
++
++/**
++ * devfs_unregister - Unregister a device entry.
++ * @de: A handle previously created by devfs_register() or returned from
++ * devfs_get_handle(). If this is %NULL the routine does nothing.
++ */
++
++void devfs_unregister (devfs_handle_t de)
++{
++ VERIFY_ENTRY (de);
++ if ( (de == NULL) || (de->parent == NULL) ) return;
++ DPRINTK (DEBUG_UNREGISTER, "(%s): de: %p refcount: %d\n",
++ de->name, de, atomic_read (&de->refcount) );
++ write_lock (&de->parent->u.dir.lock);
++ _devfs_unregister (de->parent, de);
++ devfs_put (de);
++} /* End Function devfs_unregister */
++
++static int devfs_do_symlink (devfs_handle_t dir, const char *name,
++ unsigned int flags, const char *link,
++ devfs_handle_t *handle, void *info)
++{
++ int err;
++ unsigned int linklength;
++ char *newlink;
++ struct devfs_entry *de;
++
++ if (handle != NULL) *handle = NULL;
++ if (name == NULL)
++ {
++ PRINTK ("(): NULL name pointer\n");
++ return -EINVAL;
++ }
++ if (link == NULL)
++ {
++ PRINTK ("(%s): NULL link pointer\n", name);
++ return -EINVAL;
++ }
++ linklength = strlen (link);
++ if ( ( newlink = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL )
++ return -ENOMEM;
++ memcpy (newlink, link, linklength);
++ newlink[linklength] = '\0';
++ if ( ( de = _devfs_prepare_leaf (&dir, name, S_IFLNK | S_IRUGO | S_IXUGO) )
++ == NULL )
++ {
++ PRINTK ("(%s): could not prepare leaf\n", name);
++ kfree (newlink);
++ return -ENOTDIR;
++ }
++ de->info = info;
++ de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
++ de->u.symlink.linkname = newlink;
++ de->u.symlink.length = linklength;
++ if ( ( err = _devfs_append_entry (dir, de, FALSE, NULL) ) != 0 )
++ {
++ PRINTK ("(%s): could not append to parent, err: %d\n", name, err);
++ devfs_put (dir);
++ return err;
++ }
++ devfs_put (dir);
++#ifdef CONFIG_DEVFS_DEBUG
++ spin_lock (&stat_lock);
++ stat_num_bytes += linklength + 1;
++ spin_unlock (&stat_lock);
++#endif
++ if (handle != NULL) *handle = de;
++ return 0;
++} /* End Function devfs_do_symlink */
++
++
++/**
++ * devfs_mk_symlink Create a symbolic link in the devfs namespace.
++ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
++ * new name is relative to the root of the devfs.
++ * @name: The name of the entry.
++ * @flags: A set of bitwise-ORed flags (DEVFS_FL_*).
++ * @link: The destination name.
++ * @handle: The handle to the symlink entry is written here. This may be %NULL.
++ * @info: An arbitrary pointer which will be associated with the entry.
++ *
++ * Returns 0 on success, else a negative error code is returned.
++ */
++
++int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags,
++ const char *link, devfs_handle_t *handle, void *info)
++{
++ int err;
++ devfs_handle_t de;
++
++ if (handle != NULL) *handle = NULL;
++ DPRINTK (DEBUG_REGISTER, "(%s)\n", name);
++ err = devfs_do_symlink (dir, name, flags, link, &de, info);
++ if (err) return err;
++ if (handle == NULL) de->vfs_deletable = TRUE;
++ else *handle = de;
++ devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
++ return 0;
++} /* End Function devfs_mk_symlink */
++
++
++/**
++ * devfs_mk_dir - Create a directory in the devfs namespace.
++ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
++ * new name is relative to the root of the devfs.
++ * @name: The name of the entry.
++ * @info: An arbitrary pointer which will be associated with the entry.
++ *
++ * Use of this function is optional. The devfs_register() function
++ * will automatically create intermediate directories as needed. This function
++ * is provided for efficiency reasons, as it provides a handle to a directory.
++ * Returns a handle which may later be used in a call to devfs_unregister().
++ * On failure %NULL is returned.
++ */
++
++devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, void *info)
++{
++ int err;
++ struct devfs_entry *de, *old;
++
++ if (name == NULL)
++ {
++ PRINTK ("(): NULL name pointer\n");
++ return NULL;
++ }
++ if ( ( de = _devfs_prepare_leaf (&dir, name, MODE_DIR) ) == NULL )
++ {
++ PRINTK ("(%s): could not prepare leaf\n", name);
++ return NULL;
++ }
++ de->info = info;
++ if ( ( err = _devfs_append_entry (dir, de, FALSE, &old) ) != 0 )
++ {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,1)
++ if ( old && S_ISDIR (old->mode) )
++ {
++ PRINTK ("(%s): using old entry in dir: %p \"%s\"\n",
++ name, dir, dir->name);
++ old->vfs_deletable = FALSE;
++ devfs_put (dir);
++ return old;
++ }
++#endif
++ PRINTK ("(%s): could not append to dir: %p \"%s\", err: %d\n",
++ name, dir, dir->name, err);
++ devfs_put (old);
++ devfs_put (dir);
++ return NULL;
++ }
++ DPRINTK (DEBUG_REGISTER, "(%s): de: %p dir: %p \"%s\"\n",
++ name, de, dir, dir->name);
++ devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, 0);
++ devfs_put (dir);
++ return de;
++} /* End Function devfs_mk_dir */
++
++
++/**
++ * devfs_get_handle - Find the handle of a devfs entry.
++ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
++ * name is relative to the root of the devfs.
++ * @name: The name of the entry.
++ * @major: The major number. This is used if @name is %NULL.
++ * @minor: The minor number. This is used if @name is %NULL.
++ * @type: The type of special file to search for. This may be either
++ * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK.
++ * @traverse_symlinks: If %TRUE then symlink entries in the devfs namespace are
++ * traversed. Symlinks pointing out of the devfs namespace will cause a
++ * failure. Symlink traversal consumes stack space.
++ *
++ * Returns a handle which may later be used in a call to
++ * devfs_unregister(), devfs_get_flags(), or devfs_set_flags(). A
++ * subsequent devfs_put() is required to decrement the refcount.
++ * On failure %NULL is returned.
++ */
++
++devfs_handle_t devfs_get_handle (devfs_handle_t dir, const char *name,
++ unsigned int major, unsigned int minor,
++ char type, int traverse_symlinks)
++{
++ if ( (name != NULL) && (name[0] == '\0') ) name = NULL;
++ return _devfs_find_entry (dir, name, major, minor, type,traverse_symlinks);
++} /* End Function devfs_get_handle */
++
++
++/* Compatibility function. Will be removed in sometime in 2.5 */
++
++devfs_handle_t devfs_find_handle (devfs_handle_t dir, const char *name,
++ unsigned int major, unsigned int minor,
++ char type, int traverse_symlinks)
++{
++ devfs_handle_t de;
++
++ de = devfs_get_handle (dir, name, major, minor, type, traverse_symlinks);
++ devfs_put (de);
++ return de;
++} /* End Function devfs_find_handle */
++
++
++/**
++ * devfs_get_flags - Get the flags for a devfs entry.
++ * @de: The handle to the device entry.
++ * @flags: The flags are written here.
++ *
++ * Returns 0 on success, else a negative error code.
++ */
++
++int devfs_get_flags (devfs_handle_t de, unsigned int *flags)
++{
++ unsigned int fl = 0;
++
++ if (de == NULL) return -EINVAL;
++ VERIFY_ENTRY (de);
++ if (de->hide) fl |= DEVFS_FL_HIDE;
++ if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
++ {
++ if (de->u.fcb.auto_owner) fl |= DEVFS_FL_AUTO_OWNER;
++ if (de->u.fcb.aopen_notify) fl |= DEVFS_FL_AOPEN_NOTIFY;
++ if (de->u.fcb.removable) fl |= DEVFS_FL_REMOVABLE;
++ }
++ *flags = fl;
++ return 0;
++} /* End Function devfs_get_flags */
++
++
++/*
++ * devfs_set_flags - Set the flags for a devfs entry.
++ * @de: The handle to the device entry.
++ * @flags: The flags to set. Unset flags are cleared.
++ *
++ * Returns 0 on success, else a negative error code.
++ */
++
++int devfs_set_flags (devfs_handle_t de, unsigned int flags)
++{
++ if (de == NULL) return -EINVAL;
++ VERIFY_ENTRY (de);
++ DPRINTK (DEBUG_SET_FLAGS, "(%s): flags: %x\n", de->name, flags);
++ de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
++ if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
++ {
++ de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE;
++ de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE:FALSE;
++ }
++ return 0;
++} /* End Function devfs_set_flags */
++
++
++/**
++ * devfs_get_maj_min - Get the major and minor numbers for a devfs entry.
++ * @de: The handle to the device entry.
++ * @major: The major number is written here. This may be %NULL.
++ * @minor: The minor number is written here. This may be %NULL.
++ *
++ * Returns 0 on success, else a negative error code.
++ */
++
++int devfs_get_maj_min (devfs_handle_t de, unsigned int *major,
++ unsigned int *minor)
++{
++ if (de == NULL) return -EINVAL;
++ VERIFY_ENTRY (de);
++ if ( S_ISDIR (de->mode) ) return -EISDIR;
++ if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) ) return -EINVAL;
++ if (major != NULL) *major = de->u.fcb.u.device.major;
++ if (minor != NULL) *minor = de->u.fcb.u.device.minor;
++ return 0;
++} /* End Function devfs_get_maj_min */
++
++
++/**
++ * devfs_get_handle_from_inode - Get the devfs handle for a VFS inode.
++ * @inode: The VFS inode.
++ *
++ * Returns the devfs handle on success, else %NULL.
++ */
++
++devfs_handle_t devfs_get_handle_from_inode (struct inode *inode)
++{
++ if (!inode || !inode->i_sb) return NULL;
++ if (inode->i_sb->s_magic != DEVFS_SUPER_MAGIC) return NULL;
++ return get_devfs_entry_from_vfs_inode (inode);
++} /* End Function devfs_get_handle_from_inode */
++
++
++/**
++ * devfs_generate_path - Generate a pathname for an entry, relative to the devfs root.
++ * @de: The devfs entry.
++ * @path: The buffer to write the pathname to. The pathname and '\0'
++ * terminator will be written at the end of the buffer.
++ * @buflen: The length of the buffer.
++ *
++ * Returns the offset in the buffer where the pathname starts on success,
++ * else a negative error code.
++ */
++
++int devfs_generate_path (devfs_handle_t de, char *path, int buflen)
++{
++ int pos;
++#define NAMEOF(de) ( (de)->mode ? (de)->name : (de)->u.name )
++
++ if (de == NULL) return -EINVAL;
++ VERIFY_ENTRY (de);
++ if (de->namelen >= buflen) return -ENAMETOOLONG; /* Must be first */
++ path[buflen - 1] = '\0';
++ if (de->parent == NULL) return buflen - 1; /* Don't prepend root */
++ pos = buflen - de->namelen - 1;
++ memcpy (path + pos, NAMEOF (de), de->namelen);
++ for (de = de->parent; de->parent != NULL; de = de->parent)
++ {
++ if (pos - de->namelen - 1 < 0) return -ENAMETOOLONG;
++ path[--pos] = '/';
++ pos -= de->namelen;
++ memcpy (path + pos, NAMEOF (de), de->namelen);
++ }
++ return pos;
++} /* End Function devfs_generate_path */
++
++
++/**
++ * devfs_get_ops - Get the device operations for a devfs entry.
++ * @de: The handle to the device entry.
++ *
++ * Returns a pointer to the device operations on success, else NULL.
++ * The use count for the module owning the operations will be incremented.
++ */
++
++void *devfs_get_ops (devfs_handle_t de)
++{
++ struct module *owner;
++
++ if (de == NULL) return NULL;
++ VERIFY_ENTRY (de);
++ if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
++ return NULL;
++ if (de->u.fcb.ops == NULL) return NULL;
++ read_lock (&de->parent->u.dir.lock); /* Prevent module from unloading */
++ if (de->next == de) owner = NULL; /* Ops pointer is already stale */
++ else if ( S_ISCHR (de->mode) || S_ISREG (de->mode) )
++ owner = ( (struct file_operations *) de->u.fcb.ops )->owner;
++ else owner = ( (struct block_device_operations *) de->u.fcb.ops )->owner;
++ if ( (de->next == de) || !try_inc_mod_count (owner) )
++ { /* Entry is already unhooked or module is unloading */
++ read_unlock (&de->parent->u.dir.lock);
++ return NULL;
++ }
++ read_unlock (&de->parent->u.dir.lock); /* Module can continue unloading*/
++ return de->u.fcb.ops;
++} /* End Function devfs_get_ops */
++
++
++/**
++ * devfs_put_ops - Put the device operations for a devfs entry.
++ * @de: The handle to the device entry.
++ *
++ * The use count for the module owning the operations will be decremented.
++ */
++
++void devfs_put_ops (devfs_handle_t de)
++{
++ struct module *owner;
++
++ if (de == NULL) return;
++ VERIFY_ENTRY (de);
++ if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
++ return;
++ if (de->u.fcb.ops == NULL) return;
++ if ( S_ISCHR (de->mode) || S_ISREG (de->mode) )
++ owner = ( (struct file_operations *) de->u.fcb.ops )->owner;
++ else owner = ( (struct block_device_operations *) de->u.fcb.ops )->owner;
++ if (owner) __MOD_DEC_USE_COUNT (owner);
++} /* End Function devfs_put_ops */
++
++
++/**
++ * devfs_set_file_size - Set the file size for a devfs regular file.
++ * @de: The handle to the device entry.
++ * @size: The new file size.
++ *
++ * Returns 0 on success, else a negative error code.
++ */
++
++int devfs_set_file_size (devfs_handle_t de, unsigned long size)
++{
++ if (de == NULL) return -EINVAL;
++ VERIFY_ENTRY (de);
++ if ( !S_ISREG (de->mode) ) return -EINVAL;
++ if (de->u.fcb.u.file.size == size) return 0;
++ de->u.fcb.u.file.size = size;
++ if (de->inode.dentry == NULL) return 0;
++ if (de->inode.dentry->d_inode == NULL) return 0;
++ de->inode.dentry->d_inode->i_size = size;
++ return 0;
++} /* End Function devfs_set_file_size */
++
++
++/**
++ * devfs_get_info - Get the info pointer written to private_data of @de upon open.
++ * @de: The handle to the device entry.
++ *
++ * Returns the info pointer.
++ */
++void *devfs_get_info (devfs_handle_t de)
++{
++ if (de == NULL) return NULL;
++ VERIFY_ENTRY (de);
++ return de->info;
++} /* End Function devfs_get_info */
++
++
++/**
++ * devfs_set_info - Set the info pointer written to private_data upon open.
++ * @de: The handle to the device entry.
++ * @info: pointer to the data
++ *
++ * Returns 0 on success, else a negative error code.
++ */
++int devfs_set_info (devfs_handle_t de, void *info)
++{
++ if (de == NULL) return -EINVAL;
++ VERIFY_ENTRY (de);
++ de->info = info;
++ return 0;
++} /* End Function devfs_set_info */
++
++
++/**
++ * devfs_get_parent - Get the parent device entry.
++ * @de: The handle to the device entry.
++ *
++ * Returns the parent device entry if it exists, else %NULL.
++ */
++devfs_handle_t devfs_get_parent (devfs_handle_t de)
++{
++ if (de == NULL) return NULL;
++ VERIFY_ENTRY (de);
++ return de->parent;
++} /* End Function devfs_get_parent */
++
++
++/**
++ * devfs_get_first_child - Get the first leaf node in a directory.
++ * @de: The handle to the device entry.
++ *
++ * Returns the leaf node device entry if it exists, else %NULL.
++ */
++
++devfs_handle_t devfs_get_first_child (devfs_handle_t de)
++{
++ if (de == NULL) return NULL;
++ VERIFY_ENTRY (de);
++ if ( !S_ISDIR (de->mode) ) return NULL;
++ return de->u.dir.first;
++} /* End Function devfs_get_first_child */
++
++
++/**
++ * devfs_get_next_sibling - Get the next sibling leaf node. for a device entry.
++ * @de: The handle to the device entry.
++ *
++ * Returns the leaf node device entry if it exists, else %NULL.
++ */
++
++devfs_handle_t devfs_get_next_sibling (devfs_handle_t de)
++{
++ if (de == NULL) return NULL;
++ VERIFY_ENTRY (de);
++ return de->next;
++} /* End Function devfs_get_next_sibling */
++
++
++/**
++ * devfs_auto_unregister - Configure a devfs entry to be automatically unregistered.
++ * @master: The master devfs entry. Only one slave may be registered.
++ * @slave: The devfs entry which will be automatically unregistered when the
++ * master entry is unregistered. It is illegal to call devfs_unregister()
++ * on this entry.
++ */
++
++void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave)
++{
++ if (master == NULL) return;
++ VERIFY_ENTRY (master);
++ VERIFY_ENTRY (slave);
++ if (master->slave != NULL)
++ {
++ /* Because of the dumbness of the layers above, ignore duplicates */
++ if (master->slave == slave) return;
++ PRINTK ("(%s): only one slave allowed\n", master->name);
++ OOPS ("(): old slave: \"%s\" new slave: \"%s\"\n",
++ master->slave->name, slave->name);
++ }
++ master->slave = slave;
++} /* End Function devfs_auto_unregister */
++
++
++/**
++ * devfs_get_unregister_slave - Get the slave entry which will be automatically unregistered.
++ * @master: The master devfs entry.
++ *
++ * Returns the slave which will be unregistered when @master is unregistered.
++ */
++
++devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master)
++{
++ if (master == NULL) return NULL;
++ VERIFY_ENTRY (master);
++ return master->slave;
++} /* End Function devfs_get_unregister_slave */
++
++
++/**
++ * devfs_get_name - Get the name for a device entry in its parent directory.
++ * @de: The handle to the device entry.
++ * @namelen: The length of the name is written here. This may be %NULL.
++ *
++ * Returns the name on success, else %NULL.
++ */
++
++const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen)
++{
++ if (de == NULL) return NULL;
++ VERIFY_ENTRY (de);
++ if (namelen != NULL) *namelen = de->namelen;
++ return de->name;
++} /* End Function devfs_get_name */
++
++
++/**
++ * devfs_register_chrdev - Optionally register a conventional character driver.
++ * @major: The major number for the driver.
++ * @name: The name of the driver (as seen in /proc/devices).
++ * @fops: The &file_operations structure pointer.
++ *
++ * This function will register a character driver provided the "devfs=only"
++ * option was not provided at boot time.
++ * Returns 0 on success, else a negative error code on failure.
++ */
++
++int devfs_register_chrdev (unsigned int major, const char *name,
++ struct file_operations *fops)
++{
++ if (boot_options & OPTION_ONLY) return 0;
++ return register_chrdev (major, name, fops);
++} /* End Function devfs_register_chrdev */
++
++
++/**
++ * devfs_register_blkdev - Optionally register a conventional block driver.
++ * @major: The major number for the driver.
++ * @name: The name of the driver (as seen in /proc/devices).
++ * @bdops: The &block_device_operations structure pointer.
++ *
++ * This function will register a block driver provided the "devfs=only"
++ * option was not provided at boot time.
++ * Returns 0 on success, else a negative error code on failure.
++ */
++
++int devfs_register_blkdev (unsigned int major, const char *name,
++ struct block_device_operations *bdops)
++{
++ if (boot_options & OPTION_ONLY) return 0;
++ return register_blkdev (major, name, bdops);
++} /* End Function devfs_register_blkdev */
++
++
++/**
++ * devfs_unregister_chrdev - Optionally unregister a conventional character driver.
++ * @major: The major number for the driver.
++ * @name: The name of the driver (as seen in /proc/devices).
++ *
++ * This function will unregister a character driver provided the "devfs=only"
++ * option was not provided at boot time.
++ * Returns 0 on success, else a negative error code on failure.
++ */
++
++int devfs_unregister_chrdev (unsigned int major, const char *name)
++{
++ if (boot_options & OPTION_ONLY) return 0;
++ return unregister_chrdev (major, name);
++} /* End Function devfs_unregister_chrdev */
++
++
++/**
++ * devfs_unregister_blkdev - Optionally unregister a conventional block driver.
++ * @major: The major number for the driver.
++ * @name: The name of the driver (as seen in /proc/devices).
++ *
++ * This function will unregister a block driver provided the "devfs=only"
++ * option was not provided at boot time.
++ * Returns 0 on success, else a negative error code on failure.
++ */
++
++int devfs_unregister_blkdev (unsigned int major, const char *name)
++{
++ if (boot_options & OPTION_ONLY) return 0;
++ return unregister_blkdev (major, name);
++} /* End Function devfs_unregister_blkdev */
++
++/**
++ * devfs_setup - Process kernel boot options.
++ * @str: The boot options after the "devfs=".
++ */
++
++static int __init devfs_setup (char *str)
++{
++ static struct
++ {
++ char *name;
++ unsigned int mask;
++ unsigned int *opt;
++ } devfs_options_tab[] __initdata =
++ {
++#ifdef CONFIG_DEVFS_DEBUG
++ {"dall", DEBUG_ALL, &devfs_debug_init},
++ {"dmod", DEBUG_MODULE_LOAD, &devfs_debug_init},
++ {"dreg", DEBUG_REGISTER, &devfs_debug_init},
++ {"dunreg", DEBUG_UNREGISTER, &devfs_debug_init},
++ {"dfree", DEBUG_FREE, &devfs_debug_init},
++ {"diget", DEBUG_I_GET, &devfs_debug_init},
++ {"dchange", DEBUG_SET_FLAGS, &devfs_debug_init},
++ {"dsread", DEBUG_S_READ, &devfs_debug_init},
++ {"dichange", DEBUG_I_CHANGE, &devfs_debug_init},
++ {"dimknod", DEBUG_I_MKNOD, &devfs_debug_init},
++ {"dilookup", DEBUG_I_LOOKUP, &devfs_debug_init},
++ {"diunlink", DEBUG_I_UNLINK, &devfs_debug_init},
++#endif /* CONFIG_DEVFS_DEBUG */
++ {"only", OPTION_ONLY, &boot_options},
++ {"mount", OPTION_MOUNT, &boot_options},
++ {NULL, 0, NULL}
++ };
++
++ while ( (*str != '\0') && !isspace (*str) )
++ {
++ int i, found = 0, invert = 0;
++
++ if (strncmp (str, "no", 2) == 0)
++ {
++ invert = 1;
++ str += 2;
++ }
++ for (i = 0; devfs_options_tab[i].name != NULL; i++)
++ {
++ int len = strlen (devfs_options_tab[i].name);
++
++ if (strncmp (str, devfs_options_tab[i].name, len) == 0)
++ {
++ if (invert)
++ *devfs_options_tab[i].opt &= ~devfs_options_tab[i].mask;
++ else
++ *devfs_options_tab[i].opt |= devfs_options_tab[i].mask;
++ str += len;
++ found = 1;
++ break;
++ }
++ }
++ if (!found) return 0; /* No match */
++ if (*str != ',') return 0; /* No more options */
++ ++str;
++ }
++ return 1;
++} /* End Function devfs_setup */
++
++__setup("devfs=", devfs_setup);
++
++EXPORT_SYMBOL(devfs_put);
++EXPORT_SYMBOL(devfs_register);
++EXPORT_SYMBOL(devfs_unregister);
++EXPORT_SYMBOL(devfs_mk_symlink);
++EXPORT_SYMBOL(devfs_mk_dir);
++EXPORT_SYMBOL(devfs_get_handle);
++EXPORT_SYMBOL(devfs_find_handle);
++EXPORT_SYMBOL(devfs_get_flags);
++EXPORT_SYMBOL(devfs_set_flags);
++EXPORT_SYMBOL(devfs_get_maj_min);
++EXPORT_SYMBOL(devfs_get_handle_from_inode);
++EXPORT_SYMBOL(devfs_generate_path);
++EXPORT_SYMBOL(devfs_get_ops);
++EXPORT_SYMBOL(devfs_set_file_size);
++EXPORT_SYMBOL(devfs_get_info);
++EXPORT_SYMBOL(devfs_set_info);
++EXPORT_SYMBOL(devfs_get_parent);
++EXPORT_SYMBOL(devfs_get_first_child);
++EXPORT_SYMBOL(devfs_get_next_sibling);
++EXPORT_SYMBOL(devfs_auto_unregister);
++EXPORT_SYMBOL(devfs_get_unregister_slave);
++EXPORT_SYMBOL(devfs_get_name);
++EXPORT_SYMBOL(devfs_register_chrdev);
++EXPORT_SYMBOL(devfs_register_blkdev);
++EXPORT_SYMBOL(devfs_unregister_chrdev);
++EXPORT_SYMBOL(devfs_unregister_blkdev);
++
++
++/**
++ * try_modload - Notify devfsd of an inode lookup by a non-devfsd process.
++ * @parent: The parent devfs entry.
++ * @fs_info: The filesystem info.
++ * @name: The device name.
++ * @namelen: The number of characters in @name.
++ * @buf: A working area that will be used. This must not go out of scope
++ * until devfsd is idle again.
++ *
++ * Returns 0 on success (event was queued), else a negative error code.
++ */
++
++static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info,
++ const char *name, unsigned namelen,
++ struct devfs_entry *buf)
++{
++ if ( !( fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP) ) )
++ return -ENOENT;
++ if ( is_devfsd_or_child (fs_info) ) return -ENOENT;
++ memset (buf, 0, sizeof *buf);
++ atomic_set (&buf->refcount, 1);
++ buf->parent = parent;
++ buf->namelen = namelen;
++ buf->u.name = name;
++ WRITE_ENTRY_MAGIC (buf, MAGIC_VALUE);
++ if ( !devfsd_notify_de (buf, DEVFSD_NOTIFY_LOOKUP, 0,
++ current->euid, current->egid, fs_info, 0) )
++ return -ENOENT;
++ /* Possible success: event has been queued */
++ return 0;
++} /* End Function try_modload */
++
++
++/**
++ * check_disc_changed - Check if a removable disc was changed.
++ * @de: The device.
++ *
++ * Returns 1 if the media was changed, else 0.
++ *
++ * This function may block, and may indirectly cause the parent directory
++ * contents to be changed due to partition re-reading.
++ */
++
++static int check_disc_changed (struct devfs_entry *de)
++{
++ int tmp;
++ int retval = 0;
++ kdev_t dev = mk_kdev (de->u.fcb.u.device.major, de->u.fcb.u.device.minor);
++ struct block_device_operations *bdops;
++ extern int warn_no_part;
++
++ if ( !S_ISBLK (de->mode) ) return 0;
++ bdops = devfs_get_ops (de);
++ if (!bdops) return 0;
++ if (bdops->check_media_change == NULL) goto out;
++ if ( !bdops->check_media_change (dev) ) goto out;
++ retval = 1;
++ printk (KERN_DEBUG "VFS: Disk change detected on device %s\n",
++ kdevname (dev) );
++ if ( invalidate_device (dev, 0) )
++ printk (KERN_WARNING "VFS: busy inodes on changed media..\n");
++ /* Ugly hack to disable messages about unable to read partition table */
++ tmp = warn_no_part;
++ warn_no_part = 0;
++ if (bdops->revalidate) bdops->revalidate (dev);
++ warn_no_part = tmp;
++out:
++ devfs_put_ops (de);
++ return retval;
++} /* End Function check_disc_changed */
++
++
++/**
++ * scan_dir_for_removable - Scan a directory for removable media devices and check media.
++ * @dir: The directory.
++ *
++ * This function may block, and may indirectly cause the directory
++ * contents to be changed due to partition re-reading. The directory will
++ * be locked for reading.
++ */
++
++static void scan_dir_for_removable (struct devfs_entry *dir)
++{
++ struct devfs_entry *de;
++
++ read_lock (&dir->u.dir.lock);
++ if (dir->u.dir.num_removable < 1) de = NULL;
++ else
++ {
++ for (de = dir->u.dir.first; de != NULL; de = de->next)
++ {
++ if (S_ISBLK (de->mode) && de->u.fcb.removable) break;
++ }
++ devfs_get (de);
++ }
++ read_unlock (&dir->u.dir.lock);
++ if (de) check_disc_changed (de);
++ devfs_put (de);
++} /* End Function scan_dir_for_removable */
++
++/**
++ * get_removable_partition - Get removable media partition.
++ * @dir: The parent directory.
++ * @name: The name of the entry.
++ * @namelen: The number of characters in <<name>>.
++ *
++ * Returns 1 if the media was changed, else 0.
++ *
++ * This function may block, and may indirectly cause the directory
++ * contents to be changed due to partition re-reading. The directory must
++ * be locked for reading upon entry, and will be unlocked upon exit.
++ */
++
++static int get_removable_partition (struct devfs_entry *dir, const char *name,
++ unsigned int namelen)
++{
++ int retval;
++ struct devfs_entry *de;
++
++ if (dir->u.dir.num_removable < 1)
++ {
++ read_unlock (&dir->u.dir.lock);
++ return 0;
++ }
++ for (de = dir->u.dir.first; de != NULL; de = de->next)
++ {
++ if (!S_ISBLK (de->mode) || !de->u.fcb.removable) continue;
++ if (strcmp (de->name, "disc") == 0) break;
++ /* Support for names where the partition is appended to the disc name
++ */
++ if (de->namelen >= namelen) continue;
++ if (strncmp (de->name, name, de->namelen) == 0) break;
++ }
++ devfs_get (de);
++ read_unlock (&dir->u.dir.lock);
++ retval = de ? check_disc_changed (de) : 0;
++ devfs_put (de);
++ return retval;
++} /* End Function get_removable_partition */
++
++
++/* Superblock operations follow */
++
++static struct inode_operations devfs_iops;
++static struct inode_operations devfs_dir_iops;
++static struct file_operations devfs_fops;
++static struct file_operations devfs_dir_fops;
++static struct inode_operations devfs_symlink_iops;
++
++static int devfs_notify_change (struct dentry *dentry, struct iattr *iattr)
++{
++ int retval;
++ struct devfs_entry *de;
++ struct inode *inode = dentry->d_inode;
++ struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
++
++ de = get_devfs_entry_from_vfs_inode (inode);
++ if (de == NULL) return -ENODEV;
++ retval = inode_change_ok (inode, iattr);
++ if (retval != 0) return retval;
++ retval = inode_setattr (inode, iattr);
++ if (retval != 0) return retval;
++ DPRINTK (DEBUG_I_CHANGE, "(%d): VFS inode: %p devfs_entry: %p\n",
++ (int) inode->i_ino, inode, de);
++ DPRINTK (DEBUG_I_CHANGE, "(): mode: 0%o uid: %d gid: %d\n",
++ (int) inode->i_mode, (int) inode->i_uid, (int) inode->i_gid);
++ /* Inode is not on hash chains, thus must save permissions here rather
++ than in a write_inode() method */
++ if ( ( !S_ISREG (inode->i_mode) && !S_ISCHR (inode->i_mode) &&
++ !S_ISBLK (inode->i_mode) ) || !de->u.fcb.auto_owner )
++ {
++ de->mode = inode->i_mode;
++ de->inode.uid = inode->i_uid;
++ de->inode.gid = inode->i_gid;
++ }
++ de->inode.atime = inode->i_atime;
++ de->inode.mtime = inode->i_mtime;
++ de->inode.ctime = inode->i_ctime;
++ if ( ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) ) &&
++ !is_devfsd_or_child (fs_info) )
++ devfsd_notify_de (de, DEVFSD_NOTIFY_CHANGE, inode->i_mode,
++ inode->i_uid, inode->i_gid, fs_info, 0);
++ return 0;
++} /* End Function devfs_notify_change */
++
++static int devfs_statfs (struct super_block *sb, struct statfs *buf)
++{
++ buf->f_type = DEVFS_SUPER_MAGIC;
++ buf->f_bsize = FAKE_BLOCK_SIZE;
++ buf->f_bfree = 0;
++ buf->f_bavail = 0;
++ buf->f_ffree = 0;
++ buf->f_namelen = NAME_MAX;
++ return 0;
++} /* End Function devfs_statfs */
++
++static void devfs_clear_inode (struct inode *inode)
++{
++ if ( S_ISBLK (inode->i_mode) ) bdput (inode->i_bdev);
++} /* End Function devfs_clear_inode */
++
++static struct super_operations devfs_sops =
++{
++ put_inode: force_delete,
++ clear_inode: devfs_clear_inode,
++ statfs: devfs_statfs,
++};
++
++
++/**
++ * _devfs_get_vfs_inode - Get a VFS inode.
++ * @sb: The super block.
++ * @de: The devfs inode.
++ * @dentry: The dentry to register with the devfs inode.
++ *
++ * Returns the inode on success, else %NULL. An implicit devfs_get() is
++ * performed if the inode is created.
++ */
++
++static struct inode *_devfs_get_vfs_inode (struct super_block *sb,
++ struct devfs_entry *de,
++ struct dentry *dentry)
++{
++ int is_fcb = FALSE;
++ struct inode *inode;
++
++ if (de->prev == de) return NULL; /* Quick check to see if unhooked */
++ if ( ( inode = new_inode (sb) ) == NULL )
++ {
++ PRINTK ("(%s): new_inode() failed, de: %p\n", de->name, de);
++ return NULL;
++ }
++ if (de->parent)
++ {
++ read_lock (&de->parent->u.dir.lock);
++ if (de->prev != de) de->inode.dentry = dentry; /* Not unhooked */
++ read_unlock (&de->parent->u.dir.lock);
++ }
++ else de->inode.dentry = dentry; /* Root: no locking needed */
++ if (de->inode.dentry != dentry)
++ { /* Must have been unhooked */
++ iput (inode);
++ return NULL;
++ }
++ inode->u.generic_ip = devfs_get (de);
++ inode->i_ino = de->inode.ino;
++ DPRINTK (DEBUG_I_GET, "(%d): VFS inode: %p devfs_entry: %p\n",
++ (int) inode->i_ino, inode, de);
++ inode->i_blocks = 0;
++ inode->i_blksize = FAKE_BLOCK_SIZE;
++ inode->i_op = &devfs_iops;
++ inode->i_fop = &devfs_fops;
++ inode->i_rdev = NODEV;
++ if ( S_ISCHR (de->mode) )
++ {
++ inode->i_rdev = mk_kdev (de->u.fcb.u.device.major,
++ de->u.fcb.u.device.minor);
++ inode->i_cdev = cdget ( kdev_t_to_nr (inode->i_rdev) );
++ is_fcb = TRUE;
++ }
++ else if ( S_ISBLK (de->mode) )
++ {
++ inode->i_rdev = mk_kdev (de->u.fcb.u.device.major,
++ de->u.fcb.u.device.minor);
++ if (bd_acquire (inode) == 0)
++ {
++ if (!inode->i_bdev->bd_op && de->u.fcb.ops)
++ inode->i_bdev->bd_op = de->u.fcb.ops;
++ }
++ else PRINTK ("(%d): no block device from bdget()\n",(int)inode->i_ino);
++ is_fcb = TRUE;
++ }
++ else if ( S_ISFIFO (de->mode) ) inode->i_fop = &def_fifo_fops;
++ else if ( S_ISREG (de->mode) )
++ {
++ inode->i_size = de->u.fcb.u.file.size;
++ is_fcb = TRUE;
++ }
++ else if ( S_ISDIR (de->mode) )
++ {
++ inode->i_op = &devfs_dir_iops;
++ inode->i_fop = &devfs_dir_fops;
++ }
++ else if ( S_ISLNK (de->mode) )
++ {
++ inode->i_op = &devfs_symlink_iops;
++ inode->i_size = de->u.symlink.length;
++ }
++ if (is_fcb && de->u.fcb.auto_owner)
++ inode->i_mode = (de->mode & S_IFMT) | S_IRUGO | S_IWUGO;
++ else inode->i_mode = de->mode;
++ inode->i_uid = de->inode.uid;
++ inode->i_gid = de->inode.gid;
++ inode->i_atime = de->inode.atime;
++ inode->i_mtime = de->inode.mtime;
++ inode->i_ctime = de->inode.ctime;
++ DPRINTK (DEBUG_I_GET, "(): mode: 0%o uid: %d gid: %d\n",
++ (int) inode->i_mode, (int) inode->i_uid, (int) inode->i_gid);
++ return inode;
++} /* End Function _devfs_get_vfs_inode */
++
++
++/* File operations for device entries follow */
++
++static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir)
++{
++ int err, count;
++ int stored = 0;
++ struct fs_info *fs_info;
++ struct devfs_entry *parent, *de, *next = NULL;
++ struct inode *inode = file->f_dentry->d_inode;
++
++ fs_info = inode->i_sb->u.generic_sbp;
++ parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode);
++ if ( (long) file->f_pos < 0 ) return -EINVAL;
++ DPRINTK (DEBUG_F_READDIR, "(%s): fs_info: %p pos: %ld\n",
++ parent->name, fs_info, (long) file->f_pos);
++ switch ( (long) file->f_pos )
++ {
++ case 0:
++ scan_dir_for_removable (parent);
++ err = (*filldir) (dirent, "..", 2, file->f_pos,
++ file->f_dentry->d_parent->d_inode->i_ino, DT_DIR);
++ if (err == -EINVAL) break;
++ if (err < 0) return err;
++ file->f_pos++;
++ ++stored;
++ /* Fall through */
++ case 1:
++ err = (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino, DT_DIR);
++ if (err == -EINVAL) break;
++ if (err < 0) return err;
++ file->f_pos++;
++ ++stored;
++ /* Fall through */
++ default:
++ /* Skip entries */
++ count = file->f_pos - 2;
++ read_lock (&parent->u.dir.lock);
++ for (de = parent->u.dir.first; de && (count > 0); de = de->next)
++ if ( !IS_HIDDEN (de) ) --count;
++ devfs_get (de);
++ read_unlock (&parent->u.dir.lock);
++ /* Now add all remaining entries */
++ while (de)
++ {
++ if ( IS_HIDDEN (de) ) err = 0;
++ else
++ {
++ err = (*filldir) (dirent, de->name, de->namelen,
++ file->f_pos, de->inode.ino, de->mode >> 12);
++ if (err >= 0)
++ {
++ file->f_pos++;
++ ++stored;
++ }
++ }
++ read_lock (&parent->u.dir.lock);
++ next = devfs_get (de->next);
++ read_unlock (&parent->u.dir.lock);
++ devfs_put (de);
++ de = next;
++ if (err == -EINVAL) break;
++ if (err < 0) return err;
++ }
++ break;
++ }
++ return stored;
++} /* End Function devfs_readdir */
++
++static int devfs_open (struct inode *inode, struct file *file)
++{
++ int err;
++ struct fcb_type *df;
++ struct devfs_entry *de;
++ struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
++
++ de = get_devfs_entry_from_vfs_inode (inode);
++ if (de == NULL) return -ENODEV;
++ if ( S_ISDIR (de->mode) ) return 0;
++ df = &de->u.fcb;
++ file->private_data = de->info;
++ if ( S_ISBLK (inode->i_mode) )
++ {
++ file->f_op = &def_blk_fops;
++ if (df->ops) inode->i_bdev->bd_op = df->ops;
++ err = def_blk_fops.open (inode, file);
++ }
++ else
++ {
++ file->f_op = fops_get ( (struct file_operations *) df->ops );
++ if (file->f_op)
++ {
++ lock_kernel ();
++ err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0;
++ unlock_kernel ();
++ }
++ else
++ { /* Fallback to legacy scheme */
++ if ( S_ISCHR (inode->i_mode) ) err = chrdev_open (inode, file);
++ else err = -ENODEV;
++ }
++ }
++ if (err < 0) return err;
++ /* Open was successful */
++ if (df->open) return 0;
++ df->open = TRUE; /* This is the first open */
++ if (df->auto_owner)
++ {
++ /* Change the ownership/protection to what driver specified */
++ inode->i_mode = de->mode;
++ inode->i_uid = current->euid;
++ inode->i_gid = current->egid;
++ }
++ if ( df->aopen_notify && !is_devfsd_or_child (fs_info) )
++ devfsd_notify_de (de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode,
++ current->euid, current->egid, fs_info, 0);
++ return 0;
++} /* End Function devfs_open */
++
++static struct file_operations devfs_fops =
++{
++ open: devfs_open,
++};
++
++static struct file_operations devfs_dir_fops =
++{
++ read: generic_read_dir,
++ readdir: devfs_readdir,
++ open: devfs_open,
++};
++
++
++/* Dentry operations for device entries follow */
++
++
++/**
++ * devfs_d_release - Callback for when a dentry is freed.
++ * @dentry: The dentry.
++ */
++
++static void devfs_d_release (struct dentry *dentry)
++{
++ DPRINTK (DEBUG_D_RELEASE, "(%p): inode: %p\n", dentry, dentry->d_inode);
++} /* End Function devfs_d_release */
++
++/**
++ * devfs_d_iput - Callback for when a dentry loses its inode.
++ * @dentry: The dentry.
++ * @inode: The inode.
++ */
++
++static void devfs_d_iput (struct dentry *dentry, struct inode *inode)
++{
++ struct devfs_entry *de;
++
++ de = get_devfs_entry_from_vfs_inode (inode);
++ DPRINTK (DEBUG_D_IPUT,"(%s): dentry: %p inode: %p de: %p de->dentry: %p\n",
++ de->name, dentry, inode, de, de->inode.dentry);
++ if ( de->inode.dentry && (de->inode.dentry != dentry) )
++ OOPS ("(%s): de: %p dentry: %p de->dentry: %p\n",
++ de->name, de, dentry, de->inode.dentry);
++ de->inode.dentry = NULL;
++ iput (inode);
++ devfs_put (de);
++} /* End Function devfs_d_iput */
++
++static int devfs_d_delete (struct dentry *dentry);
++
++static struct dentry_operations devfs_dops =
++{
++ d_delete: devfs_d_delete,
++ d_release: devfs_d_release,
++ d_iput: devfs_d_iput,
++};
++
++static int devfs_d_revalidate_wait (struct dentry *dentry, int flags);
++
++static struct dentry_operations devfs_wait_dops =
++{
++ d_delete: devfs_d_delete,
++ d_release: devfs_d_release,
++ d_iput: devfs_d_iput,
++ d_revalidate: devfs_d_revalidate_wait,
++};
++
++/**
++ * devfs_d_delete - Callback for when all files for a dentry are closed.
++ * @dentry: The dentry.
++ */
++
++static int devfs_d_delete (struct dentry *dentry)
++{
++ struct inode *inode = dentry->d_inode;
++ struct devfs_entry *de;
++ struct fs_info *fs_info;
++
++ if (dentry->d_op == &devfs_wait_dops) dentry->d_op = &devfs_dops;
++ /* Unhash dentry if negative (has no inode) */
++ if (inode == NULL)
++ {
++ DPRINTK (DEBUG_D_DELETE, "(%p): dropping negative dentry\n", dentry);
++ return 1;
++ }
++ fs_info = inode->i_sb->u.generic_sbp;
++ de = get_devfs_entry_from_vfs_inode (inode);
++ DPRINTK (DEBUG_D_DELETE, "(%p): inode: %p devfs_entry: %p\n",
++ dentry, inode, de);
++ if (de == NULL) return 0;
++ if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
++ return 0;
++ if (!de->u.fcb.open) return 0;
++ de->u.fcb.open = FALSE;
++ if (de->u.fcb.aopen_notify)
++ devfsd_notify_de (de, DEVFSD_NOTIFY_CLOSE, inode->i_mode,
++ current->euid, current->egid, fs_info, 1);
++ if (!de->u.fcb.auto_owner) return 0;
++ /* Change the ownership/protection back */
++ inode->i_mode = (de->mode & S_IFMT) | S_IRUGO | S_IWUGO;
++ inode->i_uid = de->inode.uid;
++ inode->i_gid = de->inode.gid;
++ return 0;
++} /* End Function devfs_d_delete */
++
++struct devfs_lookup_struct
++{
++ devfs_handle_t de;
++ wait_queue_head_t wait_queue;
++};
++
++static int devfs_d_revalidate_wait (struct dentry *dentry, int flags)
++{
++ struct inode *dir = dentry->d_parent->d_inode;
++ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
++ devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir);
++ struct devfs_lookup_struct *lookup_info = dentry->d_fsdata;
++ DECLARE_WAITQUEUE (wait, current);
++
++ if ( is_devfsd_or_child (fs_info) )
++ {
++ devfs_handle_t de = lookup_info->de;
++ struct inode *inode;
++
++ DPRINTK (DEBUG_I_LOOKUP,
++ "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n",
++ dentry->d_name.name, dentry, dentry->d_inode, de,
++ current->comm);
++ if (dentry->d_inode) return 1;
++ if (de == NULL)
++ {
++ read_lock (&parent->u.dir.lock);
++ de = _devfs_search_dir (parent, dentry->d_name.name,
++ dentry->d_name.len);
++ read_unlock (&parent->u.dir.lock);
++ if (de == NULL) return 1;
++ lookup_info->de = de;
++ }
++ /* Create an inode, now that the driver information is available */
++ inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry);
++ if (!inode) return 1;
++ DPRINTK (DEBUG_I_LOOKUP,
++ "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n",
++ de->name, de->inode.ino, inode, de, current->comm);
++ d_instantiate (dentry, inode);
++ return 1;
++ }
++ if (lookup_info == NULL) return 1; /* Early termination */
++ read_lock (&parent->u.dir.lock);
++ if (dentry->d_fsdata)
++ {
++ add_wait_queue (&lookup_info->wait_queue, &wait);
++ current->state = TASK_UNINTERRUPTIBLE;
++ read_unlock (&parent->u.dir.lock);
++ schedule ();
++ }
++ else read_unlock (&parent->u.dir.lock);
++ return 1;
++} /* End Function devfs_d_revalidate_wait */
++
++
++/* Inode operations for device entries follow */
++
++static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry)
++{
++ struct devfs_entry tmp; /* Must stay in scope until devfsd idle again */
++ struct devfs_lookup_struct lookup_info;
++ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
++ struct devfs_entry *parent, *de;
++ struct inode *inode;
++ struct dentry *retval = NULL;
++
++ /* Set up the dentry operations before anything else, to ensure cleaning
++ up on any error */
++ dentry->d_op = &devfs_dops;
++ /* First try to get the devfs entry for this directory */
++ parent = get_devfs_entry_from_vfs_inode (dir);
++ DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p parent: %p by: \"%s\"\n",
++ dentry->d_name.name, dentry, parent, current->comm);
++ if (parent == NULL) return ERR_PTR (-ENOENT);
++ read_lock (&parent->u.dir.lock);
++ de = _devfs_search_dir (parent, dentry->d_name.name, dentry->d_name.len);
++ if (de) read_unlock (&parent->u.dir.lock);
++ else
++ { /* Try re-reading the partition (media may have changed) */
++ if ( get_removable_partition (parent, dentry->d_name.name,
++ dentry->d_name.len) ) /* Unlocks */
++ { /* Media did change */
++ read_lock (&parent->u.dir.lock);
++ de = _devfs_search_dir (parent, dentry->d_name.name,
++ dentry->d_name.len);
++ read_unlock (&parent->u.dir.lock);
++ }
++ }
++ lookup_info.de = de;
++ init_waitqueue_head (&lookup_info.wait_queue);
++ dentry->d_fsdata = &lookup_info;
++ if (de == NULL)
++ { /* Try with devfsd. For any kind of failure, leave a negative dentry
++ so someone else can deal with it (in the case where the sysadmin
++ does a mknod()). It's important to do this before hashing the
++ dentry, so that the devfsd queue is filled before revalidates
++ can start */
++ if (try_modload (parent, fs_info,
++ dentry->d_name.name, dentry->d_name.len, &tmp) < 0)
++ { /* Lookup event was not queued to devfsd */
++ d_add (dentry, NULL);
++ return NULL;
++ }
++ }
++ dentry->d_op = &devfs_wait_dops;
++ d_add (dentry, NULL); /* Open the floodgates */
++ /* Unlock directory semaphore, which will release any waiters. They
++ will get the hashed dentry, and may be forced to wait for
++ revalidation */
++ up (&dir->i_sem);
++ wait_for_devfsd_finished (fs_info); /* If I'm not devfsd, must wait */
++ down (&dir->i_sem); /* Grab it again because them's the rules */
++ de = lookup_info.de;
++ /* If someone else has been so kind as to make the inode, we go home
++ early */
++ if (dentry->d_inode) goto out;
++ if (de == NULL)
++ {
++ read_lock (&parent->u.dir.lock);
++ de = _devfs_search_dir (parent, dentry->d_name.name,
++ dentry->d_name.len);
++ read_unlock (&parent->u.dir.lock);
++ if (de == NULL) goto out;
++ /* OK, there's an entry now, but no VFS inode yet */
++ }
++ /* Create an inode, now that the driver information is available */
++ inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry);
++ if (!inode)
++ {
++ retval = ERR_PTR (-ENOMEM);
++ goto out;
++ }
++ DPRINTK (DEBUG_I_LOOKUP, "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n",
++ de->name, de->inode.ino, inode, de, current->comm);
++ d_instantiate (dentry, inode);
++out:
++ dentry->d_op = &devfs_dops;
++ dentry->d_fsdata = NULL;
++ write_lock (&parent->u.dir.lock);
++ wake_up (&lookup_info.wait_queue);
++ write_unlock (&parent->u.dir.lock);
++ devfs_put (de);
++ return retval;
++} /* End Function devfs_lookup */
++
++static int devfs_unlink (struct inode *dir, struct dentry *dentry)
++{
++ int unhooked;
++ struct devfs_entry *de;
++ struct inode *inode = dentry->d_inode;
++ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
++
++ de = get_devfs_entry_from_vfs_inode (inode);
++ DPRINTK (DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de);
++ if (de == NULL) return -ENOENT;
++ if (!de->vfs_deletable) return -EPERM;
++ write_lock (&de->parent->u.dir.lock);
++ unhooked = _devfs_unhook (de);
++ write_unlock (&de->parent->u.dir.lock);
++ if (!unhooked) return -ENOENT;
++ if ( !is_devfsd_or_child (fs_info) )
++ devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
++ inode->i_uid, inode->i_gid, fs_info, 0);
++ free_dentry (de);
++ devfs_put (de);
++ return 0;
++} /* End Function devfs_unlink */
++
++static int devfs_symlink (struct inode *dir, struct dentry *dentry,
++ const char *symname)
++{
++ int err;
++ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
++ struct devfs_entry *parent, *de;
++ struct inode *inode;
++
++ /* First try to get the devfs entry for this directory */
++ parent = get_devfs_entry_from_vfs_inode (dir);
++ if (parent == NULL) return -ENOENT;
++ err = devfs_do_symlink (parent, dentry->d_name.name, DEVFS_FL_NONE,
++ symname, &de, NULL);
++ DPRINTK (DEBUG_DISABLED, "(%s): errcode from <devfs_do_symlink>: %d\n",
++ dentry->d_name.name, err);
++ if (err < 0) return err;
++ de->vfs_deletable = TRUE;
++ de->inode.uid = current->euid;
++ de->inode.gid = current->egid;
++ de->inode.atime = CURRENT_TIME;
++ de->inode.mtime = CURRENT_TIME;
++ de->inode.ctime = CURRENT_TIME;
++ if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
++ return -ENOMEM;
++ DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n",
++ dentry->d_name.name, de->inode.ino, inode, dentry);
++ d_instantiate (dentry, inode);
++ if ( !is_devfsd_or_child (fs_info) )
++ devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
++ inode->i_uid, inode->i_gid, fs_info, 0);
++ return 0;
++} /* End Function devfs_symlink */
++
++static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
++{
++ int err;
++ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
++ struct devfs_entry *parent, *de;
++ struct inode *inode;
++
++ mode = (mode & ~S_IFMT) | S_IFDIR; /* VFS doesn't pass S_IFMT part */
++ parent = get_devfs_entry_from_vfs_inode (dir);
++ if (parent == NULL) return -ENOENT;
++ de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
++ if (!de) return -ENOMEM;
++ de->vfs_deletable = TRUE;
++ if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 )
++ return err;
++ de->inode.uid = current->euid;
++ de->inode.gid = current->egid;
++ de->inode.atime = CURRENT_TIME;
++ de->inode.mtime = CURRENT_TIME;
++ de->inode.ctime = CURRENT_TIME;
++ if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
++ return -ENOMEM;
++ DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n",
++ dentry->d_name.name, de->inode.ino, inode, dentry);
++ d_instantiate (dentry, inode);
++ if ( !is_devfsd_or_child (fs_info) )
++ devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
++ inode->i_uid, inode->i_gid, fs_info, 0);
++ return 0;
++} /* End Function devfs_mkdir */
++
++static int devfs_rmdir (struct inode *dir, struct dentry *dentry)
++{
++ int err = 0;
++ struct devfs_entry *de;
++ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
++ struct inode *inode = dentry->d_inode;
++
++ if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL;
++ de = get_devfs_entry_from_vfs_inode (inode);
++ if (de == NULL) return -ENOENT;
++ if ( !S_ISDIR (de->mode) ) return -ENOTDIR;
++ if (!de->vfs_deletable) return -EPERM;
++ /* First ensure the directory is empty and will stay that way */
++ write_lock (&de->u.dir.lock);
++ if (de->u.dir.first) err = -ENOTEMPTY;
++ else de->u.dir.no_more_additions = TRUE;
++ write_unlock (&de->u.dir.lock);
++ if (err) return err;
++ /* Now unhook the directory from it's parent */
++ write_lock (&de->parent->u.dir.lock);
++ if ( !_devfs_unhook (de) ) err = -ENOENT;
++ write_unlock (&de->parent->u.dir.lock);
++ if (err) return err;
++ if ( !is_devfsd_or_child (fs_info) )
++ devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
++ inode->i_uid, inode->i_gid, fs_info, 0);
++ free_dentry (de);
++ devfs_put (de);
++ return 0;
++} /* End Function devfs_rmdir */
++
++static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
++ int rdev)
++{
++ int err;
++ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
++ struct devfs_entry *parent, *de;
++ struct inode *inode;
++
++ DPRINTK (DEBUG_I_MKNOD, "(%s): mode: 0%o dev: %d\n",
++ dentry->d_name.name, mode, rdev);
++ parent = get_devfs_entry_from_vfs_inode (dir);
++ if (parent == NULL) return -ENOENT;
++ de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
++ if (!de) return -ENOMEM;
++ de->vfs_deletable = TRUE;
++ if ( S_ISBLK (mode) || S_ISCHR (mode) )
++ {
++ de->u.fcb.u.device.major = MAJOR (rdev);
++ de->u.fcb.u.device.minor = MINOR (rdev);
++ }
++ if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 )
++ return err;
++ de->inode.uid = current->euid;
++ de->inode.gid = current->egid;
++ de->inode.atime = CURRENT_TIME;
++ de->inode.mtime = CURRENT_TIME;
++ de->inode.ctime = CURRENT_TIME;
++ if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
++ return -ENOMEM;
++ DPRINTK (DEBUG_I_MKNOD, ": new VFS inode(%u): %p dentry: %p\n",
++ de->inode.ino, inode, dentry);
++ d_instantiate (dentry, inode);
++ if ( !is_devfsd_or_child (fs_info) )
++ devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
++ inode->i_uid, inode->i_gid, fs_info, 0);
++ return 0;
++} /* End Function devfs_mknod */
++
++static int devfs_readlink (struct dentry *dentry, char *buffer, int buflen)
++{
++ int err;
++ struct devfs_entry *de;
++
++ de = get_devfs_entry_from_vfs_inode (dentry->d_inode);
++ if (!de) return -ENODEV;
++ err = vfs_readlink (dentry, buffer, buflen, de->u.symlink.linkname);
++ return err;
++} /* End Function devfs_readlink */
++
++static int devfs_follow_link (struct dentry *dentry, struct nameidata *nd)
++{
++ int err;
++ struct devfs_entry *de;
++
++ de = get_devfs_entry_from_vfs_inode (dentry->d_inode);
++ if (!de) return -ENODEV;
++ err = vfs_follow_link (nd, de->u.symlink.linkname);
++ return err;
++} /* End Function devfs_follow_link */
++
++static struct inode_operations devfs_iops =
++{
++ setattr: devfs_notify_change,
++};
++
++static struct inode_operations devfs_dir_iops =
++{
++ lookup: devfs_lookup,
++ unlink: devfs_unlink,
++ symlink: devfs_symlink,
++ mkdir: devfs_mkdir,
++ rmdir: devfs_rmdir,
++ mknod: devfs_mknod,
++ setattr: devfs_notify_change,
++};
++
++static struct inode_operations devfs_symlink_iops =
++{
++ readlink: devfs_readlink,
++ follow_link: devfs_follow_link,
++ setattr: devfs_notify_change,
++};
++
++static struct super_block *devfs_read_super (struct super_block *sb,
++ void *data, int silent)
++{
++ struct inode *root_inode = NULL;
++
++ if (_devfs_get_root_entry () == NULL) goto out_no_root;
++ atomic_set (&fs_info.devfsd_overrun_count, 0);
++ init_waitqueue_head (&fs_info.devfsd_wait_queue);
++ init_waitqueue_head (&fs_info.revalidate_wait_queue);
++ fs_info.sb = sb;
++ sb->u.generic_sbp = &fs_info;
++ sb->s_blocksize = 1024;
++ sb->s_blocksize_bits = 10;
++ sb->s_magic = DEVFS_SUPER_MAGIC;
++ sb->s_op = &devfs_sops;
++ if ( ( root_inode = _devfs_get_vfs_inode (sb, root_entry, NULL) ) == NULL )
++ goto out_no_root;
++ sb->s_root = d_alloc_root (root_inode);
++ if (!sb->s_root) goto out_no_root;
++ DPRINTK (DEBUG_S_READ, "(): made devfs ptr: %p\n", sb->u.generic_sbp);
++ return sb;
++
++out_no_root:
++ PRINTK ("(): get root inode failed\n");
++ if (root_inode) iput (root_inode);
++ return NULL;
++} /* End Function devfs_read_super */
++
++
++static DECLARE_FSTYPE (devfs_fs_type, DEVFS_NAME, devfs_read_super, FS_SINGLE);
++
++
++/* File operations for devfsd follow */
++
++static ssize_t devfsd_read (struct file *file, char *buf, size_t len,
++ loff_t *ppos)
++{
++ int done = FALSE;
++ int ival;
++ loff_t pos, devname_offset, tlen, rpos;
++ devfs_handle_t de;
++ struct devfsd_buf_entry *entry;
++ struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->u.generic_sbp;
++ struct devfsd_notify_struct *info = fs_info->devfsd_info;
++ DECLARE_WAITQUEUE (wait, current);
++
++ /* Can't seek (pread) on this device */
++ if (ppos != &file->f_pos) return -ESPIPE;
++ /* Verify the task has grabbed the queue */
++ if (fs_info->devfsd_task != current) return -EPERM;
++ info->major = 0;
++ info->minor = 0;
++ /* Block for a new entry */
++ add_wait_queue (&fs_info->devfsd_wait_queue, &wait);
++ current->state = TASK_INTERRUPTIBLE;
++ while ( devfsd_queue_empty (fs_info) )
++ {
++ fs_info->devfsd_sleeping = TRUE;
++ wake_up (&fs_info->revalidate_wait_queue);
++ schedule ();
++ fs_info->devfsd_sleeping = FALSE;
++ if ( signal_pending (current) )
++ {
++ remove_wait_queue (&fs_info->devfsd_wait_queue, &wait);
++ current->state = TASK_RUNNING;
++ return -EINTR;
++ }
++ set_current_state (TASK_INTERRUPTIBLE);
++ }
++ remove_wait_queue (&fs_info->devfsd_wait_queue, &wait);
++ current->state = TASK_RUNNING;
++ /* Now play with the data */
++ ival = atomic_read (&fs_info->devfsd_overrun_count);
++ info->overrun_count = ival;
++ entry = fs_info->devfsd_first_event;
++ info->type = entry->type;
++ info->mode = entry->mode;
++ info->uid = entry->uid;
++ info->gid = entry->gid;
++ de = entry->de;
++ if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) )
++ {
++ info->major = de->u.fcb.u.device.major;
++ info->minor = de->u.fcb.u.device.minor;
++ }
++ pos = devfs_generate_path (de, info->devname, DEVFS_PATHLEN);
++ if (pos < 0) return pos;
++ info->namelen = DEVFS_PATHLEN - pos - 1;
++ if (info->mode == 0) info->mode = de->mode;
++ devname_offset = info->devname - (char *) info;
++ rpos = *ppos;
++ if (rpos < devname_offset)
++ {
++ /* Copy parts of the header */
++ tlen = devname_offset - rpos;
++ if (tlen > len) tlen = len;
++ if ( copy_to_user (buf, (char *) info + rpos, tlen) )
++ {
++ return -EFAULT;
++ }
++ rpos += tlen;
++ buf += tlen;
++ len -= tlen;
++ }
++ if ( (rpos >= devname_offset) && (len > 0) )
++ {
++ /* Copy the name */
++ tlen = info->namelen + 1;
++ if (tlen > len) tlen = len;
++ else done = TRUE;
++ if ( copy_to_user (buf, info->devname + pos + rpos - devname_offset,
++ tlen) )
++ {
++ return -EFAULT;
++ }
++ rpos += tlen;
++ }
++ tlen = rpos - *ppos;
++ if (done)
++ {
++ devfs_handle_t parent;
++
++ spin_lock (&fs_info->devfsd_buffer_lock);
++ fs_info->devfsd_first_event = entry->next;
++ if (entry->next == NULL) fs_info->devfsd_last_event = NULL;
++ spin_unlock (&fs_info->devfsd_buffer_lock);
++ for (; de != NULL; de = parent)
++ {
++ parent = de->parent;
++ devfs_put (de);
++ }
++ kmem_cache_free (devfsd_buf_cache, entry);
++ if (ival > 0) atomic_sub (ival, &fs_info->devfsd_overrun_count);
++ *ppos = 0;
++ }
++ else *ppos = rpos;
++ return tlen;
++} /* End Function devfsd_read */
++
++static int devfsd_ioctl (struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ int ival;
++ struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
++
++ switch (cmd)
++ {
++ case DEVFSDIOC_GET_PROTO_REV:
++ ival = DEVFSD_PROTOCOL_REVISION_KERNEL;
++ if ( copy_to_user ( (void *)arg, &ival, sizeof ival ) ) return -EFAULT;
++ break;
++ case DEVFSDIOC_SET_EVENT_MASK:
++ /* Ensure only one reader has access to the queue. This scheme will
++ work even if the global kernel lock were to be removed, because it
++ doesn't matter who gets in first, as long as only one gets it */
++ if (fs_info->devfsd_task == NULL)
++ {
++ static spinlock_t lock = SPIN_LOCK_UNLOCKED;
++
++ if ( !spin_trylock (&lock) ) return -EBUSY;
++ if (fs_info->devfsd_task != NULL)
++ { /* We lost the race... */
++ spin_unlock (&lock);
++ return -EBUSY;
++ }
++ fs_info->devfsd_task = current;
++ spin_unlock (&lock);
++ fs_info->devfsd_pgrp = (current->pgrp == current->pid) ?
++ current->pgrp : 0;
++ fs_info->devfsd_file = file;
++ fs_info->devfsd_info = kmalloc (sizeof *fs_info->devfsd_info,
++ GFP_KERNEL);
++ if (!fs_info->devfsd_info)
++ {
++ devfsd_close (inode, file);
++ return -ENOMEM;
++ }
++ }
++ else if (fs_info->devfsd_task != current) return -EBUSY;
++ fs_info->devfsd_event_mask = arg; /* Let the masses come forth */
++ break;
++ case DEVFSDIOC_RELEASE_EVENT_QUEUE:
++ if (fs_info->devfsd_file != file) return -EPERM;
++ return devfsd_close (inode, file);
++ /*break;*/
++#ifdef CONFIG_DEVFS_DEBUG
++ case DEVFSDIOC_SET_DEBUG_MASK:
++ if ( copy_from_user (&ival, (void *) arg, sizeof ival) )return -EFAULT;
++ devfs_debug = ival;
++ break;
++#endif
++ default:
++ return -ENOIOCTLCMD;
++ }
++ return 0;
++} /* End Function devfsd_ioctl */
++
++static int devfsd_close (struct inode *inode, struct file *file)
++{
++ struct devfsd_buf_entry *entry, *next;
++ struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
++
++ if (fs_info->devfsd_file != file) return 0;
++ fs_info->devfsd_event_mask = 0;
++ fs_info->devfsd_file = NULL;
++ spin_lock (&fs_info->devfsd_buffer_lock);
++ entry = fs_info->devfsd_first_event;
++ fs_info->devfsd_first_event = NULL;
++ fs_info->devfsd_last_event = NULL;
++ if (fs_info->devfsd_info)
++ {
++ kfree (fs_info->devfsd_info);
++ fs_info->devfsd_info = NULL;
++ }
++ spin_unlock (&fs_info->devfsd_buffer_lock);
++ fs_info->devfsd_pgrp = 0;
++ fs_info->devfsd_task = NULL;
++ wake_up (&fs_info->revalidate_wait_queue);
++ for (; entry; entry = next)
++ {
++ next = entry->next;
++ kmem_cache_free (devfsd_buf_cache, entry);
++ }
++ return 0;
++} /* End Function devfsd_close */
++
++#ifdef CONFIG_DEVFS_DEBUG
++static ssize_t stat_read (struct file *file, char *buf, size_t len,
++ loff_t *ppos)
++{
++ ssize_t num;
++ char txt[80];
++
++ num = sprintf (txt, "Number of entries: %u number of bytes: %u\n",
++ stat_num_entries, stat_num_bytes) + 1;
++ /* Can't seek (pread) on this device */
++ if (ppos != &file->f_pos) return -ESPIPE;
++ if (*ppos >= num) return 0;
++ if (*ppos + len > num) len = num - *ppos;
++ if ( copy_to_user (buf, txt + *ppos, len) ) return -EFAULT;
++ *ppos += len;
++ return len;
++} /* End Function stat_read */
++#endif
++
++
++static int __init init_devfs_fs (void)
++{
++ int err;
++
++ printk (KERN_INFO "%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n",
++ DEVFS_NAME, DEVFS_VERSION);
++ devfsd_buf_cache = kmem_cache_create ("devfsd_event",
++ sizeof (struct devfsd_buf_entry),
++ 0, 0, NULL, NULL);
++ if (!devfsd_buf_cache) OOPS ("(): unable to allocate event slab\n");
++#ifdef CONFIG_DEVFS_DEBUG
++ devfs_debug = devfs_debug_init;
++ printk (KERN_INFO "%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
++#endif
++ printk (KERN_INFO "%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options);
++ err = register_filesystem (&devfs_fs_type);
++ if (!err)
++ {
++ struct vfsmount *devfs_mnt = kern_mount (&devfs_fs_type);
++ err = PTR_ERR (devfs_mnt);
++ if ( !IS_ERR (devfs_mnt) ) err = 0;
++ }
++ return err;
++} /* End Function init_devfs_fs */
++
++void __init mount_devfs_fs (void)
++{
++ int err;
++
++ if ( !(boot_options & OPTION_MOUNT) ) return;
++ err = do_mount ("none", "/dev", "devfs", 0, "");
++ if (err == 0) printk (KERN_INFO "Mounted devfs on /dev\n");
++ else PRINTK ("(): unable to mount devfs, err: %d\n", err);
++} /* End Function mount_devfs_fs */
++
++module_init(init_devfs_fs)
+--- /dev/null 2003-01-30 18:24:37.000000000 +0800
++++ linux-2.4.19-hp3_pnnl1-root/fs/super.c 2003-04-15 13:21:57.000000000 +0800
+@@ -0,0 +1,926 @@
++/*
++ * linux/fs/super.c
++ *
++ * Copyright (C) 1991, 1992 Linus Torvalds
++ *
++ * super.c contains code to handle: - mount structures
++ * - super-block tables
++ * - filesystem drivers list
++ * - mount system call
++ * - umount system call
++ * - ustat system call
++ *
++ * GK 2/5/95 - Changed to support mounting the root fs via NFS
++ *
++ * Added kerneld support: Jacques Gelinas and Bjorn Ekwall
++ * Added change_root: Werner Almesberger & Hans Lermen, Feb '96
++ * Added options to /proc/mounts:
++ * Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996.
++ * Added devfs support: Richard Gooch <rgooch@atnf.csiro.au>, 13-JAN-1998
++ * Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000
++ */
++
++#include <linux/config.h>
++#include <linux/slab.h>
++#include <linux/locks.h>
++#include <linux/smp_lock.h>
++#include <linux/devfs_fs_kernel.h>
++#include <linux/major.h>
++#include <linux/acct.h>
++#include <linux/quotaops.h>
++
++#include <asm/uaccess.h>
++
++#include <linux/kmod.h>
++#define __NO_VERSION__
++#include <linux/module.h>
++
++LIST_HEAD(super_blocks);
++spinlock_t sb_lock = SPIN_LOCK_UNLOCKED;
++
++/*
++ * lock/unlockfs grab a read lock on s_umount, but you need this lock to
++ * make sure no lockfs runs are in progress before inserting/removing
++ * supers from the list.
++ */
++static DECLARE_MUTEX(lockfs_sem);
++
++/*
++ * Handling of filesystem drivers list.
++ * Rules:
++ * Inclusion to/removals from/scanning of list are protected by spinlock.
++ * During the unload module must call unregister_filesystem().
++ * We can access the fields of list element if:
++ * 1) spinlock is held or
++ * 2) we hold the reference to the module.
++ * The latter can be guaranteed by call of try_inc_mod_count(); if it
++ * returned 0 we must skip the element, otherwise we got the reference.
++ * Once the reference is obtained we can drop the spinlock.
++ */
++
++static struct file_system_type *file_systems;
++static rwlock_t file_systems_lock = RW_LOCK_UNLOCKED;
++
++/* WARNING: This can be used only if we _already_ own a reference */
++static void get_filesystem(struct file_system_type *fs)
++{
++ if (fs->owner)
++ __MOD_INC_USE_COUNT(fs->owner);
++}
++
++static void put_filesystem(struct file_system_type *fs)
++{
++ if (fs->owner)
++ __MOD_DEC_USE_COUNT(fs->owner);
++}
++
++static struct file_system_type **find_filesystem(const char *name)
++{
++ struct file_system_type **p;
++ for (p=&file_systems; *p; p=&(*p)->next)
++ if (strcmp((*p)->name,name) == 0)
++ break;
++ return p;
++}
++
++/**
++ * register_filesystem - register a new filesystem
++ * @fs: the file system structure
++ *
++ * Adds the file system passed to the list of file systems the kernel
++ * is aware of for mount and other syscalls. Returns 0 on success,
++ * or a negative errno code on an error.
++ *
++ * The &struct file_system_type that is passed is linked into the kernel
++ * structures and must not be freed until the file system has been
++ * unregistered.
++ */
++
++int register_filesystem(struct file_system_type * fs)
++{
++ int res = 0;
++ struct file_system_type ** p;
++
++ if (!fs)
++ return -EINVAL;
++ if (fs->next)
++ return -EBUSY;
++ INIT_LIST_HEAD(&fs->fs_supers);
++ write_lock(&file_systems_lock);
++ p = find_filesystem(fs->name);
++ if (*p)
++ res = -EBUSY;
++ else
++ *p = fs;
++ write_unlock(&file_systems_lock);
++ return res;
++}
++
++/**
++ * unregister_filesystem - unregister a file system
++ * @fs: filesystem to unregister
++ *
++ * Remove a file system that was previously successfully registered
++ * with the kernel. An error is returned if the file system is not found.
++ * Zero is returned on a success.
++ *
++ * Once this function has returned the &struct file_system_type structure
++ * may be freed or reused.
++ */
++
++int unregister_filesystem(struct file_system_type * fs)
++{
++ struct file_system_type ** tmp;
++
++ write_lock(&file_systems_lock);
++ tmp = &file_systems;
++ while (*tmp) {
++ if (fs == *tmp) {
++ *tmp = fs->next;
++ fs->next = NULL;
++ write_unlock(&file_systems_lock);
++ return 0;
++ }
++ tmp = &(*tmp)->next;
++ }
++ write_unlock(&file_systems_lock);
++ return -EINVAL;
++}
++
++static int fs_index(const char * __name)
++{
++ struct file_system_type * tmp;
++ char * name;
++ int err, index;
++
++ name = getname(__name);
++ err = PTR_ERR(name);
++ if (IS_ERR(name))
++ return err;
++
++ err = -EINVAL;
++ read_lock(&file_systems_lock);
++ for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) {
++ if (strcmp(tmp->name,name) == 0) {
++ err = index;
++ break;
++ }
++ }
++ read_unlock(&file_systems_lock);
++ putname(name);
++ return err;
++}
++
++static int fs_name(unsigned int index, char * buf)
++{
++ struct file_system_type * tmp;
++ int len, res;
++
++ read_lock(&file_systems_lock);
++ for (tmp = file_systems; tmp; tmp = tmp->next, index--)
++ if (index <= 0 && try_inc_mod_count(tmp->owner))
++ break;
++ read_unlock(&file_systems_lock);
++ if (!tmp)
++ return -EINVAL;
++
++ /* OK, we got the reference, so we can safely block */
++ len = strlen(tmp->name) + 1;
++ res = copy_to_user(buf, tmp->name, len) ? -EFAULT : 0;
++ put_filesystem(tmp);
++ return res;
++}
++
++static int fs_maxindex(void)
++{
++ struct file_system_type * tmp;
++ int index;
++
++ read_lock(&file_systems_lock);
++ for (tmp = file_systems, index = 0 ; tmp ; tmp = tmp->next, index++)
++ ;
++ read_unlock(&file_systems_lock);
++ return index;
++}
++
++/*
++ * Whee.. Weird sysv syscall.
++ */
++asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2)
++{
++ int retval = -EINVAL;
++
++ switch (option) {
++ case 1:
++ retval = fs_index((const char *) arg1);
++ break;
++
++ case 2:
++ retval = fs_name(arg1, (char *) arg2);
++ break;
++
++ case 3:
++ retval = fs_maxindex();
++ break;
++ }
++ return retval;
++}
++
++int get_filesystem_list(char * buf)
++{
++ int len = 0;
++ struct file_system_type * tmp;
++
++ read_lock(&file_systems_lock);
++ tmp = file_systems;
++ while (tmp && len < PAGE_SIZE - 80) {
++ len += sprintf(buf+len, "%s\t%s\n",
++ (tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",
++ tmp->name);
++ tmp = tmp->next;
++ }
++ read_unlock(&file_systems_lock);
++ return len;
++}
++
++struct file_system_type *get_fs_type(const char *name)
++{
++ struct file_system_type *fs;
++
++ read_lock(&file_systems_lock);
++ fs = *(find_filesystem(name));
++ if (fs && !try_inc_mod_count(fs->owner))
++ fs = NULL;
++ read_unlock(&file_systems_lock);
++ if (!fs && (request_module(name) == 0)) {
++ read_lock(&file_systems_lock);
++ fs = *(find_filesystem(name));
++ if (fs && !try_inc_mod_count(fs->owner))
++ fs = NULL;
++ read_unlock(&file_systems_lock);
++ }
++ return fs;
++}
++
++/**
++ * alloc_super - create new superblock
++ *
++ * Allocates and initializes a new &struct super_block. alloc_super()
++ * returns a pointer new superblock or %NULL if allocation had failed.
++ */
++static struct super_block *alloc_super(void)
++{
++ static struct super_operations empty_sops = {};
++ struct super_block *s = kmalloc(sizeof(struct super_block), GFP_USER);
++ if (s) {
++ memset(s, 0, sizeof(struct super_block));
++ INIT_LIST_HEAD(&s->s_dirty);
++ INIT_LIST_HEAD(&s->s_locked_inodes);
++ INIT_LIST_HEAD(&s->s_files);
++ INIT_LIST_HEAD(&s->s_instances);
++ init_rwsem(&s->s_umount);
++ sema_init(&s->s_lock, 1);
++ down_write(&s->s_umount);
++ s->s_count = S_BIAS;
++ atomic_set(&s->s_active, 1);
++ sema_init(&s->s_vfs_rename_sem,1);
++ sema_init(&s->s_nfsd_free_path_sem,1);
++ sema_init(&s->s_dquot.dqio_sem, 1);
++ sema_init(&s->s_dquot.dqoff_sem, 1);
++ s->s_maxbytes = MAX_NON_LFS;
++ s->s_op = &empty_sops;
++ s->dq_op = sb_dquot_ops;
++ s->s_qcop = sb_quotactl_ops;
++ }
++ return s;
++}
++
++/**
++ * destroy_super - frees a superblock
++ * @s: superblock to free
++ *
++ * Frees a superblock.
++ */
++static inline void destroy_super(struct super_block *s)
++{
++ kfree(s);
++}
++
++/* Superblock refcounting */
++
++/**
++ * deactivate_super - turn an active reference into temporary
++ * @s: superblock to deactivate
++ *
++ * Turns an active reference into temporary one. Returns 0 if there are
++ * other active references, 1 if we had deactivated the last one.
++ */
++static inline int deactivate_super(struct super_block *s)
++{
++ if (!atomic_dec_and_lock(&s->s_active, &sb_lock))
++ return 0;
++ s->s_count -= S_BIAS-1;
++ spin_unlock(&sb_lock);
++ return 1;
++}
++
++/**
++ * put_super - drop a temporary reference to superblock
++ * @s: superblock in question
++ *
++ * Drops a temporary reference, frees superblock if there's no
++ * references left.
++ */
++static inline void put_super(struct super_block *s)
++{
++ spin_lock(&sb_lock);
++ if (!--s->s_count)
++ destroy_super(s);
++ spin_unlock(&sb_lock);
++}
++
++/**
++ * grab_super - acquire an active reference
++ * @s - reference we are trying to make active
++ *
++ * Tries to acquire an active reference. grab_super() is used when we
++ * had just found a superblock in super_blocks or fs_type->fs_supers
++ * and want to turn it into a full-blown active reference. grab_super()
++ * is called with sb_lock held and drops it. Returns 1 in case of
++ * success, 0 if we had failed (superblock contents was already dead or
++ * dying when grab_super() had been called).
++ */
++static int grab_super(struct super_block *s)
++{
++ s->s_count++;
++ spin_unlock(&sb_lock);
++ down_write(&s->s_umount);
++ if (s->s_root) {
++ spin_lock(&sb_lock);
++ if (s->s_count > S_BIAS) {
++ atomic_inc(&s->s_active);
++ s->s_count--;
++ spin_unlock(&sb_lock);
++ return 1;
++ }
++ spin_unlock(&sb_lock);
++ }
++ up_write(&s->s_umount);
++ put_super(s);
++ return 0;
++}
++
++/**
++ * insert_super - put superblock on the lists
++ * @s: superblock in question
++ * @type: filesystem type it will belong to
++ *
++ * Associates superblock with fs type and puts it on per-type and global
++ * superblocks' lists. Should be called with sb_lock held; drops it.
++ */
++static void insert_super(struct super_block *s, struct file_system_type *type)
++{
++ s->s_type = type;
++ list_add(&s->s_list, super_blocks.prev);
++ list_add(&s->s_instances, &type->fs_supers);
++ spin_unlock(&sb_lock);
++ get_filesystem(type);
++}
++
++static void put_anon_dev(kdev_t dev);
++
++/**
++ * remove_super - makes superblock unreachable
++ * @s: superblock in question
++ *
++ * Removes superblock from the lists, unlocks it, drop the reference
++ * and releases the hosting device. @s should have no active
++ * references by that time and after remove_super() it's essentially
++ * in rundown mode - all remaining references are temporary, no new
++ * reference of any sort are going to appear and all holders of
++ * temporary ones will eventually drop them. At that point superblock
++ * itself will be destroyed; all its contents is already gone.
++ */
++static void remove_super(struct super_block *s)
++{
++ kdev_t dev = s->s_dev;
++ struct block_device *bdev = s->s_bdev;
++ struct file_system_type *fs = s->s_type;
++
++ spin_lock(&sb_lock);
++ list_del(&s->s_list);
++ list_del(&s->s_instances);
++ spin_unlock(&sb_lock);
++ up_write(&s->s_umount);
++ put_super(s);
++ put_filesystem(fs);
++ if (bdev)
++ blkdev_put(bdev, BDEV_FS);
++ else
++ put_anon_dev(dev);
++}
++
++struct vfsmount *alloc_vfsmnt(char *name);
++void free_vfsmnt(struct vfsmount *mnt);
++
++static inline struct super_block * find_super(kdev_t dev)
++{
++ struct list_head *p;
++
++ list_for_each(p, &super_blocks) {
++ struct super_block * s = sb_entry(p);
++ if (s->s_dev == dev) {
++ s->s_count++;
++ return s;
++ }
++ }
++ return NULL;
++}
++
++void drop_super(struct super_block *sb)
++{
++ up_read(&sb->s_umount);
++ put_super(sb);
++}
++
++static void write_super_lockfs(struct super_block *sb)
++{
++ lock_super(sb);
++ if (sb->s_root && sb->s_op) {
++ if (sb->s_dirt && sb->s_op->write_super)
++ sb->s_op->write_super(sb);
++ if (sb->s_op->write_super_lockfs) {
++ sb->s_op->write_super_lockfs(sb);
++ }
++ }
++ unlock_super(sb);
++}
++
++static inline void write_super(struct super_block *sb)
++{
++ lock_super(sb);
++ if (sb->s_root && sb->s_dirt)
++ if (sb->s_op && sb->s_op->write_super)
++ sb->s_op->write_super(sb);
++ unlock_super(sb);
++}
++
++/*
++ * Note: check the dirty flag before waiting, so we don't
++ * hold up the sync while mounting a device. (The newly
++ * mounted device won't need syncing.)
++ */
++void sync_supers(kdev_t dev)
++{
++ struct super_block * sb;
++
++ if (dev) {
++ sb = get_super(dev);
++ if (sb) {
++ if (sb->s_dirt)
++ write_super(sb);
++ drop_super(sb);
++ }
++ return;
++ }
++restart:
++ spin_lock(&sb_lock);
++ sb = sb_entry(super_blocks.next);
++ while (sb != sb_entry(&super_blocks))
++ if (sb->s_dirt) {
++ sb->s_count++;
++ spin_unlock(&sb_lock);
++ down_read(&sb->s_umount);
++ write_super(sb);
++ drop_super(sb);
++ goto restart;
++ } else
++ sb = sb_entry(sb->s_list.next);
++ spin_unlock(&sb_lock);
++}
++
++/*
++ * Note: don't check the dirty flag before waiting, we want the lock
++ * to happen every time this is called. dev must be non-zero
++ */
++void sync_supers_lockfs(kdev_t dev)
++{
++ struct super_block * sb;
++
++ down(&lockfs_sem) ;
++ if (dev) {
++ sb = get_super(dev);
++ if (sb) {
++ write_super_lockfs(sb);
++ drop_super(sb);
++ }
++ }
++}
++
++void unlockfs(kdev_t dev)
++{
++ struct super_block * sb;
++
++ if (dev) {
++ sb = get_super(dev);
++ if (sb) {
++ if (sb->s_op && sb->s_op->unlockfs)
++ sb->s_op->unlockfs(sb) ;
++ drop_super(sb);
++ }
++ }
++ up(&lockfs_sem) ;
++}
++
++/**
++ * get_super - get the superblock of a device
++ * @dev: device to get the superblock for
++ *
++ * Scans the superblock list and finds the superblock of the file system
++ * mounted on the device given. %NULL is returned if no match is found.
++ */
++
++struct super_block * get_super(kdev_t dev)
++{
++ struct super_block * s;
++
++ if (!dev)
++ return NULL;
++restart:
++ spin_lock(&sb_lock);
++ s = find_super(dev);
++ if (s) {
++ spin_unlock(&sb_lock);
++ down_read(&s->s_umount);
++ if (s->s_root)
++ return s;
++ drop_super(s);
++ goto restart;
++ }
++ spin_unlock(&sb_lock);
++ return NULL;
++}
++
++asmlinkage long sys_ustat(dev_t dev, struct ustat * ubuf)
++{
++ struct super_block *s;
++ struct ustat tmp;
++ struct statfs sbuf;
++ int err = -EINVAL;
++
++ s = get_super(to_kdev_t(dev));
++ if (s == NULL)
++ goto out;
++ err = vfs_statfs(s, &sbuf);
++ drop_super(s);
++ if (err)
++ goto out;
++
++ memset(&tmp,0,sizeof(struct ustat));
++ tmp.f_tfree = sbuf.f_bfree;
++ tmp.f_tinode = sbuf.f_ffree;
++
++ err = copy_to_user(ubuf,&tmp,sizeof(struct ustat)) ? -EFAULT : 0;
++out:
++ return err;
++}
++
++/**
++ * do_remount_sb - asks filesystem to change mount options.
++ * @sb: superblock in question
++ * @flags: numeric part of options
++ * @data: the rest of options
++ *
++ * Alters the mount options of a mounted file system.
++ */
++int do_remount_sb(struct super_block *sb, int flags, void *data)
++{
++ int retval;
++
++ if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev))
++ return -EACCES;
++ /*flags |= MS_RDONLY;*/
++ if (flags & MS_RDONLY)
++ acct_auto_close(sb->s_dev);
++ shrink_dcache_sb(sb);
++ fsync_super(sb);
++ /* If we are remounting RDONLY, make sure there are no rw files open */
++ if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY))
++ if (!fs_may_remount_ro(sb))
++ return -EBUSY;
++ if (sb->s_op && sb->s_op->remount_fs) {
++ lock_super(sb);
++ retval = sb->s_op->remount_fs(sb, &flags, data);
++ unlock_super(sb);
++ if (retval)
++ return retval;
++ }
++ sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
++ return 0;
++}
++
++/*
++ * Unnamed block devices are dummy devices used by virtual
++ * filesystems which don't use real block-devices. -- jrs
++ */
++
++enum {Max_anon = 256};
++static unsigned long unnamed_dev_in_use[Max_anon/(8*sizeof(unsigned long))];
++static spinlock_t unnamed_dev_lock = SPIN_LOCK_UNLOCKED;/* protects the above */
++
++/**
++ * put_anon_dev - release anonymous device number.
++ * @dev: device in question
++ */
++static void put_anon_dev(kdev_t dev)
++{
++ spin_lock(&unnamed_dev_lock);
++ clear_bit(MINOR(dev), unnamed_dev_in_use);
++ spin_unlock(&unnamed_dev_lock);
++}
++
++/**
++ * get_anon_super - allocate a superblock for non-device fs
++ * @type: filesystem type
++ * @compare: check if existing superblock is what we want
++ * @data: argument for @compare.
++ *
++ * get_anon_super is a helper for non-blockdevice filesystems.
++ * It either finds and returns one of the superblocks of given type
++ * (if it can find one that would satisfy caller) or creates a new
++ * one. In the either case we return an active reference to superblock
++ * with ->s_umount locked. If superblock is new it gets a new
++ * anonymous device allocated for it and is inserted into lists -
++ * other initialization is left to caller.
++ *
++ * Rather than duplicating all that logics every time when
++ * we want something that doesn't fit "nodev" and "single" we pull
++ * the relevant code into common helper and let get_sb_...() call
++ * it.
++ *
++ * NB: get_sb_...() is going to become an fs type method, with
++ * current ->read_super() becoming a callback used by common instances.
++ */
++struct super_block *get_anon_super(struct file_system_type *type,
++ int (*compare)(struct super_block *,void *), void *data)
++{
++ struct super_block *s = alloc_super();
++ kdev_t dev;
++ struct list_head *p;
++
++ if (!s)
++ return ERR_PTR(-ENOMEM);
++
++retry:
++ spin_lock(&sb_lock);
++ if (compare) list_for_each(p, &type->fs_supers) {
++ struct super_block *old;
++ old = list_entry(p, struct super_block, s_instances);
++ if (!compare(old, data))
++ continue;
++ if (!grab_super(old))
++ goto retry;
++ destroy_super(s);
++ return old;
++ }
++
++ spin_lock(&unnamed_dev_lock);
++ dev = find_first_zero_bit(unnamed_dev_in_use, Max_anon);
++ if (dev == Max_anon) {
++ spin_unlock(&unnamed_dev_lock);
++ spin_unlock(&sb_lock);
++ destroy_super(s);
++ return ERR_PTR(-EMFILE);
++ }
++ set_bit(dev, unnamed_dev_in_use);
++ spin_unlock(&unnamed_dev_lock);
++
++ s->s_dev = dev;
++ insert_super(s, type);
++ return s;
++}
++
++static struct super_block *get_sb_bdev(struct file_system_type *fs_type,
++ int flags, char *dev_name, void * data)
++{
++ struct inode *inode;
++ struct block_device *bdev;
++ struct block_device_operations *bdops;
++ devfs_handle_t de;
++ struct super_block * s;
++ struct nameidata nd;
++ struct list_head *p;
++ kdev_t dev;
++ int error = 0;
++ mode_t mode = FMODE_READ; /* we always need it ;-) */
++
++ /* What device it is? */
++ if (!dev_name || !*dev_name)
++ return ERR_PTR(-EINVAL);
++ if (path_init(dev_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd))
++ error = path_walk(dev_name, &nd);
++ if (error)
++ return ERR_PTR(error);
++ inode = nd.dentry->d_inode;
++ error = -ENOTBLK;
++ if (!S_ISBLK(inode->i_mode))
++ goto out;
++ error = -EACCES;
++ if (nd.mnt->mnt_flags & MNT_NODEV)
++ goto out;
++ bd_acquire(inode);
++ bdev = inode->i_bdev;
++ de = devfs_get_handle_from_inode (inode);
++ bdops = devfs_get_ops (de); /* Increments module use count */
++ if (bdops) bdev->bd_op = bdops;
++ /* Done with lookups, semaphore down */
++ dev = to_kdev_t(bdev->bd_dev);
++ if (!(flags & MS_RDONLY))
++ mode |= FMODE_WRITE;
++ error = blkdev_get(bdev, mode, 0, BDEV_FS);
++ devfs_put_ops (de); /* Decrement module use count now we're safe */
++ if (error)
++ goto out;
++ check_disk_change(dev);
++ error = -EACCES;
++ if (!(flags & MS_RDONLY) && is_read_only(dev))
++ goto out1;
++
++ error = -ENOMEM;
++ s = alloc_super();
++ if (!s)
++ goto out1;
++
++ error = -EBUSY;
++ down(&lockfs_sem);
++restart:
++ spin_lock(&sb_lock);
++
++ list_for_each(p, &super_blocks) {
++ struct super_block *old = sb_entry(p);
++ if (old->s_dev != dev)
++ continue;
++ if (old->s_type != fs_type ||
++ ((flags ^ old->s_flags) & MS_RDONLY)) {
++ spin_unlock(&sb_lock);
++ destroy_super(s);
++ up(&lockfs_sem);
++ goto out1;
++ }
++ if (!grab_super(old))
++ goto restart;
++ destroy_super(s);
++ blkdev_put(bdev, BDEV_FS);
++ path_release(&nd);
++ up(&lockfs_sem);
++ return old;
++ }
++ s->s_dev = dev;
++ s->s_bdev = bdev;
++ s->s_flags = flags;
++ insert_super(s, fs_type);
++ up(&lockfs_sem);
++ if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
++ goto Einval;
++ s->s_flags |= MS_ACTIVE;
++ path_release(&nd);
++ return s;
++
++Einval:
++ deactivate_super(s);
++ remove_super(s);
++ error = -EINVAL;
++ goto out;
++out1:
++ blkdev_put(bdev, BDEV_FS);
++out:
++ path_release(&nd);
++ return ERR_PTR(error);
++}
++
++static struct super_block *get_sb_nodev(struct file_system_type *fs_type,
++ int flags, char *dev_name, void *data)
++{
++ struct super_block *s = get_anon_super(fs_type, NULL, NULL);
++
++ if (IS_ERR(s))
++ return s;
++
++ s->s_flags = flags;
++ if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0)) {
++ deactivate_super(s);
++ remove_super(s);
++ return ERR_PTR(-EINVAL);
++ }
++ s->s_flags |= MS_ACTIVE;
++ return s;
++}
++
++static int compare_single(struct super_block *s, void *p)
++{
++ return 1;
++}
++
++static struct super_block *get_sb_single(struct file_system_type *fs_type,
++ int flags, char *dev_name, void *data)
++{
++ struct super_block *s = get_anon_super(fs_type, compare_single, NULL);
++
++ if (IS_ERR(s))
++ return s;
++ if (!s->s_root) {
++ s->s_flags = flags;
++ if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0)) {
++ deactivate_super(s);
++ remove_super(s);
++ return ERR_PTR(-EINVAL);
++ }
++ s->s_flags |= MS_ACTIVE;
++ }
++ do_remount_sb(s, flags, data);
++ return s;
++}
++
++struct vfsmount *
++do_kern_mount(const char *fstype, int flags, char *name, void *data)
++{
++ struct file_system_type *type = get_fs_type(fstype);
++ struct super_block *sb = ERR_PTR(-ENOMEM);
++ struct vfsmount *mnt;
++
++ if (!type)
++ return ERR_PTR(-ENODEV);
++
++ mnt = alloc_vfsmnt(name);
++ if (!mnt)
++ goto out;
++ if (type->fs_flags & FS_REQUIRES_DEV)
++ sb = get_sb_bdev(type, flags, name, data);
++ else if (type->fs_flags & FS_SINGLE)
++ sb = get_sb_single(type, flags, name, data);
++ else
++ sb = get_sb_nodev(type, flags, name, data);
++ if (IS_ERR(sb))
++ goto out_mnt;
++ if (type->fs_flags & FS_NOMOUNT)
++ sb->s_flags |= MS_NOUSER;
++ mnt->mnt_sb = sb;
++ mnt->mnt_root = dget(sb->s_root);
++ mnt->mnt_mountpoint = sb->s_root;
++ mnt->mnt_parent = mnt;
++ up_write(&sb->s_umount);
++ put_filesystem(type);
++ return mnt;
++out_mnt:
++ free_vfsmnt(mnt);
++out:
++ put_filesystem(type);
++ return (struct vfsmount *)sb;
++}
++
++void kill_super(struct super_block *sb)
++{
++ struct dentry *root = sb->s_root;
++ struct file_system_type *fs = sb->s_type;
++ struct super_operations *sop = sb->s_op;
++
++ if (!deactivate_super(sb))
++ return;
++
++ down(&lockfs_sem);
++ down_write(&sb->s_umount);
++ up(&lockfs_sem);
++
++ sb->s_root = NULL;
++ /* Need to clean after the sucker */
++ if (fs->fs_flags & FS_LITTER)
++ d_genocide(root);
++ shrink_dcache_parent(root);
++ dput(root);
++ fsync_super(sb);
++ lock_super(sb);
++ lock_kernel();
++ sb->s_flags &= ~MS_ACTIVE;
+ invalidate_inodes(sb, 0); /* bad name - it should be evict_inodes() */
- if (sop) {
- if (sop->write_super && sb->s_dirt)
- sop->write_super(sb);
-@@ -844,7 +844,7 @@ void kill_super(struct super_block *sb)
- }
-
- /* Forget any remaining inodes */
-- if (invalidate_inodes(sb)) {
++ if (sop) {
++ if (sop->write_super && sb->s_dirt)
++ sop->write_super(sb);
++ if (sop->put_super)
++ sop->put_super(sb);
++ }
++
++ /* Forget any remaining inodes */
+ if (invalidate_inodes(sb, 1)) {
- printk(KERN_ERR "VFS: Busy inodes after unmount. "
- "Self-destruct in 5 seconds. Have a nice day...\n");
- }
---- linux-2.4.20/include/linux/fs.h~invalidate_show 2003-04-08 23:34:36.000000000 -0600
-+++ linux-2.4.20-braam/include/linux/fs.h 2003-04-08 23:34:36.000000000 -0600
-@@ -1237,7 +1237,7 @@ static inline void mark_buffer_dirty_ino
- extern void set_buffer_flushtime(struct buffer_head *);
- extern void balance_dirty(void);
- extern int check_disk_change(kdev_t);
--extern int invalidate_inodes(struct super_block *);
++ printk(KERN_ERR "VFS: Busy inodes after unmount. "
++ "Self-destruct in 5 seconds. Have a nice day...\n");
++ }
++
++ unlock_kernel();
++ unlock_super(sb);
++ remove_super(sb);
++}
++
++struct vfsmount *kern_mount(struct file_system_type *type)
++{
++ return do_kern_mount(type->name, 0, (char *)type->name, NULL);
++}
+--- /dev/null 2003-01-30 18:24:37.000000000 +0800
++++ linux-2.4.19-hp3_pnnl1-root/include/linux/fs.h 2003-04-15 13:21:57.000000000 +0800
+@@ -0,0 +1,1709 @@
++#ifndef _LINUX_FS_H
++#define _LINUX_FS_H
++
++/*
++ * This file has definitions for some important file table
++ * structures etc.
++ */
++
++#include <linux/config.h>
++#include <linux/linkage.h>
++#include <linux/limits.h>
++#include <linux/wait.h>
++#include <linux/types.h>
++#include <linux/vfs.h>
++#include <linux/net.h>
++#include <linux/kdev_t.h>
++#include <linux/ioctl.h>
++#include <linux/list.h>
++#include <linux/dcache.h>
++#include <linux/stat.h>
++#include <linux/cache.h>
++#include <linux/stddef.h>
++#include <linux/string.h>
++
++#include <asm/atomic.h>
++#include <asm/bitops.h>
++
++struct poll_table_struct;
++
++
++/*
++ * It's silly to have NR_OPEN bigger than NR_FILE, but you can change
++ * the file limit at runtime and only root can increase the per-process
++ * nr_file rlimit, so it's safe to set up a ridiculously high absolute
++ * upper limit on files-per-process.
++ *
++ * Some programs (notably those using select()) may have to be
++ * recompiled to take full advantage of the new limits..
++ */
++
++/* Fixed constants first: */
++#undef NR_OPEN
++#define NR_OPEN (1024*1024) /* Absolute upper limit on fd num */
++#define INR_OPEN 1024 /* Initial setting for nfile rlimits */
++
++#define BLOCK_SIZE_BITS 10
++#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS)
++
++/* And dynamically-tunable limits and defaults: */
++struct files_stat_struct {
++ int nr_files; /* read only */
++ int nr_free_files; /* read only */
++ int max_files; /* tunable */
++};
++extern struct files_stat_struct files_stat;
++
++struct inodes_stat_t {
++ int nr_inodes;
++ int nr_unused;
++ int dummy[5];
++};
++extern struct inodes_stat_t inodes_stat;
++
++extern int leases_enable, dir_notify_enable, lease_break_time;
++
++#define NR_FILE 8192 /* this can well be larger on a larger system */
++#define NR_RESERVED_FILES 10 /* reserved for root */
++#define NR_SUPER 256
++
++#define MAY_EXEC 1
++#define MAY_WRITE 2
++#define MAY_READ 4
++
++#define FMODE_READ 1
++#define FMODE_WRITE 2
++
++#define READ 0
++#define WRITE 1
++#define READA 2 /* read-ahead - don't block if no resources */
++#define SPECIAL 4 /* For non-blockdevice requests in request queue */
++
++#define SEL_IN 1
++#define SEL_OUT 2
++#define SEL_EX 4
++
++/* public flags for file_system_type */
++#define FS_REQUIRES_DEV 1
++#define FS_NO_DCACHE 2 /* Only dcache the necessary things. */
++#define FS_NO_PRELIM 4 /* prevent preloading of dentries, even if
++ * FS_NO_DCACHE is not set.
++ */
++#define FS_SINGLE 8 /* Filesystem that can have only one superblock */
++#define FS_NOMOUNT 16 /* Never mount from userland */
++#define FS_LITTER 32 /* Keeps the tree in dcache */
++#define FS_ODD_RENAME 32768 /* Temporary stuff; will go away as soon
++ * as nfs_rename() will be cleaned up
++ */
++/*
++ * These are the fs-independent mount-flags: up to 32 flags are supported
++ */
++#define MS_RDONLY 1 /* Mount read-only */
++#define MS_NOSUID 2 /* Ignore suid and sgid bits */
++#define MS_NODEV 4 /* Disallow access to device special files */
++#define MS_NOEXEC 8 /* Disallow program execution */
++#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
++#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
++#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */
++#define MS_NOATIME 1024 /* Do not update access times. */
++#define MS_NODIRATIME 2048 /* Do not update directory access times */
++#define MS_BIND 4096
++#define MS_MOVE 8192
++#define MS_REC 16384
++#define MS_VERBOSE 32768
++#define MS_POSIXACL 65536 /* VFS does not apply the umask */
++#define MS_ACTIVE (1<<30)
++#define MS_NOUSER (1<<31)
++
++/*
++ * Superblock flags that can be altered by MS_REMOUNT
++ */
++#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_NOATIME|\
++ MS_NODIRATIME)
++
++/*
++ * Old magic mount flag and mask
++ */
++#define MS_MGC_VAL 0xC0ED0000
++#define MS_MGC_MSK 0xffff0000
++
++/* Inode flags - they have nothing to superblock flags now */
++
++#define S_SYNC 1 /* Writes are synced at once */
++#define S_NOATIME 2 /* Do not update access times */
++#define S_QUOTA 4 /* Quota initialized for file */
++#define S_APPEND 8 /* Append-only file */
++#define S_IMMUTABLE 16 /* Immutable file */
++#define S_DEAD 32 /* removed, but still open directory */
++#define S_NOQUOTA 64 /* Inode is not counted to quota */
++
++/*
++ * Note that nosuid etc flags are inode-specific: setting some file-system
++ * flags just means all the inodes inherit those flags by default. It might be
++ * possible to override it selectively if you really wanted to with some
++ * ioctl() that is not currently implemented.
++ *
++ * Exception: MS_RDONLY is always applied to the entire file system.
++ *
++ * Unfortunately, it is possible to change a filesystems flags with it mounted
++ * with files in use. This means that all of the inodes will not have their
++ * i_flags updated. Hence, i_flags no longer inherit the superblock mount
++ * flags, so these have to be checked separately. -- rmk@arm.uk.linux.org
++ */
++#define __IS_FLG(inode,flg) ((inode)->i_sb->s_flags & (flg))
++
++#define IS_RDONLY(inode) ((inode)->i_sb->s_flags & MS_RDONLY)
++#define IS_SYNC(inode) (__IS_FLG(inode, MS_SYNCHRONOUS) || ((inode)->i_flags & S_SYNC))
++#define IS_MANDLOCK(inode) __IS_FLG(inode, MS_MANDLOCK)
++
++#define IS_QUOTAINIT(inode) ((inode)->i_flags & S_QUOTA)
++#define IS_NOQUOTA(inode) ((inode)->i_flags & S_NOQUOTA)
++#define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
++#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
++#define IS_NOATIME(inode) (__IS_FLG(inode, MS_NOATIME) || ((inode)->i_flags & S_NOATIME))
++#define IS_NODIRATIME(inode) __IS_FLG(inode, MS_NODIRATIME)
++#define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL)
++
++#define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD)
++
++/* the read-only stuff doesn't really belong here, but any other place is
++ probably as bad and I don't want to create yet another include file. */
++
++#define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */
++#define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */
++#define BLKRRPART _IO(0x12,95) /* re-read partition table */
++#define BLKGETSIZE _IO(0x12,96) /* return device size /512 (long *arg) */
++#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
++#define BLKRASET _IO(0x12,98) /* Set read ahead for block device */
++#define BLKRAGET _IO(0x12,99) /* get current read ahead setting */
++#define BLKFRASET _IO(0x12,100)/* set filesystem (mm/filemap.c) read-ahead */
++#define BLKFRAGET _IO(0x12,101)/* get filesystem (mm/filemap.c) read-ahead */
++#define BLKSECTSET _IO(0x12,102)/* set max sectors per request (ll_rw_blk.c) */
++#define BLKSECTGET _IO(0x12,103)/* get max sectors per request (ll_rw_blk.c) */
++#define BLKSSZGET _IO(0x12,104)/* get block device sector size */
++#if 0
++#define BLKPG _IO(0x12,105)/* See blkpg.h */
++#define BLKELVGET _IOR(0x12,106,sizeof(blkelv_ioctl_arg_t))/* elevator get */
++#define BLKELVSET _IOW(0x12,107,sizeof(blkelv_ioctl_arg_t))/* elevator set */
++/* This was here just to show that the number is taken -
++ probably all these _IO(0x12,*) ioctls should be moved to blkpg.h. */
++#endif
++/* A jump here: 108-111 have been used for various private purposes. */
++#define BLKBSZGET _IOR(0x12,112,sizeof(int))
++#define BLKBSZSET _IOW(0x12,113,sizeof(int))
++#define BLKGETSIZE64 _IOR(0x12,114,sizeof(u64)) /* return device size in bytes (u64 *arg) */
++
++#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
++#define FIBMAP _IO(0x00,1) /* bmap access */
++#define FIGETBSZ _IO(0x00,2) /* get the block size used for bmap */
++
++#ifdef __KERNEL__
++
++#include <asm/semaphore.h>
++#include <asm/byteorder.h>
++
++extern void update_atime (struct inode *);
++#define UPDATE_ATIME(inode) update_atime (inode)
++
++extern void buffer_init(unsigned long);
++extern void inode_init(unsigned long);
++extern void mnt_init(unsigned long);
++
++/* bh state bits */
++enum bh_state_bits {
++ BH_Uptodate, /* 1 if the buffer contains valid data */
++ BH_Dirty, /* 1 if the buffer is dirty */
++ BH_Lock, /* 1 if the buffer is locked */
++ BH_Req, /* 0 if the buffer has been invalidated */
++ BH_Mapped, /* 1 if the buffer has a disk mapping */
++ BH_New, /* 1 if the buffer is new and not yet written out */
++ BH_Async, /* 1 if the buffer is under end_buffer_io_async I/O */
++ BH_Wait_IO, /* 1 if we should write out this buffer */
++ BH_Launder, /* 1 if we can throttle on this buffer */
++ BH_JBD, /* 1 if it has an attached journal_head */
++ BH_Delay, /* 1 if the buffer is delayed allocate */
++
++ BH_PrivateStart,/* not a state bit, but the first bit available
++ * for private allocation by other entities
++ */
++};
++
++#define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512)
++
++/*
++ * Try to keep the most commonly used fields in single cache lines (16
++ * bytes) to improve performance. This ordering should be
++ * particularly beneficial on 32-bit processors.
++ *
++ * We use the first 16 bytes for the data which is used in searches
++ * over the block hash lists (ie. getblk() and friends).
++ *
++ * The second 16 bytes we use for lru buffer scans, as used by
++ * sync_buffers() and refill_freelist(). -- sct
++ */
++struct buffer_head {
++ /* First cache line: */
++ struct buffer_head *b_next; /* Hash queue list */
++ unsigned long b_blocknr; /* block number */
++ unsigned short b_size; /* block size */
++ unsigned short b_list; /* List that this buffer appears */
++ kdev_t b_dev; /* device (B_FREE = free) */
++
++ atomic_t b_count; /* users using this block */
++ kdev_t b_rdev; /* Real device */
++ unsigned long b_state; /* buffer state bitmap (see above) */
++ unsigned long b_flushtime; /* Time when (dirty) buffer should be written */
++
++ struct buffer_head *b_next_free;/* lru/free list linkage */
++ struct buffer_head *b_prev_free;/* doubly linked list of buffers */
++ struct buffer_head *b_this_page;/* circular list of buffers in one page */
++ struct buffer_head *b_reqnext; /* request queue */
++
++ struct buffer_head **b_pprev; /* doubly linked list of hash-queue */
++ char * b_data; /* pointer to data block */
++ struct page *b_page; /* the page this bh is mapped to */
++ void (*b_end_io)(struct buffer_head *bh, int uptodate); /* I/O completion */
++ void *b_private; /* reserved for b_end_io */
++
++ unsigned long b_rsector; /* Real buffer location on disk */
++ wait_queue_head_t b_wait;
++
++ struct inode * b_inode;
++ struct list_head b_inode_buffers; /* doubly linked list of inode dirty buffers */
++};
++
++typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
++void init_buffer(struct buffer_head *, bh_end_io_t *, void *);
++
++#define __buffer_state(bh, state) (((bh)->b_state & (1UL << BH_##state)) != 0)
++
++#define buffer_uptodate(bh) __buffer_state(bh,Uptodate)
++#define buffer_dirty(bh) __buffer_state(bh,Dirty)
++#define buffer_locked(bh) __buffer_state(bh,Lock)
++#define buffer_req(bh) __buffer_state(bh,Req)
++#define buffer_mapped(bh) __buffer_state(bh,Mapped)
++#define buffer_new(bh) __buffer_state(bh,New)
++#define buffer_async(bh) __buffer_state(bh,Async)
++#define buffer_launder(bh) __buffer_state(bh,Launder)
++#define buffer_delay(bh) __buffer_state(bh,Delay)
++
++#define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK)
++
++extern void set_bh_page(struct buffer_head *bh, struct page *page, unsigned long offset);
++
++#define touch_buffer(bh) mark_page_accessed(bh->b_page)
++
++
++#include <linux/pipe_fs_i.h>
++#include <linux/minix_fs_i.h>
++#include <linux/ext2_fs_i.h>
++#include <linux/ext3_fs_i.h>
++#include <linux/hpfs_fs_i.h>
++#include <linux/ntfs_fs_i.h>
++#include <linux/msdos_fs_i.h>
++#include <linux/umsdos_fs_i.h>
++#include <linux/iso_fs_i.h>
++#include <linux/nfs_fs_i.h>
++#include <linux/sysv_fs_i.h>
++#include <linux/affs_fs_i.h>
++#include <linux/ufs_fs_i.h>
++#include <linux/efs_fs_i.h>
++#include <linux/coda_fs_i.h>
++#include <linux/romfs_fs_i.h>
++#include <linux/shmem_fs.h>
++#include <linux/smb_fs_i.h>
++#include <linux/hfs_fs_i.h>
++#include <linux/adfs_fs_i.h>
++#include <linux/qnx4_fs_i.h>
++#include <linux/reiserfs_fs_i.h>
++#include <linux/bfs_fs_i.h>
++#include <linux/udf_fs_i.h>
++#include <linux/ncp_fs_i.h>
++#include <linux/proc_fs_i.h>
++#include <linux/usbdev_fs_i.h>
++#include <linux/jffs2_fs_i.h>
++#include <linux/cramfs_fs_sb.h>
++
++/*
++ * Attribute flags. These should be or-ed together to figure out what
++ * has been changed!
++ */
++#define ATTR_MODE 1
++#define ATTR_UID 2
++#define ATTR_GID 4
++#define ATTR_SIZE 8
++#define ATTR_ATIME 16
++#define ATTR_MTIME 32
++#define ATTR_CTIME 64
++#define ATTR_ATIME_SET 128
++#define ATTR_MTIME_SET 256
++#define ATTR_FORCE 512 /* Not a change, but a change it */
++#define ATTR_ATTR_FLAG 1024
++#define ATTR_RAW 2048 /* file system, not vfs will massage attrs */
++#define ATTR_FROM_OPEN 4096 /* called from open path, ie O_TRUNC */
++
++/*
++ * This is the Inode Attributes structure, used for notify_change(). It
++ * uses the above definitions as flags, to know which values have changed.
++ * Also, in this manner, a Filesystem can look at only the values it cares
++ * about. Basically, these are the attributes that the VFS layer can
++ * request to change from the FS layer.
++ *
++ * Derek Atkins <warlord@MIT.EDU> 94-10-20
++ */
++struct iattr {
++ unsigned int ia_valid;
++ umode_t ia_mode;
++ uid_t ia_uid;
++ gid_t ia_gid;
++ loff_t ia_size;
++ time_t ia_atime;
++ time_t ia_mtime;
++ time_t ia_ctime;
++ unsigned int ia_attr_flags;
++};
++
++/*
++ * This is the inode attributes flag definitions
++ */
++#define ATTR_FLAG_SYNCRONOUS 1 /* Syncronous write */
++#define ATTR_FLAG_NOATIME 2 /* Don't update atime */
++#define ATTR_FLAG_APPEND 4 /* Append-only file */
++#define ATTR_FLAG_IMMUTABLE 8 /* Immutable file */
++#define ATTR_FLAG_NODIRATIME 16 /* Don't update atime for directory */
++
++/*
++ * Includes for diskquotas and mount structures.
++ */
++#include <linux/quota.h>
++#include <linux/mount.h>
++
++/*
++ * oh the beauties of C type declarations.
++ */
++struct page;
++struct address_space;
++struct kiobuf;
++
++struct address_space_operations {
++ int (*writepage)(struct page *);
++ int (*readpage)(struct file *, struct page *);
++ int (*sync_page)(struct page *);
++ /*
++ * ext3 requires that a successful prepare_write() call be followed
++ * by a commit_write() call - they must be balanced
++ */
++ int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
++ int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
++ /* Unfortunately this kludge is needed for FIBMAP. Don't use it */
++ int (*bmap)(struct address_space *, long);
++ int (*flushpage) (struct page *, unsigned long);
++ int (*releasepage) (struct page *, int);
++#define KERNEL_HAS_O_DIRECT /* this is for modules out of the kernel */
++ int (*direct_IO)(int, struct inode *, struct kiobuf *, unsigned long, int);
++};
++
++struct address_space {
++ struct list_head clean_pages; /* list of clean pages */
++ struct list_head dirty_pages; /* list of dirty pages */
++ struct list_head locked_pages; /* list of locked pages */
++ unsigned long nrpages; /* number of total pages */
++ struct address_space_operations *a_ops; /* methods */
++ struct inode *host; /* owner: inode, block_device */
++ struct vm_area_struct *i_mmap; /* list of private mappings */
++ struct vm_area_struct *i_mmap_shared; /* list of shared mappings */
++ spinlock_t i_shared_lock; /* and spinlock protecting it */
++ int gfp_mask; /* how to allocate the pages */
++};
++
++struct char_device {
++ struct list_head hash;
++ atomic_t count;
++ dev_t dev;
++ atomic_t openers;
++ struct semaphore sem;
++};
++
++struct block_device {
++ struct list_head bd_hash;
++ atomic_t bd_count;
++ struct inode * bd_inode;
++ dev_t bd_dev; /* not a kdev_t - it's a search key */
++ int bd_openers;
++ const struct block_device_operations *bd_op;
++ struct semaphore bd_sem; /* open/close mutex */
++ struct list_head bd_inodes;
++};
++
++struct inode {
++ struct list_head i_hash;
++ struct list_head i_list;
++ struct list_head i_dentry;
++
++ struct list_head i_dirty_buffers;
++ struct list_head i_dirty_data_buffers;
++
++ unsigned long i_ino;
++ atomic_t i_count;
++ kdev_t i_dev;
++ umode_t i_mode;
++ nlink_t i_nlink;
++ uid_t i_uid;
++ gid_t i_gid;
++ kdev_t i_rdev;
++ loff_t i_size;
++ time_t i_atime;
++ time_t i_mtime;
++ time_t i_ctime;
++ unsigned int i_blkbits;
++ unsigned long i_blksize;
++ unsigned long i_blocks;
++ unsigned long i_version;
++ unsigned short i_bytes;
++ struct semaphore i_sem;
++ struct semaphore i_zombie;
++ struct inode_operations *i_op;
++ struct file_operations *i_fop; /* former ->i_op->default_file_ops */
++ struct super_block *i_sb;
++ wait_queue_head_t i_wait;
++ struct file_lock *i_flock;
++ struct address_space *i_mapping;
++ struct address_space i_data;
++ struct dquot *i_dquot[MAXQUOTAS];
++ /* These three should probably be a union */
++ struct list_head i_devices;
++ struct pipe_inode_info *i_pipe;
++ struct block_device *i_bdev;
++ struct char_device *i_cdev;
++
++ unsigned long i_dnotify_mask; /* Directory notify events */
++ struct dnotify_struct *i_dnotify; /* for directory notifications */
++
++ unsigned long i_state;
++
++ unsigned int i_flags;
++ unsigned char i_sock;
++
++ atomic_t i_writecount;
++ unsigned int i_attr_flags;
++ __u32 i_generation;
++ union {
++ struct minix_inode_info minix_i;
++ struct ext2_inode_info ext2_i;
++ struct ext3_inode_info ext3_i;
++ struct hpfs_inode_info hpfs_i;
++ struct ntfs_inode_info ntfs_i;
++ struct msdos_inode_info msdos_i;
++ struct umsdos_inode_info umsdos_i;
++ struct iso_inode_info isofs_i;
++ struct nfs_inode_info nfs_i;
++ struct sysv_inode_info sysv_i;
++ struct affs_inode_info affs_i;
++ struct ufs_inode_info ufs_i;
++ struct efs_inode_info efs_i;
++ struct romfs_inode_info romfs_i;
++ struct shmem_inode_info shmem_i;
++ struct coda_inode_info coda_i;
++ struct smb_inode_info smbfs_i;
++ struct hfs_inode_info hfs_i;
++ struct adfs_inode_info adfs_i;
++ struct qnx4_inode_info qnx4_i;
++ struct reiserfs_inode_info reiserfs_i;
++ struct bfs_inode_info bfs_i;
++ struct udf_inode_info udf_i;
++ struct ncp_inode_info ncpfs_i;
++ struct proc_inode_info proc_i;
++ struct socket socket_i;
++ struct usbdev_inode_info usbdev_i;
++ struct jffs2_inode_info jffs2_i;
++ void *generic_ip;
++ } u;
++};
++
++static inline void inode_add_bytes(struct inode *inode, loff_t bytes)
++{
++ inode->i_blocks += bytes >> 9;
++ bytes &= 511;
++ inode->i_bytes += bytes;
++ if (inode->i_bytes >= 512) {
++ inode->i_blocks++;
++ inode->i_bytes -= 512;
++ }
++}
++
++static inline void inode_sub_bytes(struct inode *inode, loff_t bytes)
++{
++ inode->i_blocks -= bytes >> 9;
++ bytes &= 511;
++ if (inode->i_bytes < bytes) {
++ inode->i_blocks--;
++ inode->i_bytes += 512;
++ }
++ inode->i_bytes -= bytes;
++}
++
++static inline loff_t inode_get_bytes(struct inode *inode)
++{
++ return (((loff_t)inode->i_blocks) << 9) + inode->i_bytes;
++}
++
++static inline void inode_set_bytes(struct inode *inode, loff_t bytes)
++{
++ inode->i_blocks = bytes >> 9;
++ inode->i_bytes = bytes & 511;
++}
++
++struct fown_struct {
++ int pid; /* pid or -pgrp where SIGIO should be sent */
++ uid_t uid, euid; /* uid/euid of process setting the owner */
++ int signum; /* posix.1b rt signal to be delivered on IO */
++};
++
++struct file {
++ struct list_head f_list;
++ struct dentry *f_dentry;
++ struct vfsmount *f_vfsmnt;
++ struct file_operations *f_op;
++ atomic_t f_count;
++ unsigned int f_flags;
++ mode_t f_mode;
++ loff_t f_pos;
++ unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
++ struct fown_struct f_owner;
++ unsigned int f_uid, f_gid;
++ int f_error;
++
++ unsigned long f_version;
++
++ /* needed for tty driver, and maybe others */
++ void *private_data;
++ struct lookup_intent *f_intent;
++
++ /* preallocated helper kiobuf to speedup O_DIRECT */
++ struct kiobuf *f_iobuf;
++ long f_iobuf_lock;
++};
++extern spinlock_t files_lock;
++#define file_list_lock() spin_lock(&files_lock);
++#define file_list_unlock() spin_unlock(&files_lock);
++
++#define get_file(x) atomic_inc(&(x)->f_count)
++#define file_count(x) atomic_read(&(x)->f_count)
++
++extern int init_private_file(struct file *, struct dentry *, int);
++
++#define MAX_NON_LFS ((1UL<<31) - 1)
++
++/* Page cache limit. The filesystems should put that into their s_maxbytes
++ limits, otherwise bad things can happen in VM. */
++#if BITS_PER_LONG==32
++#define MAX_LFS_FILESIZE (((u64)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1)
++#elif BITS_PER_LONG==64
++#define MAX_LFS_FILESIZE 0x7fffffffffffffff
++#endif
++#define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */
++#define BLKSETLASTSECT _IO(0x12,109) /* get last sector of block device */
++
++#define FL_POSIX 1
++#define FL_FLOCK 2
++#define FL_BROKEN 4 /* broken flock() emulation */
++#define FL_ACCESS 8 /* for processes suspended by mandatory locking */
++#define FL_LOCKD 16 /* lock held by rpc.lockd */
++#define FL_LEASE 32 /* lease held on this file */
++
++/*
++ * The POSIX file lock owner is determined by
++ * the "struct files_struct" in the thread group
++ * (or NULL for no owner - BSD locks).
++ *
++ * Lockd stuffs a "host" pointer into this.
++ */
++typedef struct files_struct *fl_owner_t;
++
++struct file_lock {
++ struct file_lock *fl_next; /* singly linked list for this inode */
++ struct list_head fl_link; /* doubly linked list of all locks */
++ struct list_head fl_block; /* circular list of blocked processes */
++ fl_owner_t fl_owner;
++ unsigned int fl_pid;
++ wait_queue_head_t fl_wait;
++ struct file *fl_file;
++ unsigned char fl_flags;
++ unsigned char fl_type;
++ loff_t fl_start;
++ loff_t fl_end;
++
++ void (*fl_notify)(struct file_lock *); /* unblock callback */
++ void (*fl_insert)(struct file_lock *); /* lock insertion callback */
++ void (*fl_remove)(struct file_lock *); /* lock removal callback */
++
++ struct fasync_struct * fl_fasync; /* for lease break notifications */
++
++ union {
++ struct nfs_lock_info nfs_fl;
++ } fl_u;
++};
++
++/* The following constant reflects the upper bound of the file/locking space */
++#ifndef OFFSET_MAX
++#define INT_LIMIT(x) (~((x)1 << (sizeof(x)*8 - 1)))
++#define OFFSET_MAX INT_LIMIT(loff_t)
++#define OFFT_OFFSET_MAX INT_LIMIT(off_t)
++#endif
++
++extern struct list_head file_lock_list;
++
++#include <linux/fcntl.h>
++
++extern int fcntl_getlk(unsigned int, struct flock *);
++extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
++
++extern int fcntl_getlk64(unsigned int, struct flock64 *);
++extern int fcntl_setlk64(unsigned int, unsigned int, struct flock64 *);
++
++/* fs/locks.c */
++extern void locks_init_lock(struct file_lock *);
++extern void locks_copy_lock(struct file_lock *, struct file_lock *);
++extern void locks_remove_posix(struct file *, fl_owner_t);
++extern void locks_remove_flock(struct file *);
++extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
++extern int posix_lock_file(struct file *, struct file_lock *, unsigned int);
++extern void posix_block_lock(struct file_lock *, struct file_lock *);
++extern void posix_unblock_lock(struct file_lock *);
++extern int posix_locks_deadlock(struct file_lock *, struct file_lock *);
++extern int __get_lease(struct inode *inode, unsigned int flags);
++extern time_t lease_get_mtime(struct inode *);
++extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
++extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
++
++struct fasync_struct {
++ int magic;
++ int fa_fd;
++ struct fasync_struct *fa_next; /* singly linked list */
++ struct file *fa_file;
++};
++
++#define FASYNC_MAGIC 0x4601
++
++/* SMP safe fasync helpers: */
++extern int fasync_helper(int, struct file *, int, struct fasync_struct **);
++/* can be called from interrupts */
++extern void kill_fasync(struct fasync_struct **, int, int);
++/* only for net: no internal synchronization */
++extern void __kill_fasync(struct fasync_struct *, int, int);
++
++struct nameidata {
++ struct dentry *dentry;
++ struct vfsmount *mnt;
++ struct qstr last;
++ unsigned int flags;
++ int last_type;
++};
++
++/*
++ * Umount options
++ */
++
++#define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */
++#define MNT_DETACH 0x00000002 /* Just detach from the tree */
++
++#include <linux/minix_fs_sb.h>
++#include <linux/ext2_fs_sb.h>
++#include <linux/ext3_fs_sb.h>
++#include <linux/hpfs_fs_sb.h>
++#include <linux/ntfs_fs_sb.h>
++#include <linux/msdos_fs_sb.h>
++#include <linux/iso_fs_sb.h>
++#include <linux/nfs_fs_sb.h>
++#include <linux/sysv_fs_sb.h>
++#include <linux/affs_fs_sb.h>
++#include <linux/ufs_fs_sb.h>
++#include <linux/efs_fs_sb.h>
++#include <linux/romfs_fs_sb.h>
++#include <linux/smb_fs_sb.h>
++#include <linux/hfs_fs_sb.h>
++#include <linux/adfs_fs_sb.h>
++#include <linux/qnx4_fs_sb.h>
++#include <linux/reiserfs_fs_sb.h>
++#include <linux/bfs_fs_sb.h>
++#include <linux/udf_fs_sb.h>
++#include <linux/ncp_fs_sb.h>
++#include <linux/usbdev_fs_sb.h>
++#include <linux/cramfs_fs_sb.h>
++#include <linux/jffs2_fs_sb.h>
++
++extern struct list_head super_blocks;
++extern spinlock_t sb_lock;
++
++#define sb_entry(list) list_entry((list), struct super_block, s_list)
++#define S_BIAS (1<<30)
++struct super_block {
++ struct list_head s_list; /* Keep this first */
++ kdev_t s_dev;
++ unsigned long s_blocksize;
++ unsigned char s_blocksize_bits;
++ unsigned char s_dirt;
++ unsigned long long s_maxbytes; /* Max file size */
++ struct file_system_type *s_type;
++ struct super_operations *s_op;
++ struct dquot_operations *dq_op;
++ struct quotactl_ops *s_qcop;
++ unsigned long s_flags;
++ unsigned long s_magic;
++ struct dentry *s_root;
++ struct rw_semaphore s_umount;
++ struct semaphore s_lock;
++ int s_count;
++ atomic_t s_active;
++
++ struct list_head s_dirty; /* dirty inodes */
++ struct list_head s_locked_inodes;/* inodes being synced */
++ struct list_head s_files;
++
++ struct block_device *s_bdev;
++ struct list_head s_instances;
++ struct quota_info s_dquot; /* Diskquota specific options */
++
++ union {
++ struct minix_sb_info minix_sb;
++ struct ext2_sb_info ext2_sb;
++ struct ext3_sb_info ext3_sb;
++ struct hpfs_sb_info hpfs_sb;
++ struct ntfs_sb_info ntfs_sb;
++ struct msdos_sb_info msdos_sb;
++ struct isofs_sb_info isofs_sb;
++ struct nfs_sb_info nfs_sb;
++ struct sysv_sb_info sysv_sb;
++ struct affs_sb_info affs_sb;
++ struct ufs_sb_info ufs_sb;
++ struct efs_sb_info efs_sb;
++ struct shmem_sb_info shmem_sb;
++ struct romfs_sb_info romfs_sb;
++ struct smb_sb_info smbfs_sb;
++ struct hfs_sb_info hfs_sb;
++ struct adfs_sb_info adfs_sb;
++ struct qnx4_sb_info qnx4_sb;
++ struct reiserfs_sb_info reiserfs_sb;
++ struct bfs_sb_info bfs_sb;
++ struct udf_sb_info udf_sb;
++ struct ncp_sb_info ncpfs_sb;
++ struct usbdev_sb_info usbdevfs_sb;
++ struct jffs2_sb_info jffs2_sb;
++ struct cramfs_sb_info cramfs_sb;
++ void *generic_sbp;
++ } u;
++ /*
++ * The next field is for VFS *only*. No filesystems have any business
++ * even looking at it. You had been warned.
++ */
++ struct semaphore s_vfs_rename_sem; /* Kludge */
++
++ /* The next field is used by knfsd when converting a (inode number based)
++ * file handle into a dentry. As it builds a path in the dcache tree from
++ * the bottom up, there may for a time be a subpath of dentrys which is not
++ * connected to the main tree. This semaphore ensure that there is only ever
++ * one such free path per filesystem. Note that unconnected files (or other
++ * non-directories) are allowed, but not unconnected diretories.
++ */
++ struct semaphore s_nfsd_free_path_sem;
++};
++
++/*
++ * VFS helper functions..
++ */
++extern int vfs_create(struct inode *, struct dentry *, int);
++extern int vfs_mkdir(struct inode *, struct dentry *, int);
++extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t);
++extern int vfs_symlink(struct inode *, struct dentry *, const char *);
++extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
++extern int vfs_rmdir(struct inode *, struct dentry *);
++extern int vfs_unlink(struct inode *, struct dentry *);
++int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
++ struct inode *new_dir, struct dentry *new_dentry,
++ struct lookup_intent *it);
++
++/*
++ * File types
++ */
++#define DT_UNKNOWN 0
++#define DT_FIFO 1
++#define DT_CHR 2
++#define DT_DIR 4
++#define DT_BLK 6
++#define DT_REG 8
++#define DT_LNK 10
++#define DT_SOCK 12
++#define DT_WHT 14
++
++/*
++ * This is the "filldir" function type, used by readdir() to let
++ * the kernel specify what kind of dirent layout it wants to have.
++ * This allows the kernel to read directories into kernel space or
++ * to have different dirent layouts depending on the binary type.
++ */
++typedef int (*filldir_t)(void *, const char *, int, loff_t, ino_t, unsigned);
++
++struct block_device_operations {
++ int (*open) (struct inode *, struct file *);
++ int (*release) (struct inode *, struct file *);
++ int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
++ int (*check_media_change) (kdev_t);
++ int (*revalidate) (kdev_t);
++ struct module *owner;
++};
++
++/*
++ * NOTE:
++ * read, write, poll, fsync, readv, writev can be called
++ * without the big kernel lock held in all filesystems.
++ */
++struct file_operations {
++ struct module *owner;
++ loff_t (*llseek) (struct file *, loff_t, int);
++ ssize_t (*read) (struct file *, char *, size_t, loff_t *);
++ ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
++ int (*readdir) (struct file *, void *, filldir_t);
++ unsigned int (*poll) (struct file *, struct poll_table_struct *);
++ int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
++ int (*mmap) (struct file *, struct vm_area_struct *);
++ int (*open) (struct inode *, struct file *);
++ int (*flush) (struct file *);
++ int (*release) (struct inode *, struct file *);
++ int (*fsync) (struct file *, struct dentry *, int datasync);
++ int (*fasync) (int, struct file *, int);
++ int (*lock) (struct file *, int, struct file_lock *);
++ ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
++ unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
++};
++
++struct inode_operations {
++ int (*create) (struct inode *,struct dentry *,int);
++ struct dentry * (*lookup) (struct inode *,struct dentry *);
++ struct dentry * (*lookup2) (struct inode *,struct dentry *, struct lookup_intent *);
++ int (*link) (struct dentry *,struct inode *,struct dentry *);
++ int (*link2) (struct inode *,struct inode *, const char *, int);
++ int (*unlink) (struct inode *,struct dentry *);
++ int (*unlink2) (struct inode *, const char *, int);
++ int (*symlink) (struct inode *,struct dentry *,const char *);
++ int (*symlink2) (struct inode *, const char *, int, const char *);
++ int (*mkdir) (struct inode *,struct dentry *,int);
++ int (*mkdir2) (struct inode *, const char *, int,int);
++ int (*rmdir) (struct inode *,struct dentry *);
++ int (*rmdir2) (struct inode *, const char *, int);
++ int (*mknod) (struct inode *,struct dentry *,int,int);
++ int (*mknod2) (struct inode *, const char *, int,int,int);
++ int (*rename) (struct inode *, struct dentry *,
++ struct inode *, struct dentry *);
++ int (*rename2) (struct inode *, struct inode *,
++ const char *oldname, int oldlen,
++ const char *newname, int newlen);
++ int (*readlink) (struct dentry *, char *,int);
++ int (*follow_link) (struct dentry *, struct nameidata *);
++ int (*follow_link2) (struct dentry *, struct nameidata *,
++ struct lookup_intent *it);
++ void (*truncate) (struct inode *);
++ int (*permission) (struct inode *, int);
++ int (*revalidate) (struct dentry *);
++ int (*setattr) (struct dentry *, struct iattr *);
++ int (*setattr_raw) (struct inode *, struct iattr *);
++ int (*getattr) (struct dentry *, struct iattr *);
++ int (*setxattr) (struct dentry *, const char *, void *, size_t, int);
++ ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
++ ssize_t (*listxattr) (struct dentry *, char *, size_t);
++ int (*removexattr) (struct dentry *, const char *);
++};
++
++struct seq_file;
++
++/*
++ * NOTE: write_inode, delete_inode, clear_inode, put_inode can be called
++ * without the big kernel lock held in all filesystems.
++ */
++struct super_operations {
++ struct inode *(*alloc_inode)(struct super_block *sb);
++ void (*destroy_inode)(struct inode *);
++
++ void (*read_inode) (struct inode *);
++
++ /* reiserfs kludge. reiserfs needs 64 bits of information to
++ ** find an inode. We are using the read_inode2 call to get
++ ** that information. We don't like this, and are waiting on some
++ ** VFS changes for the real solution.
++ ** iget4 calls read_inode2, iff it is defined
++ */
++ void (*read_inode2) (struct inode *, void *) ;
++ void (*dirty_inode) (struct inode *);
++ void (*write_inode) (struct inode *, int);
++ void (*put_inode) (struct inode *);
++ void (*delete_inode) (struct inode *);
++ void (*put_super) (struct super_block *);
++ void (*write_super) (struct super_block *);
++ void (*write_super_lockfs) (struct super_block *);
++ void (*unlockfs) (struct super_block *);
++ int (*statfs) (struct super_block *, struct statfs *);
++ int (*remount_fs) (struct super_block *, int *, char *);
++ void (*clear_inode) (struct inode *);
++ void (*umount_begin) (struct super_block *);
++
++ /* Following are for knfsd to interact with "interesting" filesystems
++ * Currently just reiserfs, but possibly FAT and others later
++ *
++ * fh_to_dentry is given a filehandle fragement with length, and a type flag
++ * and must return a dentry for the referenced object or, if "parent" is
++ * set, a dentry for the parent of the object.
++ * If a dentry cannot be found, a "root" dentry should be created and
++ * flaged as DCACHE_NFSD_DISCONNECTED. nfsd_iget is an example implementation.
++ *
++ * dentry_to_fh is given a dentry and must generate the filesys specific
++ * part of the file handle. Available length is passed in *lenp and used
++ * length should be returned therein.
++ * If need_parent is set, then dentry_to_fh should encode sufficient information
++ * to find the (current) parent.
++ * dentry_to_fh should return a 1byte "type" which will be passed back in
++ * the fhtype arguement to fh_to_dentry. Type of 0 is reserved.
++ * If filesystem was exportable before the introduction of fh_to_dentry,
++ * types 1 and 2 should be used is that same way as the generic code.
++ * Type 255 means error.
++ *
++ * Lengths are in units of 4bytes, not bytes.
++ */
++ struct dentry * (*fh_to_dentry)(struct super_block *sb, __u32 *fh, int len, int fhtype, int parent);
++ int (*dentry_to_fh)(struct dentry *, __u32 *fh, int *lenp, int need_parent);
++ int (*show_options)(struct seq_file *, struct vfsmount *);
++};
++
++/* Inode state bits.. */
++#define I_DIRTY_SYNC 1 /* Not dirty enough for O_DATASYNC */
++#define I_DIRTY_DATASYNC 2 /* Data-related inode changes pending */
++#define I_DIRTY_PAGES 4 /* Data-related inode changes pending */
++#define I_LOCK 8
++#define I_FREEING 16
++#define I_CLEAR 32
++#define I_NEW 64
++
++#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
++
++extern void __mark_inode_dirty(struct inode *, int);
++static inline void mark_inode_dirty(struct inode *inode)
++{
++ __mark_inode_dirty(inode, I_DIRTY);
++}
++
++static inline void mark_inode_dirty_sync(struct inode *inode)
++{
++ __mark_inode_dirty(inode, I_DIRTY_SYNC);
++}
++
++static inline void mark_inode_dirty_pages(struct inode *inode)
++{
++ __mark_inode_dirty(inode, I_DIRTY_PAGES);
++}
++
++struct file_system_type {
++ const char *name;
++ int fs_flags;
++ struct super_block *(*read_super) (struct super_block *, void *, int);
++ struct module *owner;
++ struct file_system_type * next;
++ struct list_head fs_supers;
++};
++
++#define DECLARE_FSTYPE(var,type,read,flags) \
++struct file_system_type var = { \
++ name: type, \
++ read_super: read, \
++ fs_flags: flags, \
++ owner: THIS_MODULE, \
++}
++
++#define DECLARE_FSTYPE_DEV(var,type,read) \
++ DECLARE_FSTYPE(var,type,read,FS_REQUIRES_DEV)
++
++/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
++#define fops_get(fops) \
++ (((fops) && (fops)->owner) \
++ ? ( try_inc_mod_count((fops)->owner) ? (fops) : NULL ) \
++ : (fops))
++
++#define fops_put(fops) \
++do { \
++ if ((fops) && (fops)->owner) \
++ __MOD_DEC_USE_COUNT((fops)->owner); \
++} while(0)
++
++extern int register_filesystem(struct file_system_type *);
++extern int unregister_filesystem(struct file_system_type *);
++extern struct vfsmount *kern_mount(struct file_system_type *);
++extern int may_umount(struct vfsmount *);
++extern long do_mount(char *, char *, char *, unsigned long, void *);
++struct vfsmount *do_kern_mount(const char *type, int flags, char *name, void *data);
++extern void umount_tree(struct vfsmount *);
++
++#define kern_umount mntput
++
++extern int vfs_statfs(struct super_block *, struct statfs *);
++
++/* Return value for VFS lock functions - tells locks.c to lock conventionally
++ * REALLY kosha for root NFS and nfs_lock
++ */
++#define LOCK_USE_CLNT 1
++
++#define FLOCK_VERIFY_READ 1
++#define FLOCK_VERIFY_WRITE 2
++
++extern int locks_mandatory_locked(struct inode *);
++extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
++
++/*
++ * Candidates for mandatory locking have the setgid bit set
++ * but no group execute bit - an otherwise meaningless combination.
++ */
++#define MANDATORY_LOCK(inode) \
++ (IS_MANDLOCK(inode) && ((inode)->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
++
++static inline int locks_verify_locked(struct inode *inode)
++{
++ if (MANDATORY_LOCK(inode))
++ return locks_mandatory_locked(inode);
++ return 0;
++}
++
++static inline int locks_verify_area(int read_write, struct inode *inode,
++ struct file *filp, loff_t offset,
++ size_t count)
++{
++ if (inode->i_flock && MANDATORY_LOCK(inode))
++ return locks_mandatory_area(read_write, inode, filp, offset, count);
++ return 0;
++}
++
++static inline int locks_verify_truncate(struct inode *inode,
++ struct file *filp,
++ loff_t size)
++{
++ if (inode->i_flock && MANDATORY_LOCK(inode))
++ return locks_mandatory_area(
++ FLOCK_VERIFY_WRITE, inode, filp,
++ size < inode->i_size ? size : inode->i_size,
++ (size < inode->i_size ? inode->i_size - size
++ : size - inode->i_size)
++ );
++ return 0;
++}
++
++static inline int get_lease(struct inode *inode, unsigned int mode)
++{
++ if (inode->i_flock && (inode->i_flock->fl_flags & FL_LEASE))
++ return __get_lease(inode, mode);
++ return 0;
++}
++
++/* fs/open.c */
++
++asmlinkage long sys_open(const char *, int, int);
++asmlinkage long sys_close(unsigned int); /* yes, it's really unsigned */
++extern int do_truncate(struct dentry *, loff_t start, int called_from_open);
++
++extern struct file *filp_open(const char *, int, int);
++extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
++extern int open_namei_it(const char *filename, int namei_flags, int mode,
++ struct nameidata *nd, struct lookup_intent *it);
++extern struct file *dentry_open_it(struct dentry *dentry, struct vfsmount *mnt,
++ int flags, struct lookup_intent *it);
++extern int filp_close(struct file *, fl_owner_t id);
++extern char * getname(const char *);
++
++/* fs/dcache.c */
++extern void vfs_caches_init(unsigned long);
++
++#define __getname() kmem_cache_alloc(names_cachep, SLAB_KERNEL)
++#define putname(name) kmem_cache_free(names_cachep, (void *)(name))
++
++enum {BDEV_FILE, BDEV_SWAP, BDEV_FS, BDEV_RAW};
++extern int register_blkdev(unsigned int, const char *, struct block_device_operations *);
++extern int unregister_blkdev(unsigned int, const char *);
++extern struct block_device *bdget(dev_t);
++extern int bd_acquire(struct inode *inode);
++extern void bd_forget(struct inode *inode);
++extern void bdput(struct block_device *);
++extern struct char_device *cdget(dev_t);
++extern void cdput(struct char_device *);
++extern int blkdev_open(struct inode *, struct file *);
++extern int blkdev_close(struct inode *, struct file *);
++extern struct file_operations def_blk_fops;
++extern struct address_space_operations def_blk_aops;
++extern struct file_operations def_fifo_fops;
++extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long);
++extern int blkdev_get(struct block_device *, mode_t, unsigned, int);
++extern int blkdev_put(struct block_device *, int);
++
++/* fs/devices.c */
++extern const struct block_device_operations *get_blkfops(unsigned int);
++extern int register_chrdev(unsigned int, const char *, struct file_operations *);
++extern int unregister_chrdev(unsigned int, const char *);
++extern int chrdev_open(struct inode *, struct file *);
++extern const char * bdevname(kdev_t);
++extern const char * cdevname(kdev_t);
++extern const char * kdevname(kdev_t);
++extern void init_special_inode(struct inode *, umode_t, int);
++
++/* Invalid inode operations -- fs/bad_inode.c */
++extern void make_bad_inode(struct inode *);
++extern int is_bad_inode(struct inode *);
++
++extern struct file_operations read_fifo_fops;
++extern struct file_operations write_fifo_fops;
++extern struct file_operations rdwr_fifo_fops;
++extern struct file_operations read_pipe_fops;
++extern struct file_operations write_pipe_fops;
++extern struct file_operations rdwr_pipe_fops;
++
++extern int fs_may_remount_ro(struct super_block *);
++
++extern int FASTCALL(try_to_free_buffers(struct page *, unsigned int));
++extern void refile_buffer(struct buffer_head * buf);
++extern void create_empty_buffers(struct page *, kdev_t, unsigned long);
++extern void end_buffer_io_sync(struct buffer_head *bh, int uptodate);
++
++/* reiserfs_writepage needs this */
++extern void set_buffer_async_io(struct buffer_head *bh) ;
++
++#define BUF_CLEAN 0
++#define BUF_LOCKED 1 /* Buffers scheduled for write */
++#define BUF_DIRTY 2 /* Dirty buffers, not yet scheduled for write */
++#define NR_LIST 3
++
++static inline void get_bh(struct buffer_head * bh)
++{
++ atomic_inc(&(bh)->b_count);
++}
++
++static inline void put_bh(struct buffer_head *bh)
++{
++ smp_mb__before_atomic_dec();
++ atomic_dec(&bh->b_count);
++}
++
++/*
++ * This is called by bh->b_end_io() handlers when I/O has completed.
++ */
++static inline void mark_buffer_uptodate(struct buffer_head * bh, int on)
++{
++ if (on)
++ set_bit(BH_Uptodate, &bh->b_state);
++ else
++ clear_bit(BH_Uptodate, &bh->b_state);
++}
++
++#define atomic_set_buffer_clean(bh) test_and_clear_bit(BH_Dirty, &(bh)->b_state)
++
++static inline void __mark_buffer_clean(struct buffer_head *bh)
++{
++ refile_buffer(bh);
++}
++
++static inline void mark_buffer_clean(struct buffer_head * bh)
++{
++ if (atomic_set_buffer_clean(bh))
++ __mark_buffer_clean(bh);
++}
++
++extern void FASTCALL(__mark_dirty(struct buffer_head *bh));
++extern void FASTCALL(__mark_buffer_dirty(struct buffer_head *bh));
++extern void FASTCALL(mark_buffer_dirty(struct buffer_head *bh));
++extern void FASTCALL(buffer_insert_inode_queue(struct buffer_head *, struct inode *));
++extern void FASTCALL(buffer_insert_inode_data_queue(struct buffer_head *, struct inode *));
++
++static inline int atomic_set_buffer_dirty(struct buffer_head *bh)
++{
++ return test_and_set_bit(BH_Dirty, &bh->b_state);
++}
++
++static inline void mark_buffer_async(struct buffer_head * bh, int on)
++{
++ if (on)
++ set_bit(BH_Async, &bh->b_state);
++ else
++ clear_bit(BH_Async, &bh->b_state);
++}
++
++/*
++ * If an error happens during the make_request, this function
++ * has to be recalled. It marks the buffer as clean and not
++ * uptodate, and it notifys the upper layer about the end
++ * of the I/O.
++ */
++static inline void buffer_IO_error(struct buffer_head * bh)
++{
++ mark_buffer_clean(bh);
++ /*
++ * b_end_io has to clear the BH_Uptodate bitflag in the error case!
++ */
++ bh->b_end_io(bh, 0);
++}
++
++static inline void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode)
++{
++ mark_buffer_dirty(bh);
++ buffer_insert_inode_queue(bh, inode);
++}
++
++extern void set_buffer_flushtime(struct buffer_head *);
++extern void balance_dirty(void);
++extern int check_disk_change(kdev_t);
+extern int invalidate_inodes(struct super_block *, int);
- extern int invalidate_device(kdev_t, int);
- extern void invalidate_inode_pages(struct inode *);
- extern void invalidate_inode_pages2(struct address_space *);
++extern int invalidate_device(kdev_t, int);
++extern void invalidate_inode_pages(struct inode *);
++extern void invalidate_inode_pages2(struct address_space *);
++extern void invalidate_inode_buffers(struct inode *);
++#define invalidate_buffers(dev) __invalidate_buffers((dev), 0)
++#define destroy_buffers(dev) __invalidate_buffers((dev), 1)
++extern void invalidate_bdev(struct block_device *, int);
++extern void __invalidate_buffers(kdev_t dev, int);
++extern void sync_inodes(kdev_t);
++extern void sync_unlocked_inodes(void);
++extern void write_inode_now(struct inode *, int);
++extern int sync_buffers(kdev_t, int);
++extern void sync_dev(kdev_t);
++extern int fsync_dev(kdev_t);
++extern int fsync_dev_lockfs(kdev_t);
++extern int fsync_super(struct super_block *);
++extern int fsync_no_super(kdev_t);
++extern void sync_inodes_sb(struct super_block *);
++extern int fsync_buffers_list(struct list_head *);
++static inline int fsync_inode_buffers(struct inode *inode)
++{
++ return fsync_buffers_list(&inode->i_dirty_buffers);
++}
++static inline int fsync_inode_data_buffers(struct inode *inode)
++{
++ return fsync_buffers_list(&inode->i_dirty_data_buffers);
++}
++extern int inode_has_buffers(struct inode *);
++extern int filemap_fdatasync(struct address_space *);
++extern int filemap_fdatawait(struct address_space *);
++extern void sync_supers(kdev_t);
++extern void sync_supers_lockfs(kdev_t);
++extern void unlockfs(kdev_t);
++extern int bmap(struct inode *, int);
++extern int notify_change(struct dentry *, struct iattr *);
++extern int permission(struct inode *, int);
++extern int vfs_permission(struct inode *, int);
++extern int get_write_access(struct inode *);
++extern int deny_write_access(struct file *);
++static inline void put_write_access(struct inode * inode)
++{
++ atomic_dec(&inode->i_writecount);
++}
++static inline void allow_write_access(struct file *file)
++{
++ if (file)
++ atomic_inc(&file->f_dentry->d_inode->i_writecount);
++}
++extern int do_pipe(int *);
++
++extern int open_namei(const char *, int, int, struct nameidata *);
++
++extern int kernel_read(struct file *, unsigned long, char *, unsigned long);
++extern struct file * open_exec(const char *);
++
++/* fs/dcache.c -- generic fs support functions */
++extern int is_subdir(struct dentry *, struct dentry *);
++extern ino_t find_inode_number(struct dentry *, struct qstr *);
++
++/*
++ * Kernel pointers have redundant information, so we can use a
++ * scheme where we can return either an error code or a dentry
++ * pointer with the same return value.
++ *
++ * This should be a per-architecture thing, to allow different
++ * error and pointer decisions.
++ */
++static inline void *ERR_PTR(long error)
++{
++ return (void *) error;
++}
++
++static inline long PTR_ERR(const void *ptr)
++{
++ return (long) ptr;
++}
++
++static inline long IS_ERR(const void *ptr)
++{
++ return (unsigned long)ptr > (unsigned long)-1000L;
++}
++
++/*
++ * The bitmask for a lookup event:
++ * - follow links at the end
++ * - require a directory
++ * - ending slashes ok even for nonexistent files
++ * - internal "there are more path compnents" flag
++ */
++#define LOOKUP_FOLLOW (1)
++#define LOOKUP_DIRECTORY (2)
++#define LOOKUP_CONTINUE (4)
++#define LOOKUP_POSITIVE (8)
++#define LOOKUP_PARENT (16)
++#define LOOKUP_NOALT (32)
++/*
++ * Type of the last component on LOOKUP_PARENT
++ */
++enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
++
++/*
++ * "descriptor" for what we're up to with a read for sendfile().
++ * This allows us to use the same read code yet
++ * have multiple different users of the data that
++ * we read from a file.
++ *
++ * The simplest case just copies the data to user
++ * mode.
++ */
++typedef struct {
++ size_t written;
++ size_t count;
++ char * buf;
++ int error;
++} read_descriptor_t;
++
++typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long);
++
++/* needed for stackable file system support */
++extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
++
++extern int FASTCALL(__user_walk(const char *, unsigned, struct nameidata *));
++extern int FASTCALL(__user_walk_it(const char *, unsigned, struct nameidata *, struct lookup_intent *it));
++extern int FASTCALL(path_init(const char *, unsigned, struct nameidata *));
++extern int FASTCALL(path_walk(const char *, struct nameidata *));
++extern int FASTCALL(link_path_walk(const char *, struct nameidata *));
++extern void path_release(struct nameidata *);
++extern int follow_down(struct vfsmount **, struct dentry **);
++extern int follow_up(struct vfsmount **, struct dentry **);
++extern struct dentry * lookup_one_len(const char *, struct dentry *, int);
++extern struct dentry * lookup_hash(struct qstr *, struct dentry *);
++#define user_path_walk(name,nd) __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd)
++#define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd)
++#define user_path_walk_it(name,nd,it) __user_walk_it(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd, it)
++#define user_path_walk_link_it(name,nd,it) __user_walk_it(name, LOOKUP_POSITIVE, nd, it)
++
++extern void inode_init_once(struct inode *);
++extern void iput(struct inode *);
++extern void force_delete(struct inode *);
++extern struct inode * igrab(struct inode *);
++extern ino_t iunique(struct super_block *, ino_t);
++extern void unlock_new_inode(struct inode *);
++
++typedef int (*find_inode_t)(struct inode *, unsigned long, void *);
++
++extern struct inode * iget4_locked(struct super_block *, unsigned long,
++ find_inode_t, void *);
++
++static inline struct inode *iget4(struct super_block *sb, unsigned long ino,
++ find_inode_t find_actor, void *opaque)
++{
++ struct inode *inode = iget4_locked(sb, ino, find_actor, opaque);
++
++ if (inode && (inode->i_state & I_NEW)) {
++ /*
++ * reiserfs-specific kludge that is expected to go away ASAP.
++ */
++ if (sb->s_op->read_inode2)
++ sb->s_op->read_inode2(inode, opaque);
++ else
++ sb->s_op->read_inode(inode);
++ unlock_new_inode(inode);
++ }
++
++ return inode;
++}
++
++static inline struct inode *iget(struct super_block *sb, unsigned long ino)
++{
++ struct inode *inode = iget4_locked(sb, ino, NULL, NULL);
++
++ if (inode && (inode->i_state & I_NEW)) {
++ sb->s_op->read_inode(inode);
++ unlock_new_inode(inode);
++ }
++
++ return inode;
++}
++
++static inline struct inode *iget_locked(struct super_block *sb, unsigned long ino)
++{
++ return iget4_locked(sb, ino, NULL, NULL);
++}
++
++extern void clear_inode(struct inode *);
++extern struct inode *new_inode(struct super_block *sb);
++extern void remove_suid(struct inode *inode);
++
++extern void insert_inode_hash(struct inode *);
++extern void remove_inode_hash(struct inode *);
++extern struct file * get_empty_filp(void);
++extern void file_move(struct file *f, struct list_head *list);
++extern struct buffer_head * get_hash_table(kdev_t, int, int);
++extern struct buffer_head * getblk(kdev_t, int, int);
++extern void ll_rw_block(int, int, struct buffer_head * bh[]);
++extern void submit_bh(int, struct buffer_head *);
++extern int is_read_only(kdev_t);
++extern void __brelse(struct buffer_head *);
++static inline void brelse(struct buffer_head *buf)
++{
++ if (buf)
++ __brelse(buf);
++}
++extern void __bforget(struct buffer_head *);
++static inline void bforget(struct buffer_head *buf)
++{
++ if (buf)
++ __bforget(buf);
++}
++extern int set_blocksize(kdev_t, int);
++extern int sb_set_blocksize(struct super_block *, int);
++extern int sb_min_blocksize(struct super_block *, int);
++extern struct buffer_head * bread(kdev_t, int, int);
++static inline struct buffer_head * sb_bread(struct super_block *sb, int block)
++{
++ return bread(sb->s_dev, block, sb->s_blocksize);
++}
++static inline struct buffer_head * sb_getblk(struct super_block *sb, int block)
++{
++ return getblk(sb->s_dev, block, sb->s_blocksize);
++}
++static inline struct buffer_head * sb_get_hash_table(struct super_block *sb, int block)
++{
++ return get_hash_table(sb->s_dev, block, sb->s_blocksize);
++}
++extern void wakeup_bdflush(void);
++extern void put_unused_buffer_head(struct buffer_head * bh);
++extern struct buffer_head * get_unused_buffer_head(int async);
++
++extern int brw_page(int, struct page *, kdev_t, int [], int);
++
++typedef int (get_block_t)(struct inode*,long,struct buffer_head*,int);
++
++/* Generic buffer handling for block filesystems.. */
++extern int try_to_release_page(struct page * page, int gfp_mask);
++extern int discard_bh_page(struct page *, unsigned long, int);
++#define block_flushpage(page, offset) discard_bh_page(page, offset, 1)
++#define block_invalidate_page(page) discard_bh_page(page, 0, 0)
++extern int block_symlink(struct inode *, const char *, int);
++extern int block_write_full_page(struct page*, get_block_t*);
++extern int block_read_full_page(struct page*, get_block_t*);
++extern int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
++extern int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
++ unsigned long *);
++extern int generic_cont_expand(struct inode *inode, loff_t size) ;
++extern int block_commit_write(struct page *page, unsigned from, unsigned to);
++extern int block_sync_page(struct page *);
++
++int generic_block_bmap(struct address_space *, long, get_block_t *);
++int generic_commit_write(struct file *, struct page *, unsigned, unsigned);
++int block_truncate_page(struct address_space *, loff_t, get_block_t *);
++extern int generic_direct_IO(int, struct inode *, struct kiobuf *, unsigned long, int, get_block_t *);
++extern int waitfor_one_page(struct page *);
++extern int writeout_one_page(struct page *);
++
++extern int generic_file_mmap(struct file *, struct vm_area_struct *);
++extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
++extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
++extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *);
++extern ssize_t generic_file_write_nolock(struct file *, const char *, size_t, loff_t *);
++extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, read_actor_t);
++extern loff_t no_llseek(struct file *file, loff_t offset, int origin);
++extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin);
++extern ssize_t generic_read_dir(struct file *, char *, size_t, loff_t *);
++extern int generic_file_open(struct inode * inode, struct file * filp);
++
++extern struct file_operations generic_ro_fops;
++
++extern int vfs_readlink(struct dentry *, char *, int, const char *);
++extern int vfs_follow_link(struct nameidata *, const char *);
++extern int vfs_follow_link_it(struct nameidata *, const char *,
++ struct lookup_intent *it);
++extern int page_readlink(struct dentry *, char *, int);
++extern int page_follow_link(struct dentry *, struct nameidata *);
++extern struct inode_operations page_symlink_inode_operations;
++
++extern int vfs_readdir(struct file *, filldir_t, void *);
++extern int dcache_dir_open(struct inode *, struct file *);
++extern int dcache_dir_close(struct inode *, struct file *);
++extern loff_t dcache_dir_lseek(struct file *, loff_t, int);
++extern int dcache_dir_fsync(struct file *, struct dentry *, int);
++extern int dcache_readdir(struct file *, void *, filldir_t);
++extern struct file_operations dcache_dir_ops;
++
++extern struct file_system_type *get_fs_type(const char *name);
++extern struct super_block *get_super(kdev_t);
++extern void drop_super(struct super_block *sb);
++static inline int is_mounted(kdev_t dev)
++{
++ struct super_block *sb = get_super(dev);
++ if (sb) {
++ drop_super(sb);
++ return 1;
++ }
++ return 0;
++}
++unsigned long generate_cluster(kdev_t, int b[], int);
++unsigned long generate_cluster_swab32(kdev_t, int b[], int);
++extern kdev_t ROOT_DEV;
++extern char root_device_name[];
++
++
++extern void show_buffers(void);
++
++#ifdef CONFIG_BLK_DEV_INITRD
++extern unsigned int real_root_dev;
++#endif
++
++extern ssize_t char_read(struct file *, char *, size_t, loff_t *);
++extern ssize_t block_read(struct file *, char *, size_t, loff_t *);
++extern int read_ahead[];
++
++extern ssize_t char_write(struct file *, const char *, size_t, loff_t *);
++extern ssize_t block_write(struct file *, const char *, size_t, loff_t *);
++
++extern int file_fsync(struct file *, struct dentry *, int);
++extern int generic_buffer_fdatasync(struct inode *inode, unsigned long start_idx, unsigned long end_idx);
++extern int generic_osync_inode(struct inode *, int);
++#define OSYNC_METADATA (1<<0)
++#define OSYNC_DATA (1<<1)
++#define OSYNC_INODE (1<<2)
++
++extern int inode_change_ok(struct inode *, struct iattr *);
++extern int inode_setattr(struct inode *, struct iattr *);
++
++/*
++ * Common dentry functions for inclusion in the VFS
++ * or in other stackable file systems. Some of these
++ * functions were in linux/fs/ C (VFS) files.
++ *
++ */
++
++/*
++ * Locking the parent is needed to:
++ * - serialize directory operations
++ * - make sure the parent doesn't change from
++ * under us in the middle of an operation.
++ *
++ * NOTE! Right now we'd rather use a "struct inode"
++ * for this, but as I expect things to move toward
++ * using dentries instead for most things it is
++ * probably better to start with the conceptually
++ * better interface of relying on a path of dentries.
++ */
++static inline struct dentry *lock_parent(struct dentry *dentry)
++{
++ struct dentry *dir = dget(dentry->d_parent);
++
++ down(&dir->d_inode->i_sem);
++ return dir;
++}
++
++static inline struct dentry *get_parent(struct dentry *dentry)
++{
++ return dget(dentry->d_parent);
++}
++
++static inline void unlock_dir(struct dentry *dir)
++{
++ up(&dir->d_inode->i_sem);
++ dput(dir);
++}
++
++/*
++ * Whee.. Deadlock country. Happily there are only two VFS
++ * operations that does this..
++ */
++static inline void double_down(struct semaphore *s1, struct semaphore *s2)
++{
++ if (s1 != s2) {
++ if ((unsigned long) s1 < (unsigned long) s2) {
++ struct semaphore *tmp = s2;
++ s2 = s1; s1 = tmp;
++ }
++ down(s1);
++ }
++ down(s2);
++}
++
++/*
++ * Ewwwwwwww... _triple_ lock. We are guaranteed that the 3rd argument is
++ * not equal to 1st and not equal to 2nd - the first case (target is parent of
++ * source) would be already caught, the second is plain impossible (target is
++ * its own parent and that case would be caught even earlier). Very messy.
++ * I _think_ that it works, but no warranties - please, look it through.
++ * Pox on bloody lusers who mandated overwriting rename() for directories...
++ */
++
++static inline void triple_down(struct semaphore *s1,
++ struct semaphore *s2,
++ struct semaphore *s3)
++{
++ if (s1 != s2) {
++ if ((unsigned long) s1 < (unsigned long) s2) {
++ if ((unsigned long) s1 < (unsigned long) s3) {
++ struct semaphore *tmp = s3;
++ s3 = s1; s1 = tmp;
++ }
++ if ((unsigned long) s1 < (unsigned long) s2) {
++ struct semaphore *tmp = s2;
++ s2 = s1; s1 = tmp;
++ }
++ } else {
++ if ((unsigned long) s1 < (unsigned long) s3) {
++ struct semaphore *tmp = s3;
++ s3 = s1; s1 = tmp;
++ }
++ if ((unsigned long) s2 < (unsigned long) s3) {
++ struct semaphore *tmp = s3;
++ s3 = s2; s2 = tmp;
++ }
++ }
++ down(s1);
++ } else if ((unsigned long) s2 < (unsigned long) s3) {
++ struct semaphore *tmp = s3;
++ s3 = s2; s2 = tmp;
++ }
++ down(s2);
++ down(s3);
++}
++
++static inline void double_up(struct semaphore *s1, struct semaphore *s2)
++{
++ up(s1);
++ if (s1 != s2)
++ up(s2);
++}
++
++static inline void triple_up(struct semaphore *s1,
++ struct semaphore *s2,
++ struct semaphore *s3)
++{
++ up(s1);
++ if (s1 != s2)
++ up(s2);
++ up(s3);
++}
++
++static inline void double_lock(struct dentry *d1, struct dentry *d2)
++{
++ double_down(&d1->d_inode->i_sem, &d2->d_inode->i_sem);
++}
++
++static inline void double_unlock(struct dentry *d1, struct dentry *d2)
++{
++ double_up(&d1->d_inode->i_sem,&d2->d_inode->i_sem);
++ dput(d1);
++ dput(d2);
++}
++
++#endif /* __KERNEL__ */
++
++#endif /* _LINUX_FS_H */
+--- linux-2.4.19-hp3_pnnl1/fs/exec.c~invalidate_show 2003-04-15 13:17:46.000000000 +0800
++++ linux-2.4.19-hp3_pnnl1-root/fs/exec.c 2003-04-15 13:22:26.000000000 +0800
+@@ -1058,7 +1058,7 @@ int do_coredump(long signr, struct pt_re
+ goto close_fail;
+ if (!file->f_op->write)
+ goto close_fail;
+- if (do_truncate(file->f_dentry, 0) != 0)
++ if (do_truncate(file->f_dentry, 0, 0) != 0)
+ goto close_fail;
+
+ retval = binfmt->core_dump(signr, regs, file);
_