patch-2.4.6 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.5/linux/fs/devfs/base.c linux/fs/devfs/base.c
@@ -1,6 +1,6 @@
 /*  devfs (Device FileSystem) driver.
 
-    Copyright (C) 1998-2000  Richard Gooch
+    Copyright (C) 1998-2001  Richard Gooch
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
@@ -481,6 +481,32 @@
 	       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
 */
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -515,7 +541,7 @@
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 
-#define DEVFS_VERSION            "0.102 (20000622)"
+#define DEVFS_VERSION            "0.106 (20010617)"
 
 #define DEVFS_NAME "devfs"
 
@@ -560,7 +586,7 @@
 
 #define OPTION_NONE             0x00
 #define OPTION_SHOW             0x01
-#define OPTION_NOMOUNT          0x02
+#define OPTION_MOUNT            0x02
 #define OPTION_ONLY             0x04
 
 #define OOPS(format, args...) {printk (format, ## args); \
@@ -665,26 +691,27 @@
     gid_t gid;
 };
 
-struct fs_info      /*  This structure is for each mounted devfs  */
+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;
     volatile unsigned int devfsd_buf_in;
     volatile unsigned int devfsd_buf_out;
     volatile int devfsd_sleeping;
-    volatile int devfsd_buffer_in_use;
     volatile struct task_struct *devfsd_task;
     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;
     wait_queue_head_t revalidate_wait_queue;
 };
 
-static struct fs_info fs_info;
+static struct fs_info fs_info = {devfsd_buffer_lock: SPIN_LOCK_UNLOCKED};
 static unsigned int next_devnum_char = MIN_DEVNUM;
 static unsigned int next_devnum_block = MIN_DEVNUM;
 static const int devfsd_buf_size = PAGE_SIZE / sizeof(struct devfsd_buf_entry);
@@ -694,9 +721,9 @@
 #endif
 
 #ifdef CONFIG_DEVFS_MOUNT
-static unsigned int boot_options = OPTION_NONE;
+static unsigned int boot_options = OPTION_MOUNT;
 #else
-static unsigned int boot_options = OPTION_NOMOUNT;
+static unsigned int boot_options = OPTION_NONE;
 #endif
 
 /*  Forward function declarations  */
@@ -851,14 +878,13 @@
     /*  Always ensure the root is created  */
     if (root_entry != NULL) return root_entry;
     if ( ( root_entry = create_entry (NULL, NULL, 0) ) == NULL ) return NULL;
-    root_entry->registered = TRUE;
     root_entry->mode = S_IFDIR;
     /*  Force an inode update, because lookup() is never done for the root  */
     update_devfs_inode_from_entry (root_entry);
+    root_entry->registered = TRUE;
     /*  And create the entry for ".devfsd"  */
     if ( ( new = create_entry (root_entry, ".devfsd", 0) ) == NULL )
 	return NULL;
-    new->registered = TRUE;
     new->u.fcb.u.device.major = next_devnum_char >> 8;
     new->u.fcb.u.device.minor = next_devnum_char & 0xff;
     ++next_devnum_char;
@@ -866,6 +892,7 @@
     new->u.fcb.default_uid = 0;
     new->u.fcb.default_gid = 0;
     new->u.fcb.ops = &devfsd_fops;
+    new->registered = TRUE;
     return root_entry;
 }   /*  End Function get_root_entry  */
 
@@ -943,8 +970,8 @@
 	    return NULL;
 	}
 	/*  Ensure an unregistered entry is re-registered and visible  */
-	entry->registered = TRUE;
 	entry->hide = FALSE;
+	entry->registered = TRUE;
 	subname = ptr + 1;
 	dir = entry;
     }
@@ -1053,8 +1080,10 @@
     return find_by_dev (root_entry, major, minor, type);
 }   /*  End Function find_entry  */
 
-static struct devfs_entry *get_devfs_entry_from_vfs_inode (struct inode *inode)
+static struct devfs_entry *get_devfs_entry_from_vfs_inode (struct inode *inode,
+							   int do_check)
 {
+    struct devfs_entry *de;
     struct fs_info *fs_info;
 
     if (inode == NULL) return NULL;
@@ -1062,7 +1091,9 @@
     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;
-    return fs_info->table[inode->i_ino - FIRST_INODE];
+    de = fs_info->table[inode->i_ino - FIRST_INODE];
+    if (do_check && de && !de->registered) de = NULL;
+    return de;
 }   /*  End Function get_devfs_entry_from_vfs_inode  */
 
 
