patch-2.4.10 linux/fs/devfs/base.c

Next file: linux/fs/devfs/util.c
Previous file: linux/fs/dcache.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.9/linux/fs/devfs/base.c linux/fs/devfs/base.c
@@ -388,7 +388,7 @@
 	       Work sponsored by SGI.
   v0.83
     19991107   Richard Gooch <rgooch@atnf.csiro.au>
-	       Added DEVFS_ FL_WAIT flag.
+	       Added DEVFS_FL_WAIT flag.
 	       Work sponsored by SGI.
   v0.84
     19991107   Richard Gooch <rgooch@atnf.csiro.au>
@@ -511,6 +511,40 @@
 	       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
 */
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -533,21 +567,20 @@
 #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/segment.h>
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 
-#define DEVFS_VERSION            "0.107 (20010709)"
+#define DEVFS_VERSION            "0.116 (20010919)"
 
 #define DEVFS_NAME "devfs"
 
-#define INODE_TABLE_INC 250
 #define FIRST_INODE 1
 
 #define STRING_LENGTH 256
@@ -557,7 +590,7 @@
 #  define FALSE 0
 #endif
 
-#define IS_HIDDEN(de) (( ((de)->hide && !is_devfsd_or_child(fs_info)) || (!(de)->registered&& !(de)->show_unreg)))
+#define IS_HIDDEN(de) (( ((de)->hide && !is_devfsd_or_child(fs_info)) || !(de)->registered))
 
 #define DEBUG_NONE         0x00000
 #define DEBUG_MODULE_LOAD  0x00001
@@ -567,8 +600,8 @@
 #define DEBUG_S_PUT        0x00010
 #define DEBUG_I_LOOKUP     0x00020
 #define DEBUG_I_CREATE     0x00040
-#define DEBUG_I_READ       0x00080
-#define DEBUG_I_WRITE      0x00100
+#define DEBUG_I_GET        0x00080
+#define DEBUG_I_CHANGE     0x00100
 #define DEBUG_I_UNLINK     0x00200
 #define DEBUG_I_RLINK      0x00400
 #define DEBUG_I_FLINK      0x00800
@@ -577,16 +610,12 @@
 #define DEBUG_D_DELETE     0x04000
 #define DEBUG_D_RELEASE    0x08000
 #define DEBUG_D_IPUT       0x10000
-#define DEBUG_ALL          (DEBUG_MODULE_LOAD | DEBUG_REGISTER | \
-			    DEBUG_SET_FLAGS | DEBUG_I_LOOKUP |   \
-			    DEBUG_I_UNLINK | DEBUG_I_MKNOD |     \
-			    DEBUG_D_RELEASE | DEBUG_D_IPUT)
+#define DEBUG_ALL          0xfffff
 #define DEBUG_DISABLED     DEBUG_NONE
 
 #define OPTION_NONE             0x00
-#define OPTION_SHOW             0x01
-#define OPTION_MOUNT            0x02
-#define OPTION_ONLY             0x04
+#define OPTION_MOUNT            0x01
+#define OPTION_ONLY             0x02
 
 #define OOPS(format, args...) {printk (format, ## args); \
                                printk ("Forcing Oops\n"); \
@@ -630,8 +659,10 @@
 
 struct symlink_type
 {
-    unsigned int length;  /*  Not including the NULL-termimator  */
-    char *linkname;       /*  This is NULL-terminated            */
+    atomic_t refcount;           /*  When this drops to zero, it's unused    */
+    rwlock_t lock;               /*  Lock around the registered flag         */
+    unsigned int length;         /*  Not including the NULL-termimator       */
+    char *linkname;              /*  This is NULL-terminated                 */
 };
 
 struct fifo_type
@@ -650,7 +681,6 @@
     umode_t mode;
     uid_t uid;
     gid_t gid;
-    nlink_t nlink;
 };
 
 struct devfs_entry
@@ -672,7 +702,6 @@
     umode_t mode;
     unsigned short namelen;  /*  I think 64k+ filenames are a way off...  */
     unsigned char registered:1;
