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
- Lines: 800
- Date:
Sat Sep 22 20:35:43 2001
- Orig file:
v2.4.9/linux/fs/devfs/base.c
- Orig date:
Wed Jul 25 17:10:24 2001
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)