@@ -1075,19 +1106,19 @@
 {
     struct dentry *dentry;
 
-    spin_lock(&dcache_lock);
+    spin_lock (&dcache_lock);
     dentry = de->inode.dentry;
     if (dentry != NULL)
     {
 	dget_locked (dentry);
 	de->inode.dentry = NULL;
-	spin_unlock(&dcache_lock);
+	spin_unlock (&dcache_lock);
 	/*  Forcefully remove the inode  */
 	if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0;
 	d_drop (dentry);
 	dput (dentry);
-    } else
-	spin_unlock(&dcache_lock);
+    }
+    else spin_unlock (&dcache_lock);
 }   /*  End Function free_dentries  */
 
 
@@ -1140,7 +1171,7 @@
     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();
+	if (fs_info->devfsd_task) schedule ();
     remove_wait_queue (&fs_info->revalidate_wait_queue, &wait);
     current->state = TASK_RUNNING;
     return (TRUE);
@@ -1165,7 +1196,6 @@
     unsigned int next_pos;
     unsigned long flags;
     struct devfsd_buf_entry *entry;
-    static spinlock_t lock = SPIN_LOCK_UNLOCKED;
 
     if ( !( fs_info->devfsd_event_mask & (1 << type) ) ) return (FALSE);
     next_pos = fs_info->devfsd_buf_in + 1;
@@ -1176,8 +1206,7 @@
 	atomic_inc (&fs_info->devfsd_overrun_count);
 	return (FALSE);
     }
-    spin_lock_irqsave (&lock, flags);
-    fs_info->devfsd_buffer_in_use = TRUE;
+    spin_lock_irqsave (&fs_info->devfsd_buffer_lock, flags);
     next_pos = fs_info->devfsd_buf_in + 1;
     if (next_pos >= devfsd_buf_size) next_pos = 0;
     entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer +
@@ -1188,8 +1217,7 @@
     entry->uid = uid;
     entry->gid = gid;
     fs_info->devfsd_buf_in = next_pos;
-    fs_info->devfsd_buffer_in_use = FALSE;
-    spin_unlock_irqrestore (&lock, flags);
+    spin_unlock_irqrestore (&fs_info->devfsd_buffer_lock, flags);
     wake_up_interruptible (&fs_info->devfsd_wait_queue);
     return (TRUE);
 }   /*  End Function devfsd_notify_one  */
@@ -1322,7 +1350,6 @@
 	    return NULL;
 	}
     }
-    de->registered = TRUE;
     if ( S_ISCHR (mode) || S_ISBLK (mode) )
     {
 	de->u.fcb.u.device.major = major;
@@ -1347,7 +1374,6 @@
 	de->u.fcb.default_uid = 0;
 	de->u.fcb.default_gid = 0;
     }
-    de->registered = TRUE;
     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;
@@ -1361,6 +1387,7 @@
 			|| (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;
     devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
     return de;
 }   /*  End Function devfs_register  */
@@ -1392,11 +1419,10 @@
 	de->u.fcb.ops = NULL;
 	return;
     }
-    if ( S_ISLNK (de->mode) )
+    if (S_ISLNK (de->mode) && de->registered)
     {
 	de->registered = FALSE;
-	if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname);
-	de->u.symlink.linkname = NULL;
+	kfree (de->u.symlink.linkname);
 	return;
     }
     if ( S_ISFIFO (de->mode) )
@@ -1441,22 +1467,9 @@
     unregister (de);
 }   /*  End Function devfs_unregister  */
 