-    unsigned char show_unreg:1;
     unsigned char hide:1;
     unsigned char no_persistence:1;
     char name[1];            /*  This is just a dummy: the allocated array is
@@ -693,9 +722,6 @@
 
 struct fs_info      /*  This structure is for the mounted devfs  */
 {
-    unsigned int num_inodes;    /*  Number of inodes created         */
-    unsigned int table_size;    /*  Size of the inode pointer table  */
-    struct devfs_entry **table;
     struct super_block *sb;
     volatile struct devfsd_buf_entry *devfsd_buffer;
     spinlock_t devfsd_buffer_lock;
@@ -765,7 +791,7 @@
 						    unsigned int namelen,
 						    int traverse_symlink)
 {
-    struct devfs_entry *curr;
+    struct devfs_entry *curr, *retval;
 
     if ( !S_ISDIR (parent->mode) )
     {
@@ -781,48 +807,41 @@
     if (curr == NULL) return NULL;
     if (!S_ISLNK (curr->mode) || !traverse_symlink) return curr;
     /*  Need to follow the link: this is a stack chomper  */
-    return search_for_entry (parent,
-			     curr->u.symlink.linkname, curr->u.symlink.length,
-			     FALSE, FALSE, NULL, TRUE);
+    read_lock (&curr->u.symlink.lock);
+    if (!curr->registered)
+    {
+	read_unlock (&curr->u.symlink.lock);
+	return NULL;
+    }
+    atomic_inc (&curr->u.symlink.refcount);
+    read_unlock (&curr->u.symlink.lock);
+    retval = search_for_entry (parent, curr->u.symlink.linkname,
+			       curr->u.symlink.length, FALSE, FALSE, NULL,
+			       TRUE);
+    if ( atomic_dec_and_test (&curr->u.symlink.refcount) )
+	kfree (curr->u.symlink.linkname);
+    return retval;
 }   /*  End Function search_for_entry_in_dir  */
 
 static struct devfs_entry *create_entry (struct devfs_entry *parent,
 					 const char *name,unsigned int namelen)
 {
-    struct devfs_entry *new, **table;
+    struct devfs_entry *new;
+    static unsigned long inode_counter = FIRST_INODE;
+    static spinlock_t counter_lock = SPIN_LOCK_UNLOCKED;
 
-    /*  First ensure table size is enough  */
-    if (fs_info.num_inodes >= fs_info.table_size)
-    {
-	if ( ( table = kmalloc (sizeof *table *
-				(fs_info.table_size + INODE_TABLE_INC),
-				GFP_KERNEL) ) == NULL ) return NULL;
-	fs_info.table_size += INODE_TABLE_INC;
-#ifdef CONFIG_DEVFS_DEBUG
-	if (devfs_debug & DEBUG_I_CREATE)
-	    printk ("%s: create_entry(): grew inode table to: %u entries\n",
-		    DEVFS_NAME, fs_info.table_size);
-#endif
-	if (fs_info.table)
-	{
-	    memcpy (table, fs_info.table, sizeof *table *fs_info.num_inodes);
-	    kfree (fs_info.table);
-	}
-	fs_info.table = table;
-    }
     if ( name && (namelen < 1) ) namelen = strlen (name);
     if ( ( new = kmalloc (sizeof *new + namelen, GFP_KERNEL) ) == NULL )
 	return NULL;
     /*  Magic: this will set the ctime to zero, thus subsequent lookups will
 	trigger the call to <update_devfs_inode_from_entry>  */
     memset (new, 0, sizeof *new + namelen);
+    spin_lock (&counter_lock);
+    new->inode.ino = inode_counter++;
+    spin_unlock (&counter_lock);
     new->parent = parent;
     if (name) memcpy (new->name, name, namelen);
     new->namelen = namelen;
-    new->inode.ino = fs_info.num_inodes + FIRST_INODE;
-    new->inode.nlink = 1;
-    fs_info.table[fs_info.num_inodes] = new;
-    ++fs_info.num_inodes;
     if (parent == NULL) return new;
     new->prev = parent->u.dir.last;
     /*  Insert into the parent directory's list of children  */
@@ -1083,14 +1102,10 @@
 							   int do_check)
 {
     struct devfs_entry *de;
-    struct fs_info *fs_info;
 
     if (inode == NULL) return NULL;
-    if (inode->i_ino < FIRST_INODE) return NULL;
-    fs_info = inode->i_sb->u.generic_sbp;
-    if (fs_info == NULL) return NULL;
-    if (inode->i_ino - FIRST_INODE >= fs_info->num_inodes) return NULL;
-    de = fs_info->table[inode->i_ino - FIRST_INODE];
+    de = inode->u.generic_ip;
+    if (!de) printk (__FUNCTION__ "(): NULL de for inode %ld\n", inode->i_ino);
     if (do_check && de && !de->registered) de = NULL;
     return de;
 }   /*  End Function get_devfs_entry_from_vfs_inode  */
@@ -1342,12 +1357,12 @@
 	    return NULL;
 	}
     }
