summaryrefslogtreecommitdiff
path: root/fs/devfs/base.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-04 18:38:57 -0800
committerLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-04 18:38:57 -0800
commita5287abe398b74df9040d6dcd8356cf53a174e84 (patch)
tree181a6ce3feb61fabc1dd3ca2d452074a419df431 /fs/devfs/base.c
parent4fdbe71c852f4b44203913c93a5d3a8a12041167 (diff)
v2.4.5.2 -> v2.4.5.3
- remember to increment the version number - Chris Mason: reiserfs mark_journal_new and bh leak fix - Richard Gooch: devfs update - Alexander Viro: further FS cleanup (superblock list) - David Woodhouse: MTD update - Kai Germaschewski: ISDN update (stanford checker fixes etc) - Rich Baum: gcc-3.0 warning fixes - Jeff Garzik: network driver updates - Geert Uytterhoeven: m68k fbdev logo merge glitch fix - Andrea Arcangeli: fix signal return path - David Miller: Sparc updates - Johannes Erdfelt: USB update - Carsten Otte, Andries Brouwer: don't clear blk_size unconditionally on partition check - Martin Frey: alpha Sable irq fix - Paul Mackerras: PPC softirq update - Patrick Mochel: PCI power management infrastructure - Robert Siemer: miroSOUND driver update - Neil Brown: knfsd updates, including ability to export ReiserFS filesystems - Trond Myklebust: NFS readdir fixup, don't update atime on client - Andrew Morton: truncate_inode_pages speedup - Paul Menage: make inode quota count all inodes..
Diffstat (limited to 'fs/devfs/base.c')
-rw-r--r--fs/devfs/base.c270
1 files changed, 125 insertions, 145 deletions
diff --git a/fs/devfs/base.c b/fs/devfs/base.c
index 5622686366f3..0f3d5969f651 100644
--- a/fs/devfs/base.c
+++ b/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,23 @@
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
*/
#include <linux/types.h>
#include <linux/errno.h>
@@ -515,7 +532,7 @@
#include <asm/bitops.h>
#include <asm/atomic.h>
-#define DEVFS_VERSION "0.102 (20000622)"
+#define DEVFS_VERSION "0.105 (20010604)"
#define DEVFS_NAME "devfs"
@@ -560,7 +577,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); \
@@ -694,9 +711,9 @@ static unsigned int devfs_debug = DEBUG_NONE;
#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 */
@@ -1392,11 +1409,10 @@ static void unregister (struct devfs_entry *de)
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 +1457,9 @@ void devfs_unregister (devfs_handle_t de)
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 +1469,16 @@ int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags,
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 +1487,7 @@ int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags,
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 +1507,6 @@ int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags,
}
/* 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 +1520,39 @@ int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags,
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 */
@@ -1737,10 +1767,10 @@ int devfs_generate_path (devfs_handle_t de, char *path, int buflen)
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 +2029,56 @@ int devfs_unregister_blkdev (unsigned int major, const char *name)
static int __init devfs_setup (char *str)
{
- while ( (*str != '\0') && !isspace (*str) )
+ static struct
+ {
+ char *name;
+ unsigned int mask;
+ unsigned int *opt;
+ } devfs_options_tab[] __initdata =
{
#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
+ {"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 +2105,7 @@ EXPORT_SYMBOL(devfs_get_first_child);
EXPORT_SYMBOL(devfs_get_next_sibling);
EXPORT_SYMBOL(devfs_auto_unregister);
EXPORT_SYMBOL(devfs_get_unregister_slave);
+EXPORT_SYMBOL(devfs_get_name);
EXPORT_SYMBOL(devfs_register_chrdev);
EXPORT_SYMBOL(devfs_register_blkdev);
EXPORT_SYMBOL(devfs_unregister_chrdev);
@@ -2125,15 +2128,15 @@ static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info,
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,6 +2232,7 @@ static int get_removable_partition (struct devfs_entry *dir, const char *name,
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)
@@ -2273,7 +2277,11 @@ static void devfs_read_inode (struct inode *inode)
}
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;
@@ -2399,12 +2407,6 @@ static struct inode *get_vfs_inode (struct super_block *sb,
/* 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,11 +2415,6 @@ static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir)
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);
if ( (long) file->f_pos < 0 ) return -EINVAL;
@@ -2538,7 +2535,12 @@ out:
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,
};
@@ -2723,12 +2725,6 @@ static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry)
/* 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) ?
@@ -2845,13 +2841,12 @@ static int devfs_unlink (struct inode *dir, struct dentry *dentry)
}
#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);
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 +2859,16 @@ static int devfs_symlink (struct inode *dir, struct dentry *dentry,
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,
+ 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,9 +2898,7 @@ static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
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;
@@ -2956,9 +2948,7 @@ static int devfs_rmdir (struct inode *dir, struct dentry *dentry)
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);
if (de == NULL) return -ENOENT;
@@ -3000,11 +2990,7 @@ static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
}
#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;
@@ -3079,12 +3065,6 @@ static int devfs_follow_link (struct dentry *dentry, struct nameidata *nd)
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,
};
@@ -3362,7 +3342,7 @@ void __init mount_devfs_fs (void)
{
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);