-
-/**
- *	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)
+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 is_new;
     unsigned int linklength;
@@ -1466,16 +1479,16 @@
     if (handle != NULL) *handle = NULL;
     if (name == NULL)
     {
-	printk ("%s: devfs_mk_symlink(): NULL name pointer\n", DEVFS_NAME);
+	printk ("%s: devfs_do_symlink(): NULL name pointer\n", DEVFS_NAME);
 	return -EINVAL;
     }
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_REGISTER)
-	printk ("%s: devfs_mk_symlink(%s)\n", DEVFS_NAME, name);
+	printk ("%s: devfs_do_symlink(%s)\n", DEVFS_NAME, name);
 #endif
     if (link == NULL)
     {
-	printk ("%s: devfs_mk_symlink(): NULL link pointer\n", DEVFS_NAME);
+	printk ("%s: devfs_do_symlink(): NULL link pointer\n", DEVFS_NAME);
 	return -EINVAL;
     }
     linklength = strlen (link);
@@ -1484,7 +1497,7 @@
     if (de == NULL) return -ENOMEM;
     if (!S_ISLNK (de->mode) && de->registered)
     {
-	printk ("%s: devfs_mk_symlink(): non-link entry already exists\n",
+	printk ("%s: devfs_do_symlink(): non-link entry already exists\n",
 		DEVFS_NAME);
 	return -EEXIST;
     }
@@ -1504,7 +1517,6 @@
     }
     /*  Have to create/update  */
     if (de->registered) return -EEXIST;
-    de->registered = TRUE;
     if ( ( newname = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL )
     {
 	struct devfs_entry *parent = de->parent;
@@ -1518,11 +1530,39 @@
 	kfree (de);
 	return -ENOMEM;
     }
-    if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname);
     de->u.symlink.linkname = newname;
     memcpy (de->u.symlink.linkname, link, linklength);
     de->u.symlink.linkname[linklength] = '\0';
     de->u.symlink.length = linklength;
+    de->registered = TRUE;
+    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;
+    err = devfs_do_symlink (dir, name, flags, link, &de, info);
+    if (err) return err;
+    if (handle != NULL) *handle = de;
+    devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
     return 0;
 }   /*  End Function devfs_mk_symlink  */
 
@@ -1579,9 +1619,9 @@
     de->mode = S_IFDIR | S_IRUGO | S_IXUGO;
     de->info = info;
     if (!de->registered) de->u.dir.num_removable = 0;
-    de->registered = TRUE;
     de->show_unreg = (boot_options & OPTION_SHOW) ? TRUE : FALSE;
     de->hide = FALSE;
+    de->registered = TRUE;
     return de;
 }   /*  End Function devfs_mk_dir  */
 
@@ -1716,7 +1756,7 @@
 {
     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);
+    return get_devfs_entry_from_vfs_inode (inode, TRUE);
 }   /*  End Function devfs_get_handle_from_inode  */
 
 
@@ -1737,10 +1777,10 @@
 
     if (de == NULL) return -EINVAL;
     if (de->namelen >= buflen) return -ENAMETOOLONG; /*  Must be first       */
-    if (de->parent == NULL) return buflen;           /*  Don't prepend root  */
+    path[buflen - 1] = '\0';
+    if (de->parent == NULL) return buflen - 1;       /*  Don't prepend root  */
     pos = buflen - de->namelen - 1;
     memcpy (path + pos, de->name, de->namelen);