-    de->u.fcb.autogen = 0;
+    de->u.fcb.autogen = FALSE;
     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 = (devnum == NODEV) ? 0 : 1;
+	de->u.fcb.autogen = (devnum == NODEV) ? FALSE : TRUE;
     }
     else if ( S_ISREG (mode) ) de->u.fcb.u.file.size = 0;
     else
@@ -1377,8 +1392,6 @@
 	++de->parent->u.dir.num_removable;
     }
     de->u.fcb.open = FALSE;
-    de->show_unreg = ( (boot_options & OPTION_SHOW)
-			|| (flags & DEVFS_FL_SHOW_UNREG) ) ? TRUE : FALSE;
     de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
     de->no_persistence = (flags & DEVFS_FL_NO_PERSISTENCE) ? TRUE : FALSE;
     de->registered = TRUE;
@@ -1418,13 +1431,16 @@
 				   MKDEV (de->u.fcb.u.device.major,
 					  de->u.fcb.u.device.minor) );
 	}
-	de->u.fcb.autogen = 0;
+	de->u.fcb.autogen = FALSE;
 	return;
     }
     if (S_ISLNK (de->mode) && de->registered)
     {
+	write_lock (&de->u.symlink.lock);
 	de->registered = FALSE;
-	kfree (de->u.symlink.linkname);
+	write_unlock (&de->u.symlink.lock);
+	if ( atomic_dec_and_test (&de->u.symlink.refcount) )
+	    kfree (de->u.symlink.linkname);
 	return;
     }
     if ( S_ISFIFO (de->mode) )
@@ -1475,7 +1491,7 @@
 {
     int is_new;
     unsigned int linklength;
-    char *newname;
+    char *newlink;
     struct devfs_entry *de;
 
     if (handle != NULL) *handle = NULL;
@@ -1494,49 +1510,32 @@
 	return -EINVAL;
     }
     linklength = strlen (link);
