- 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(-)
+ fs/inode.c | 21 ++++++++++++++-------
+ fs/smbfs/inode.c | 2 +-
+ fs/super.c | 4 ++--
+ include/linux/fs.h | 2 +-
+ 4 files changed, 18 insertions(+), 11 deletions(-)
---- /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.
-+ */
+--- linux-rh-2.4.20-8/fs/inode.c~invalidate_show 2003-04-11 14:04:56.000000000 +0800
++++ linux-rh-2.4.20-8-root/fs/inode.c 2003-04-16 20:59:35.000000000 +0800
+@@ -604,7 +604,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)
+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;
-+
-+ 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;
-+ }
+ {
+ struct list_head *next;
+ int busy = 0, count = 0;
+@@ -629,6 +630,11 @@ static int invalidate_list(struct list_h
+ 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 */
-+ 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
+ busy = 1;
+ }
+ /* only unused inodes may be cached with i_count zero */
+@@ -647,22 +653,23 @@ static int invalidate_list(struct list_h
+ /**
+ * 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.
-+ */
-+
+ *
+ * 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 invalidate_inodes(struct super_block * sb, int show)
-+{
-+ int busy;
-+ LIST_HEAD(throw_away);
-+
-+ spin_lock(&inode_lock);
+ {
+ 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);
+ 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);
-+
-+ 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);
+ spin_unlock(&inode_lock);
+
+ dispose_list(&throw_away);
+@@ -688,7 +695,7 @@ int invalidate_device(kdev_t dev, int do
+ * hold).
+ */
+ shrink_dcache_sb(sb);
+- res = invalidate_inodes(sb);
+ res = invalidate_inodes(sb, 0);
-+ 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;
+ drop_super(sb);
+ }
+ invalidate_buffers(dev);
+--- linux-rh-2.4.20-8/fs/super.c~invalidate_show 2003-04-11 14:04:57.000000000 +0800
++++ linux-rh-2.4.20-8-root/fs/super.c 2003-04-16 20:59:35.000000000 +0800
+@@ -943,7 +943,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() */
+ invalidate_inodes(sb, 0); /* bad name - it should be evict_inodes() */
-+ 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 (sop) {
+ if (sop->write_super && sb->s_dirt)
+ sop->write_super(sb);
+@@ -952,7 +952,7 @@ void kill_super(struct super_block *sb)
+ }
+
+ /* Forget any remaining inodes */
+- if (invalidate_inodes(sb)) {
+ if (invalidate_inodes(sb, 1)) {
-+ 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);
+ printk(KERN_ERR "VFS: Busy inodes after unmount. "
+ "Self-destruct in 5 seconds. Have a nice day...\n");
+ }
+--- linux-rh-2.4.20-8/include/linux/fs.h~invalidate_show 2003-04-16 20:55:35.000000000 +0800
++++ linux-rh-2.4.20-8-root/include/linux/fs.h 2003-04-16 20:59:35.000000000 +0800
+@@ -1283,7 +1283,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 *);
+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 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;
+ extern int invalidate_device(kdev_t, int);
+ extern void invalidate_inode_pages(struct inode *);
+ extern void invalidate_inode_pages2(struct address_space *);
+--- linux-rh-2.4.20-8/fs/smbfs/inode.c~invalidate_show 2003-04-16 20:59:48.000000000 +0800
++++ linux-rh-2.4.20-8-root/fs/smbfs/inode.c 2003-04-16 21:00:43.000000000 +0800
+@@ -167,7 +167,7 @@ smb_invalidate_inodes(struct smb_sb_info
+ {
+ VERBOSE("\n");
+ shrink_dcache_sb(SB_of(server));
+- invalidate_inodes(SB_of(server));
++ invalidate_inodes(SB_of(server), 0);
+ }
- retval = binfmt->core_dump(signr, regs, file);
+ /*
_