-    path[buflen - 1] = '\0';
     for (de = de->parent; de->parent != NULL; de = de->parent)
     {
 	if (pos - de->namelen - 1 < 0) return -ENAMETOOLONG;
@@ -1999,84 +2039,56 @@
 
 static int __init devfs_setup (char *str)
 {
-    while ( (*str != '\0') && !isspace (*str) )
+    static struct
     {
-#ifdef CONFIG_DEVFS_DEBUG
-	if (strncmp (str, "dall", 4) == 0)
-	{
-	    devfs_debug_init |= DEBUG_ALL;
-	    str += 4;
-	}
-	else if (strncmp (str, "dmod", 4) == 0)
-	{
-	    devfs_debug_init |= DEBUG_MODULE_LOAD;
-	    str += 4;
-	}
-	else if (strncmp (str, "dreg", 4) == 0)
-	{
-	    devfs_debug_init |= DEBUG_REGISTER;
-	    str += 4;
-	}
-	else if (strncmp (str, "dunreg", 6) == 0)
-	{
-	    devfs_debug_init |= DEBUG_UNREGISTER;
-	    str += 6;
-	}
-	else if (strncmp (str, "diread", 6) == 0)
-	{
-	    devfs_debug_init |= DEBUG_I_READ;
-	    str += 6;
-	}
-	else if (strncmp (str, "dchange", 7) == 0)
-	{
-	    devfs_debug_init |= DEBUG_SET_FLAGS;
-	    str += 7;
-	}
-	else if (strncmp (str, "diwrite", 7) == 0)
-	{
-	    devfs_debug_init |= DEBUG_I_WRITE;
-	    str += 7;
-	}
-	else if (strncmp (str, "dimknod", 7) == 0)
-	{
-	    devfs_debug_init |= DEBUG_I_MKNOD;
-	    str += 7;
-	}
-	else if (strncmp (str, "dilookup", 8) == 0)
-	{
-	    devfs_debug_init |= DEBUG_I_LOOKUP;
-	    str += 8;
-	}
-	else if (strncmp (str, "diunlink", 8) == 0)
-	{
-	    devfs_debug_init |= DEBUG_I_UNLINK;
-	    str += 8;
-	}
-	else
+	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},
+	{"diread",    DEBUG_I_READ,       &devfs_debug_init},
+	{"dchange",   DEBUG_SET_FLAGS,    &devfs_debug_init},
+	{"diwrite",   DEBUG_I_WRITE,      &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  */
-	if (strncmp (str, "show", 4) == 0)
-	{
-	    boot_options |= OPTION_SHOW;
-	    str += 4;
-	}
-	else if (strncmp (str, "only", 4) == 0)
-	{
-	    boot_options |= OPTION_ONLY;
-	    str += 4;
-	}
-	else if (strncmp (str, "mount", 5) == 0)
+	{"show",      OPTION_SHOW,        &boot_options},
+	{"only",      OPTION_ONLY,        &boot_options},
+	{"mount",     OPTION_MOUNT,       &boot_options},
+    };
+
+    while ( (*str != '\0') && !isspace (*str) )
+    {
+	int i, found = 0, invert = 0;
+
+	if (strncmp (str, "no", 2) == 0)
 	{
-	    boot_options &= ~OPTION_NOMOUNT;
-	    str += 5;
+	    invert = 1;
+	    str += 2;
 	}
-	else if (strncmp (str, "nomount", 7) == 0)
+	for (i = 0; i < sizeof (devfs_options_tab); i++)
 	{
-	    boot_options |= OPTION_NOMOUNT;
-	    str += 7;
+	    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;
+	    }
 	}
-	else
-	return 0;
-	if (*str != ',') return 0;
+	if (!found) return 0;       /*  No match         */
+	if (*str != ',') return 0;  /*  No more options  */
 	++str;
     }
     return 1;
@@ -2103,6 +2115,7 @@
 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);
@@ -2125,15 +2138,15 @@
 			const char *name, unsigned namelen,
 			char buf[STRING_LENGTH])
 {
-    int pos;
+    int pos = STRING_LENGTH - namelen - 1;
 
     if ( !( fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP) ) )
 	return -ENOENT;
     if ( is_devfsd_or_child (fs_info) ) return -ENOENT;
     if (namelen >= STRING_LENGTH) return -ENAMETOOLONG;
-    memcpy (buf + STRING_LENGTH - namelen - 1, name, namelen);
+    memcpy (buf + pos, name, namelen);
     buf[STRING_LENGTH - 1] = '\0';
-    pos = devfs_generate_path (parent, buf, STRING_LENGTH - namelen - 1);
+    if (parent->parent != NULL) pos = devfs_generate_path (parent, buf, pos);
     if (pos < 0) return pos;
     buf[STRING_LENGTH - namelen - 2] = '/';
     if ( !devfsd_notify_one (buf + pos, DEVFSD_NOTIFY_LOOKUP, 0,
@@ -2229,13 +2242,14 @@
 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 void devfs_read_inode (struct inode *inode)
 {
     struct devfs_entry *de;
 
-    de = get_devfs_entry_from_vfs_inode (inode);
+    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
     if (de == NULL)
     {
 	printk ("%s: read_inode(%d): VFS inode: %p  NO devfs_entry\n",
@@ -2273,7 +2287,11 @@
     }
     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;
+    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;
@@ -2302,12 +2320,12 @@
 
     if (inode->i_ino < FIRST_INODE) return;
     index = inode->i_ino - FIRST_INODE;
-    lock_kernel();
+    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();
+        unlock_kernel ();
 	return;
     }
     de = fs_info->table[index];
@@ -2327,7 +2345,7 @@
     de->inode.atime = inode->i_atime;
     de->inode.mtime = inode->i_mtime;
     de->inode.ctime = inode->i_ctime;
-    unlock_kernel();
+    unlock_kernel ();
 }   /*  End Function devfs_write_inode  */
 
 static int devfs_notify_change (struct dentry *dentry, struct iattr *iattr)
@@ -2337,7 +2355,7 @@
     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);
+    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;
@@ -2399,12 +2417,6 @@
 
 /*  File operations for device entries follow  */
 
-static ssize_t devfs_read (struct file *file, char *buf, size_t len, loff_t *ppos)
-{
-    if ( S_ISDIR (file->f_dentry->d_inode->i_mode) ) return -EISDIR;
-    return -EINVAL;
-}   /*  End Function devfs_read  */
-
 static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir)
 {
     int err, count;
@@ -2413,13 +2425,8 @@
     struct devfs_entry *parent, *de;
     struct inode *inode = file->f_dentry->d_inode;
 
-    if ( !S_ISDIR (inode->i_mode) )
-    {
-	printk ("%s: readdir(): inode is not a directory\n", DEVFS_NAME);
-	return -ENOTDIR;
-    }
     fs_info = inode->i_sb->u.generic_sbp;
-    parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode);
+    parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode, TRUE);
     if ( (long) file->f_pos < 0 ) return -EINVAL;
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_F_READDIR)
@@ -2473,8 +2480,8 @@
     struct devfs_entry *de;
     struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
 