-    de = search_for_entry (dir, name, strlen (name), TRUE, TRUE, &is_new,
-			   FALSE);
-    if (de == NULL) return -ENOMEM;
-    if (!S_ISLNK (de->mode) && de->registered)
+    if ( ( newlink = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL )
+	return -ENOMEM;
+    memcpy (newlink, link, linklength);
+    newlink[linklength] = '\0';
+    if ( ( de = search_for_entry (dir, name, strlen (name), TRUE, TRUE,
+				  &is_new, FALSE) ) == NULL )
     {
-	printk ("%s: devfs_do_symlink(): non-link entry already exists\n",
-		DEVFS_NAME);
+	kfree (newlink);
+	return -ENOMEM;
+    }
+    if (de->registered)
+    {
+	kfree (newlink);
+	printk ("%s: devfs_do_symlink(%s): entry already exists\n",
+		DEVFS_NAME, name);
 	return -EEXIST;
     }
-    if (handle != NULL) *handle = de;
     de->mode = S_IFLNK | S_IRUGO | S_IXUGO;
     de->info = info;
-    de->show_unreg = ( (boot_options & OPTION_SHOW)
-		       || (flags & DEVFS_FL_SHOW_UNREG) ) ? TRUE : FALSE;
     de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
-    /*  Note there is no need to fiddle the dentry cache if the symlink changes
-	as the symlink follow method is called every time it's needed  */
-    if ( de->registered && (linklength == de->u.symlink.length) )
-    {
-	/*  New link is same length as old link  */
-	if (memcmp (link, de->u.symlink.linkname, linklength) == 0) return 0;
-	return -EEXIST;  /*  Contents would change  */
-    }
-    /*  Have to create/update  */
-    if (de->registered) return -EEXIST;
-    if ( ( newname = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL )
-    {
-	struct devfs_entry *parent = de->parent;
-
-	if (!is_new) return -ENOMEM;
-	/*  Have to clean up  */
-	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;
-	kfree (de);
-	return -ENOMEM;
-    }
-    de->u.symlink.linkname = newname;
-    memcpy (de->u.symlink.linkname, link, linklength);
-    de->u.symlink.linkname[linklength] = '\0';
+    de->u.symlink.linkname = newlink;
     de->u.symlink.length = linklength;
+    atomic_set (&de->u.symlink.refcount, 1);
+    rwlock_init (&de->u.symlink.lock);
     de->registered = TRUE;
+    if (handle != NULL) *handle = de;
     return 0;
 }   /*  End Function devfs_do_symlink  */
 
@@ -1621,7 +1620,6 @@
     de->mode = S_IFDIR | S_IRUGO | S_IXUGO;
     de->info = info;
     if (!de->registered) de->u.dir.num_removable = 0;
-    de->show_unreg = (boot_options & OPTION_SHOW) ? TRUE : FALSE;
     de->hide = FALSE;
     de->registered = TRUE;
     return de;
@@ -1674,7 +1672,6 @@
 
     if (de == NULL) return -EINVAL;
     if (!de->registered) return -ENODEV;
-    if (de->show_unreg) fl |= DEVFS_FL_SHOW_UNREG;
     if (de->hide) fl |= DEVFS_FL_HIDE;
     if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
     {
@@ -1704,7 +1701,6 @@
 	printk ("%s: devfs_set_flags(): de->name: \"%s\"\n",
 		DEVFS_NAME, de->name);
 #endif
-    de->show_unreg = (flags & DEVFS_FL_SHOW_UNREG) ? TRUE : FALSE;
     de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
     if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
     {
@@ -2053,16 +2049,16 @@
 	{"dmod",      DEBUG_MODULE_LOAD,  &devfs_debug_init},
 	{"dreg",      DEBUG_REGISTER,     &devfs_debug_init},
 	{"dunreg",    DEBUG_UNREGISTER,   &devfs_debug_init},
-	{"diread",    DEBUG_I_READ,       &devfs_debug_init},
+	{"diget",     DEBUG_I_GET,        &devfs_debug_init},
 	{"dchange",   DEBUG_SET_FLAGS,    &devfs_debug_init},
-	{"diwrite",   DEBUG_I_WRITE,      &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  */
-	{"show",      OPTION_SHOW,        &boot_options},
 	{"only",      OPTION_ONLY,        &boot_options},
 	{"mount",     OPTION_MOUNT,       &boot_options},
+	{NULL,        0,                  NULL}
     };
 
     while ( (*str != '\0') && !isspace (*str) )
@@ -2074,7 +2070,7 @@
 	    invert = 1;
 	    str += 2;
 	}
-	for (i = 0; i < sizeof (devfs_options_tab); i++)
+	for (i = 0; devfs_options_tab[i].name != NULL; i++)
 	{
 	    int len = strlen (devfs_options_tab[i].name);
 
@@ -2247,121 +2243,37 @@
 static struct file_operations devfs_dir_fops;
 static struct inode_operations devfs_symlink_iops;
 
-static void devfs_read_inode (struct inode *inode)
-{
-    struct devfs_entry *de;
-
-    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
-    if (de == NULL)
-    {
-	printk ("%s: read_inode(%d): VFS inode: %p  NO devfs_entry\n",
-		DEVFS_NAME, (int) inode->i_ino, inode);
-	return;
-    }
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_READ)
-	printk ("%s: read_inode(%d): VFS inode: %p  devfs_entry: %p\n",
-		DEVFS_NAME, (int) inode->i_ino, inode, de);
-#endif
-    inode->i_blocks = 0;
-    inode->i_blksize = 1024;
-    inode->i_op = &devfs_iops;
-    inode->i_fop = &devfs_fops;
-    inode->i_rdev = NODEV;
-    if ( S_ISCHR (de->inode.mode) )
-    {
-	inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
-			       de->u.fcb.u.device.minor);
-	inode->i_cdev = cdget (kdev_t_to_nr(inode->i_rdev));
-    }
-    else if ( S_ISBLK (de->inode.mode) )
-    {
-	inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
-			       de->u.fcb.u.device.minor);
-	inode->i_bdev = bdget (kdev_t_to_nr(inode->i_rdev));
-	if (inode->i_bdev)
-	{
-	    if (!inode->i_bdev->bd_op && de->u.fcb.ops)
-		inode->i_bdev->bd_op = de->u.fcb.ops;
-	}
-	else printk ("%s: read_inode(%d): no block device from bdget()\n",
-		     DEVFS_NAME, (int) inode->i_ino);
-    }
-    else if ( S_ISFIFO (de->inode.mode) ) inode->i_fop = &def_fifo_fops;
-    else if ( S_ISREG (de->inode.mode) ) inode->i_size = de->u.fcb.u.file.size;
-    else if ( S_ISDIR (de->inode.mode) )
-    {
-	inode->i_op = &devfs_dir_iops;
-    	inode->i_fop = &devfs_dir_fops;
-    }
-    else if ( S_ISLNK (de->inode.mode) )
-    {
-	inode->i_op = &devfs_symlink_iops;
-	inode->i_size = de->u.symlink.length;
-    }
-    inode->i_mode = de->inode.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;
-    inode->i_nlink = de->inode.nlink;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_READ)
-	printk ("%s:   mode: 0%o  uid: %d  gid: %d\n",
-		DEVFS_NAME, (int) inode->i_mode,
-		(int) inode->i_uid, (int) inode->i_gid);
-#endif
-}   /*  End Function devfs_read_inode  */
-
-static void devfs_write_inode (struct inode *inode, int wait)
+static int devfs_notify_change (struct dentry *dentry, struct iattr *iattr)
 {
-    int index;
+    int retval;
     struct devfs_entry *de;
+    struct inode *inode = dentry->d_inode;
     struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
 
-    if (inode->i_ino < FIRST_INODE) return;
-    index = inode->i_ino - FIRST_INODE;
-    lock_kernel ();
-    if (index >= fs_info->num_inodes)
-    {
-	printk ("%s: writing inode: %lu for which there is no entry!\n",
-		DEVFS_NAME, inode->i_ino);
-        unlock_kernel ();
-	return;
-    }
-    de = fs_info->table[index];
+    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
+    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;
 #ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_WRITE)
+    if (devfs_debug & DEBUG_I_CHANGE)
     {
-	printk ("%s: write_inode(%d): VFS inode: %p  devfs_entry: %p\n",
+	printk ("%s: notify_change(%d): VFS inode: %p  devfs_entry: %p\n",
 		DEVFS_NAME, (int) inode->i_ino, inode, de);
 	printk ("%s:   mode: 0%o  uid: %d  gid: %d\n",
 		DEVFS_NAME, (int) inode->i_mode,
 		(int) inode->i_uid, (int) inode->i_gid);
     }
 #endif
+    /*  Inode is not on hash chains, thus must save permissions here rather
+	than in a write_inode() method  */
     de->inode.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;
-    unlock_kernel ();
-}   /*  End Function devfs_write_inode  */
-
-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, TRUE);
-    if (de == NULL) return -ENODEV;
-    retval = inode_change_ok (inode, iattr);
-    if (retval != 0) return retval;
-    inode_setattr (inode, iattr);
     if ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) )
 	devfsd_notify_one (de, DEVFSD_NOTIFY_CHANGE, inode->i_mode,
 			   inode->i_uid, inode->i_gid, fs_info);
