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
- Lines: 1118
- Date:
Wed Jun 20 10:54:31 2001
- Orig file:
v2.4.5/linux/fs/devfs/base.c
- Orig date:
Tue May 22 09:35:42 2001
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)