-    lock_kernel();
-    de = get_devfs_entry_from_vfs_inode (inode);
+    lock_kernel ();
+    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
     err = -ENODEV;
     if (de == NULL)
 	goto out;
@@ -2482,34 +2489,18 @@
     if ( S_ISDIR (de->mode) )
 	goto out;
     df = &de->u.fcb;
-    err = -ENODEV;
-    if (!de->registered)
-	goto out;
     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;
     }
-    else file->f_op = fops_get((struct file_operations*)df->ops);
+    else file->f_op = fops_get ( (struct file_operations*) df->ops );
     if (file->f_op)
 	err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0;
     else
     {
 	/*  Fallback to legacy scheme  */
-	/*
-	 * Do we need it? Richard, could you verify it?
-	 * It can legitimately happen if
-	 *	it is a character device and 
-	 *	df->ops == NULL and
-	 *	de->registered is true,
-	 * but AFAICS it can't happen - in devfs_register() we never set
-	 * ->ops to NULL, in unregister() we set ->registered to false,
-	 * in devfs_mknod() we set it to NULL only if ->register is false.
-	 *
-	 * Looks like this fallback is not needed at all.
-	 *							AV
-	 */
 	if ( S_ISCHR (inode->i_mode) ) err = chrdev_open (inode, file);
 	else err = -ENODEV;
     }
@@ -2532,13 +2523,18 @@
 	devfsd_notify_one (de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode,
 			   current->euid, current->egid, fs_info);
 out:
-    unlock_kernel();
+    unlock_kernel ();
     return err;
 }   /*  End Function devfs_open  */
 
 static struct file_operations devfs_fops =
 {
-    read: devfs_read,
+    open: devfs_open,
+};
+
+static struct file_operations devfs_dir_fops =
+{
+    read: generic_read_dir,
     readdir: devfs_readdir,
     open: devfs_open,
 };
@@ -2574,7 +2570,7 @@
     struct devfs_entry *de;
 
     lock_kernel ();
-    de = get_devfs_entry_from_vfs_inode (inode);
+    de = get_devfs_entry_from_vfs_inode (inode, FALSE);
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_D_IPUT)
 	printk ("%s: d_iput(): dentry: %p inode: %p de: %p  de->dentry: %p\n",
@@ -2627,7 +2623,7 @@
 	return 1;
     }
     fs_info = inode->i_sb->u.generic_sbp;