@@ -2379,10 +2291,16 @@
     return 0;
 }   /*  End Function devfs_statfs  */
 
+static void devfs_clear_inode(struct inode *inode)
+{
+	if (S_ISBLK(inode->i_mode))
+		bdput(inode->i_bdev);
+}
+
 static struct super_operations devfs_sops =
 { 
-    read_inode:    devfs_read_inode,
-    write_inode:   devfs_write_inode,
+    put_inode:     force_delete,
+    clear_inode:   devfs_clear_inode,
     statfs:        devfs_statfs,
 };
 
@@ -2411,8 +2329,67 @@
 	printk ("  old inode: %p\n", de->inode.dentry->d_inode);
 	return NULL;
     }
-    if ( ( inode = iget (sb, de->inode.ino) ) == NULL ) return NULL;
+    if ( ( inode = new_inode (sb) ) == NULL )
+    {
+	printk ("%s: get_vfs_inode(%s): new_inode() failed, de: %p\n",
+		DEVFS_NAME, de->name, de);
+	return NULL;
+    }
     de->inode.dentry = dentry;
+    inode->u.generic_ip = de;
+    inode->i_ino = de->inode.ino;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_GET)
+	printk ("%s: get_vfs_inode(%d): VFS inode: %p  devfs_entry: %p\n",
+		DEVFS_NAME, (int) inode->i_ino, inode, de);
+#endif
+    inode->i_blocks = 0;
+    inode->i_blksize = 1024;
+    inode->i_op = &devfs_iops;
+    inode->i_fop = &devfs_fops;
+    inode->i_rdev = NODEV;
+    if ( S_ISCHR (de->inode.mode) )
+    {
+	inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
+			       de->u.fcb.u.device.minor);
+	inode->i_cdev = cdget (kdev_t_to_nr(inode->i_rdev));
+    }
+    else if ( S_ISBLK (de->inode.mode) )
+    {
+	inode->i_rdev = MKDEV (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 ("%s: get_vfs_inode(%d): no block device from bdget()\n",
+		     DEVFS_NAME, (int) inode->i_ino);
+    }
+    else if ( S_ISFIFO (de->inode.mode) ) inode->i_fop = &def_fifo_fops;
+    else if ( S_ISREG (de->inode.mode) ) inode->i_size = de->u.fcb.u.file.size;
+    else if ( S_ISDIR (de->inode.mode) )
+    {
+	inode->i_op = &devfs_dir_iops;
+    	inode->i_fop = &devfs_dir_fops;
+    }
+    else if ( S_ISLNK (de->inode.mode) )
+    {
+	inode->i_op = &devfs_symlink_iops;
+	inode->i_size = de->u.symlink.length;
+    }
+    inode->i_mode = de->inode.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;
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_GET)
+	printk ("%s:   mode: 0%o  uid: %d  gid: %d\n",
+		DEVFS_NAME, (int) inode->i_mode,
+		(int) inode->i_uid, (int) inode->i_gid);
+#endif
     return inode;
 }   /*  End Function get_vfs_inode  */
 
@@ -2725,14 +2702,14 @@
     memcpy (txt, dentry->d_name.name,
 	    (dentry->d_name.len >= STRING_LENGTH) ?
 	    (STRING_LENGTH - 1) : dentry->d_name.len);
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_LOOKUP)
-	printk ("%s: lookup(%s): dentry: %p by: \"%s\"\n",
-		DEVFS_NAME, txt, dentry, current->comm);
-#endif
     fs_info = dir->i_sb->u.generic_sbp;
     /*  First try to get the devfs entry for this directory  */
     parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
+#ifdef CONFIG_DEVFS_DEBUG
+    if (devfs_debug & DEBUG_I_LOOKUP)
+	printk ("%s: lookup(%s): dentry: %p parent: %p by: \"%s\"\n",
+		DEVFS_NAME, txt, dentry, parent, current->comm);
+#endif
     if (parent == NULL) return ERR_PTR (-ENOENT);
     /*  Try to reclaim an existing devfs entry  */
     de = search_for_entry_in_dir (parent,
@@ -2823,6 +2800,7 @@
 static int devfs_unlink (struct inode *dir, struct dentry *dentry)
 {
     struct devfs_entry *de;
+    struct inode *inode = dentry->d_inode;
 
 #ifdef CONFIG_DEVFS_DEBUG
     char txt[STRING_LENGTH];
@@ -2838,9 +2816,18 @@
 
     de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE);
     if (de == NULL) return -ENOENT;
-    de->registered = FALSE;
+    devfsd_notify_one (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
+		       inode->i_uid, inode->i_gid, dir->i_sb->u.generic_sbp);
+    if ( S_ISLNK (de->mode) )
+    {
+	write_lock (&de->u.symlink.lock);
+	de->registered = FALSE;
+	write_unlock (&de->u.symlink.lock);
+	if ( atomic_dec_and_test (&de->u.symlink.refcount) )
+	    kfree (de->u.symlink.linkname);
+    }
+    else de->registered = FALSE;
     de->hide = TRUE;
-    if ( S_ISLNK (de->mode) ) kfree (de->u.symlink.linkname);
     free_dentries (de);
     return 0;
 }   /*  End Function devfs_unlink  */