-    de = get_devfs_entry_from_vfs_inode (inode);
+    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_D_DELETE)
 	printk ("%s: d_delete(): dentry: %p  inode: %p  devfs_entry: %p\n",
@@ -2682,7 +2678,7 @@
 	    {
 		devfs_handle_t parent;
 
-		parent = get_devfs_entry_from_vfs_inode (dir);
+		parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
 		de = search_for_entry_in_dir (parent, dentry->d_name.name,
 					      dentry->d_name.len, FALSE);
 	    }
@@ -2723,12 +2719,6 @@
     /*  Set up the dentry operations before anything else, to ensure cleaning
 	up on any error  */
     dentry->d_op = &devfs_dops;
-    if (dir == NULL)
-    {
-	printk ("%s: lookup(): NULL directory inode\n", DEVFS_NAME);
-	return ERR_PTR (-ENOTDIR);
-    }
-    if ( !S_ISDIR (dir->i_mode) ) return ERR_PTR (-ENOTDIR);
     memset (txt, 0, STRING_LENGTH);
     memcpy (txt, dentry->d_name.name,
 	    (dentry->d_name.len >= STRING_LENGTH) ?
@@ -2740,9 +2730,8 @@
 #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);
-    if (parent == NULL) return ERR_PTR (-EINVAL);
-    if (!parent->registered) return ERR_PTR (-ENOENT);
+    parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
+    if (parent == NULL) return ERR_PTR (-ENOENT);
     /*  Try to reclaim an existing devfs entry  */
     de = search_for_entry_in_dir (parent,
 				  dentry->d_name.name, dentry->d_name.len,
@@ -2845,13 +2834,11 @@
     }
 #endif
 
-    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
-    if (!dentry || !dentry->d_inode) return -ENOENT;
-    de = get_devfs_entry_from_vfs_inode (dentry->d_inode);
+    de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE);
     if (de == NULL) return -ENOENT;
-    if (!de->registered) return -ENOENT;
     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  */
@@ -2864,17 +2851,15 @@
     struct devfs_entry *parent, *de;
     struct inode *inode;
 
-    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
     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);
-    if (parent == NULL) return -EINVAL;
-    if (!parent->registered) return -ENOENT;
-    err = devfs_mk_symlink (parent, dentry->d_name.name, DEVFS_FL_NONE,
+    parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
+    if (parent == NULL) return -ENOENT;
+    err = devfs_do_symlink (parent, dentry->d_name.name, DEVFS_FL_NONE,
 			    symname, &de, NULL);
 #ifdef CONFIG_DEVFS_DEBUG
     if (devfs_debug & DEBUG_DISABLED)
-	printk ("%s: symlink(): errcode from <devfs_mk_symlink>: %d\n",
+	printk ("%s: symlink(): errcode from <devfs_do_symlink>: %d\n",
 		DEVFS_NAME, err);
 #endif
     if (err < 0) return err;
@@ -2904,13 +2889,10 @@
     struct inode *inode;
 
     mode = (mode & ~S_IFMT) | S_IFDIR;
-    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
     fs_info = dir->i_sb->u.generic_sbp;
-    /*  We are allowed to create the directory  */
     /*  First try to get the devfs entry for this directory  */
-    parent = get_devfs_entry_from_vfs_inode (dir);
-    if (parent == NULL) return -EINVAL;
-    if (!parent->registered) return -ENOENT;
+    parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
+    if (parent == NULL) return -ENOENT;
     /*  Try to reclaim an existing devfs entry, create if there isn't one  */
     de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len,
 			   FALSE, TRUE, &is_new, FALSE);
@@ -2920,7 +2902,6 @@
 	printk ("%s: mkdir(): existing entry\n", DEVFS_NAME);
 	return -EEXIST;
     }
-    de->registered = TRUE;
     de->hide = FALSE;
     if (!S_ISDIR (de->mode) && !is_new)
     {
@@ -2936,6 +2917,7 @@
     de->inode.atime = CURRENT_TIME;
     de->inode.mtime = CURRENT_TIME;
     de->inode.ctime = CURRENT_TIME;
+    de->registered = TRUE;
     if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
 	return -ENOMEM;
 #ifdef CONFIG_DEVFS_DEBUG
@@ -2956,13 +2938,10 @@
     struct devfs_entry *de, *child;
     struct inode *inode = dentry->d_inode;
 
-    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
     if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL;
-    if (inode == dir) return -EPERM;
     fs_info = dir->i_sb->u.generic_sbp;
-    de = get_devfs_entry_from_vfs_inode (inode);
+    de = get_devfs_entry_from_vfs_inode (inode, TRUE);
     if (de == NULL) return -ENOENT;
-    if (!de->registered) return -ENOENT;
     if ( !S_ISDIR (de->mode) ) return -ENOTDIR;
     for (child = de->u.dir.first; child != NULL; child = child->next)
     {
@@ -2973,8 +2952,8 @@
 	}
     }
     if (has_children) return -ENOTEMPTY;
-    de->registered = FALSE;
     de->hide = TRUE;
+    de->registered = FALSE;
     free_dentries (de);
     return 0;
 }   /*  End Function devfs_rmdir  */
@@ -3000,15 +2979,10 @@
     }
 #endif
 