@@ -2954,6 +2941,8 @@
 	}
     }
     if (has_children) return -ENOTEMPTY;
+    devfsd_notify_one (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
+		       inode->i_uid, inode->i_gid, fs_info);
     de->hide = TRUE;
     de->registered = FALSE;
     free_dentries (de);
@@ -2989,29 +2978,29 @@
     de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len,
 			   FALSE, TRUE, &is_new, FALSE);
     if (de == NULL) return -ENOMEM;
-    if (!de->registered)
+    if (de->registered)
     {
-	/*  Since we created the devfs entry we get to choose things  */
-	de->info = NULL;
-	de->mode = mode;
-	if ( S_ISBLK (mode) || S_ISCHR (mode) )
-	{
-	    de->u.fcb.u.device.major = MAJOR (rdev);
-	    de->u.fcb.u.device.minor = MINOR (rdev);
-	    de->u.fcb.default_uid = current->euid;
-	    de->u.fcb.default_gid = current->egid;
-	    de->u.fcb.ops = NULL;
-	    de->u.fcb.auto_owner = FALSE;
-	    de->u.fcb.aopen_notify = FALSE;
-	    de->u.fcb.open = FALSE;
-	}
-	else if ( S_ISFIFO (mode) )
-	{
-	    de->u.fifo.uid = current->euid;
-	    de->u.fifo.gid = current->egid;
-	}
+	printk ("%s: mknod(): existing entry\n", DEVFS_NAME);
+	return -EEXIST;
+    }
+    de->info = NULL;
+    de->mode = mode;
+    if ( S_ISBLK (mode) || S_ISCHR (mode) )
+    {
+	de->u.fcb.u.device.major = MAJOR (rdev);
+	de->u.fcb.u.device.minor = MINOR (rdev);
+	de->u.fcb.default_uid = current->euid;
+	de->u.fcb.default_gid = current->egid;
+	de->u.fcb.ops = NULL;
+	de->u.fcb.auto_owner = FALSE;
+	de->u.fcb.aopen_notify = FALSE;
+	de->u.fcb.open = FALSE;
+    }
+    else if ( S_ISFIFO (mode) )
+    {
+	de->u.fifo.uid = current->euid;
+	de->u.fifo.gid = current->egid;
     }
-    de->show_unreg = FALSE;
     de->hide = FALSE;
     de->inode.mode = mode;
     de->inode.uid = current->euid;
@@ -3038,11 +3027,19 @@
     int err;
     struct devfs_entry *de;
 
-    lock_kernel ();
     de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE);
-    err = de ? vfs_readlink (dentry, buffer, buflen,
-			     de->u.symlink.linkname) : -ENODEV;
-    unlock_kernel ();
+    if (!de) return -ENODEV;
+    read_lock (&de->u.symlink.lock);
+    if (!de->registered)
+    {
+	read_unlock (&de->u.symlink.lock);
+	return -ENODEV;
+    }
+    atomic_inc (&de->u.symlink.refcount);
+    read_unlock (&de->u.symlink.lock);
+    err = vfs_readlink (dentry, buffer, buflen, de->u.symlink.linkname);
+    if ( atomic_dec_and_test (&de->u.symlink.refcount) )
+	kfree (de->u.symlink.linkname);
     return err;
 }   /*  End Function devfs_readlink  */
 
@@ -3051,10 +3048,19 @@
     int err;
     struct devfs_entry *de;
 
-    lock_kernel ();
     de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE);
-    err = de ? vfs_follow_link (nd, de->u.symlink.linkname) : -ENODEV;
-    unlock_kernel ();
+    if (!de) return -ENODEV;
+    read_lock (&de->u.symlink.lock);
+    if (!de->registered)
+    {
+	read_unlock (&de->u.symlink.lock);
+	return -ENODEV;
+    }
+    atomic_inc (&de->u.symlink.refcount);
+    read_unlock (&de->u.symlink.lock);
+    err = vfs_follow_link (nd, de->u.symlink.linkname);
+    if ( atomic_dec_and_test (&de->u.symlink.refcount) )
+	kfree (de->u.symlink.linkname);
     return err;
 }   /*  End Function devfs_follow_link  */
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)