-    if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR;
     fs_info = dir->i_sb->u.generic_sbp;
-    if ( !S_ISBLK (mode) && !S_ISCHR (mode) && !S_ISFIFO (mode) &&
-	 !S_ISSOCK (mode) ) return -EPERM;
-    /*  We are allowed to create the node  */
     /*  First try to get the devfs entry for this directory  */
-    parent = get_devfs_entry_from_vfs_inode (dir);
-    if (parent == NULL) return -EINVAL;
-    if (!parent->registered) return -ENOENT;
+    parent = get_devfs_entry_from_vfs_inode (dir, TRUE);
+    if (parent == NULL) return -ENOENT;
     /*  Try to reclaim an existing devfs entry, create if there isn't one  */
     de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len,
 			   FALSE, TRUE, &is_new, FALSE);
@@ -3035,7 +3009,6 @@
 	    de->u.fifo.gid = current->egid;
 	}
     }
-    de->registered = TRUE;
     de->show_unreg = FALSE;
     de->hide = FALSE;
     de->inode.mode = mode;
@@ -3044,6 +3017,7 @@
     de->inode.atime = CURRENT_TIME;
     de->inode.mtime = CURRENT_TIME;
     de->inode.ctime = CURRENT_TIME;
+    de->registered = TRUE;
     if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
 	return -ENOMEM;
 #ifdef CONFIG_DEVFS_DEBUG
@@ -3059,32 +3033,31 @@
 
 static int devfs_readlink (struct dentry *dentry, char *buffer, int buflen)
 {
+    int err;
     struct devfs_entry *de;
 
     lock_kernel ();
-    de = get_devfs_entry_from_vfs_inode (dentry->d_inode);
+    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 ();
-    return 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;
 
     lock_kernel ();
-    de = get_devfs_entry_from_vfs_inode (dentry->d_inode);
+    de = get_devfs_entry_from_vfs_inode (dentry->d_inode, TRUE);
+    err = de ? vfs_follow_link (nd, de->u.symlink.linkname) : -ENODEV;
     unlock_kernel ();
-    return vfs_follow_link (nd, de->u.symlink.linkname);
+    return err;
 }   /*  End Function devfs_follow_link  */
 
 static struct inode_operations devfs_iops =
 {
-    link:           devfs_link,
-    unlink:         devfs_unlink,
-    symlink:        devfs_symlink,
-    mkdir:          devfs_mkdir,
-    rmdir:          devfs_rmdir,
-    mknod:          devfs_mknod,
     setattr:        devfs_notify_change,
 };
 
@@ -3151,17 +3124,17 @@
     int done = FALSE;
     int ival;
     loff_t pos, devname_offset, tlen, rpos;
-    struct devfsd_notify_struct info;
     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;
+    info->major = 0;
+    info->minor = 0;
     /*  Block for a new entry  */
     add_wait_queue (&fs_info->devfsd_wait_queue, &wait);
     current->state = TASK_INTERRUPTIBLE;
@@ -3184,18 +3157,18 @@
     /*  Now play with the data  */
     ival = atomic_read (&fs_info->devfsd_overrun_count);
     if (ival > 0) atomic_sub (ival, &fs_info->devfsd_overrun_count);
-    info.overrun_count = ival;
+    info->overrun_count = ival;
     entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer +
 	fs_info->devfsd_buf_out;
-    info.type = entry->type;
-    info.mode = entry->mode;
-    info.uid = entry->uid;
-    info.gid = entry->gid;
+    info->type = entry->type;
+    info->mode = entry->mode;
+    info->uid = entry->uid;
+    info->gid = entry->gid;
     if (entry->type == DEVFSD_NOTIFY_LOOKUP)
     {
-	info.namelen = strlen (entry->data);
+	info->namelen = strlen (entry->data);
 	pos = 0;
-	memcpy (info.devname, entry->data, info.namelen + 1);
+	memcpy (info->devname, entry->data, info->namelen + 1);
     }
     else
     {
@@ -3203,22 +3176,22 @@
 
 	if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
 	{
-	    info.major = de->u.fcb.u.device.major;
-	    info.minor = de->u.fcb.u.device.minor;
+	    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);
+	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;
+	info->namelen = DEVFS_PATHLEN - pos - 1;
+	if (info->mode == 0) info->mode = de->mode;
     }
-    devname_offset = info.devname - (char *) &info;
+    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) )
+	if ( copy_to_user (buf, (char *) info + rpos, tlen) )
 	{
 	    return -EFAULT;
 	}
@@ -3229,10 +3202,10 @@
     if ( (rpos >= devname_offset) && (len > 0) )
     {
 	/*  Copy the name  */
-	tlen = info.namelen + 1;
+	tlen = info->namelen + 1;
 	if (tlen > len) tlen = len;
 	else done = TRUE;
-	if ( copy_to_user (buf, info.devname + pos + rpos - devname_offset,
+	if ( copy_to_user (buf, info->devname + pos + rpos - devname_offset,
 			   tlen) )
 	{
 	    return -EFAULT;
@@ -3257,6 +3230,7 @@
 {
     int ival;
     struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+    static spinlock_t lock = SPIN_LOCK_UNLOCKED;
 
     switch (cmd)
     {
@@ -3270,30 +3244,21 @@
 	    doesn't matter who gets in first, as long as only one gets it  */
 	if (fs_info->devfsd_task == NULL)
 	{
-#ifdef CONFIG_SMP
-	    /*  Looks like no-one has it: check again and grab, with interrupts
-		disabled  */
-	    __cli ();
-	    if (fs_info->devfsd_task == NULL)
-#endif
+	    if ( !spin_trylock (&lock) ) return -EBUSY;
+	    fs_info->devfsd_task = current;
+	    spin_unlock (&lock);
+	    fs_info->devfsd_file = file;
+	    fs_info->devfsd_buffer = (void *) __get_free_page (GFP_KERNEL);
+	    fs_info->devfsd_info = kmalloc (sizeof *fs_info->devfsd_info,
+					    GFP_KERNEL);
+	    if (!fs_info->devfsd_buffer || !fs_info->devfsd_info)
 	    {
-		fs_info->devfsd_event_mask = 0;  /*  Temporary disable  */
-		fs_info->devfsd_task = current;
+		devfsd_close (inode, file);
+		return -ENOMEM;
 	    }
-#ifdef CONFIG_SMP
-	    __sti ();
-#endif
-	}
-	/*  Verify the task has grabbed the queue  */
-	if (fs_info->devfsd_task != current) return -EBUSY;
-	fs_info->devfsd_file = file;
-	fs_info->devfsd_buffer = (void *) __get_free_page (GFP_KERNEL);
-	if (fs_info->devfsd_buffer == NULL)
-	{
-	    devfsd_close (inode, file);
-	    return -ENOMEM;
+	    fs_info->devfsd_buf_out = fs_info->devfsd_buf_in;
 	}
-	fs_info->devfsd_buf_out = fs_info->devfsd_buf_in;
+	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:
@@ -3314,25 +3279,26 @@
 
 static int devfsd_close (struct inode *inode, struct file *file)
 {
+    unsigned long flags;
     struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
 
-    lock_kernel();
-    if (fs_info->devfsd_file != file)
-    {
-	unlock_kernel();
-	return 0;
-    }
+    if (fs_info->devfsd_file != file) return 0;
     fs_info->devfsd_event_mask = 0;
     fs_info->devfsd_file = NULL;
+    spin_lock_irqsave (&fs_info->devfsd_buffer_lock, flags);
     if (fs_info->devfsd_buffer)
     {
-	while (fs_info->devfsd_buffer_in_use) schedule ();
 	free_page ( (unsigned long) fs_info->devfsd_buffer );
+	fs_info->devfsd_buffer = NULL;
+    }
+    if (fs_info->devfsd_info)
+    {
+	kfree (fs_info->devfsd_info);
+	fs_info->devfsd_info = NULL;
     }
-    fs_info->devfsd_buffer = NULL;
+    spin_unlock_irqrestore (&fs_info->devfsd_buffer_lock, flags);
     fs_info->devfsd_task = NULL;
     wake_up (&fs_info->revalidate_wait_queue);
-    unlock_kernel();
     return 0;
 }   /*  End Function devfsd_close  */
 
@@ -3362,7 +3328,7 @@
 {
     int err;
 
-    if ( (boot_options & OPTION_NOMOUNT) ) return;
+    if ( !(boot_options & OPTION_MOUNT) ) return;
     err = do_mount ("none", "/dev", "devfs", 0, "");
     if (err == 0) printk ("Mounted devfs on /dev\n");
     else printk ("Warning: unable to mount devfs, err: %d\n", err);

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