diff -Nur linux-2.6.0.devfs.orig/fs/devfs/base.c linux-2.6.0.devfs.1/fs/devfs/base.c --- linux-2.6.0.devfs.orig/fs/devfs/base.c 2003-12-28 00:52:16.000000000 +0800 +++ linux-2.6.0.devfs.1/fs/devfs/base.c 2003-12-28 13:28:56.000000000 +0800 @@ -687,48 +687,48 @@ #include "internal.h" -#define DEVFS_VERSION "1.22 (20021013)" +#define DEVFS_VERSION "1.22 (20021013)" -#define DEVFS_NAME "devfs" +#define DEVFS_NAME "devfs" -#define FIRST_INODE 1 +#define FIRST_INODE 1 -#define STRING_LENGTH 256 -#define FAKE_BLOCK_SIZE 1024 -#define POISON_PTR ( *(void **) poison_array ) -#define MAGIC_VALUE 0x327db823 +#define STRING_LENGTH 256 +#define FAKE_BLOCK_SIZE 1024 +#define POISON_PTR ( *(void **) poison_array ) +#define MAGIC_VALUE 0x327db823 #ifndef TRUE -# define TRUE 1 -# define FALSE 0 +# define TRUE 1 +# define FALSE 0 #endif -#define MODE_DIR (S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO) +#define MODE_DIR (S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO) -#define DEBUG_NONE 0x0000000 -#define DEBUG_MODULE_LOAD 0x0000001 -#define DEBUG_REGISTER 0x0000002 -#define DEBUG_UNREGISTER 0x0000004 -#define DEBUG_FREE 0x0000008 -#define DEBUG_SET_FLAGS 0x0000010 -#define DEBUG_S_READ 0x0000100 /* Break */ -#define DEBUG_I_LOOKUP 0x0001000 /* Break */ -#define DEBUG_I_CREATE 0x0002000 -#define DEBUG_I_GET 0x0004000 -#define DEBUG_I_CHANGE 0x0008000 -#define DEBUG_I_UNLINK 0x0010000 -#define DEBUG_I_RLINK 0x0020000 -#define DEBUG_I_FLINK 0x0040000 -#define DEBUG_I_MKNOD 0x0080000 -#define DEBUG_F_READDIR 0x0100000 /* Break */ -#define DEBUG_D_DELETE 0x1000000 /* Break */ -#define DEBUG_D_RELEASE 0x2000000 -#define DEBUG_D_IPUT 0x4000000 -#define DEBUG_ALL 0xfffffff -#define DEBUG_DISABLED DEBUG_NONE +#define DEBUG_NONE 0x0000000 +#define DEBUG_MODULE_LOAD 0x0000001 +#define DEBUG_REGISTER 0x0000002 +#define DEBUG_UNREGISTER 0x0000004 +#define DEBUG_FREE 0x0000008 +#define DEBUG_SET_FLAGS 0x0000010 +#define DEBUG_S_READ 0x0000100 /* Break */ +#define DEBUG_I_LOOKUP 0x0001000 /* Break */ +#define DEBUG_I_CREATE 0x0002000 +#define DEBUG_I_GET 0x0004000 +#define DEBUG_I_CHANGE 0x0008000 +#define DEBUG_I_UNLINK 0x0010000 +#define DEBUG_I_RLINK 0x0020000 +#define DEBUG_I_FLINK 0x0040000 +#define DEBUG_I_MKNOD 0x0080000 +#define DEBUG_F_READDIR 0x0100000 /* Break */ +#define DEBUG_D_DELETE 0x1000000 /* Break */ +#define DEBUG_D_RELEASE 0x2000000 +#define DEBUG_D_IPUT 0x4000000 +#define DEBUG_ALL 0xfffffff +#define DEBUG_DISABLED DEBUG_NONE -#define OPTION_NONE 0x00 -#define OPTION_MOUNT 0x01 +#define OPTION_NONE 0x00 +#define OPTION_MOUNT 0x01 #define PRINTK(format, args...) \ {printk (KERN_ERR "%s" format, __FUNCTION__ , ## args);} @@ -738,6 +738,8 @@ printk ("Forcing Oops\n"); \ BUG();} +#define NAMEOF(de) ( (de)->mode ? (de)->name : (de)->u.name ) + #ifdef CONFIG_DEVFS_DEBUG # define VERIFY_ENTRY(de) \ {if ((de) && (de)->magic_number != MAGIC_VALUE) \ @@ -754,98 +756,89 @@ typedef struct devfs_entry *devfs_handle_t; -struct directory_type -{ - rwlock_t lock; /* Lock for searching(R)/updating(W) */ - struct devfs_entry *first; - struct devfs_entry *last; - unsigned char no_more_additions:1; +struct directory_type { + rwlock_t lock; /* Lock for searching(R)/updating(W) */ + struct devfs_entry *first; + struct devfs_entry *last; + unsigned char no_more_additions:1; }; -struct bdev_type -{ - dev_t dev; +struct bdev_type { + dev_t dev; }; -struct cdev_type -{ - struct file_operations *ops; - dev_t dev; - unsigned char autogen:1; +struct cdev_type { + struct file_operations *ops; + dev_t dev; + unsigned char autogen:1; }; -struct symlink_type -{ - unsigned int length; /* Not including the NULL-termimator */ - char *linkname; /* This is NULL-terminated */ +struct symlink_type { + unsigned int length; /* Not including the NULL-termimator */ + char *linkname; /* This is NULL-terminated */ }; -struct devfs_inode /* This structure is for "persistent" inode storage */ -{ - struct dentry *dentry; - struct timespec atime; - struct timespec mtime; - struct timespec ctime; - unsigned int ino; /* Inode number as seen in the VFS */ - uid_t uid; - gid_t gid; +/* This structure is for "persistent" inode storage */ +struct devfs_inode { + struct dentry *dentry; + struct timespec atime; + struct timespec mtime; + struct timespec ctime; + unsigned int ino; /* Inode number as seen in the VFS */ + uid_t uid; + gid_t gid; }; -struct devfs_entry -{ +struct devfs_entry { #ifdef CONFIG_DEVFS_DEBUG - unsigned int magic_number; + unsigned int magic_number; #endif - void *info; - atomic_t refcount; /* When this drops to zero, it's unused */ - union - { - struct directory_type dir; - struct bdev_type bdev; - struct cdev_type cdev; - struct symlink_type symlink; - const char *name; /* Only used for (mode == 0) */ - } - u; - struct devfs_entry *prev; /* Previous entry in the parent directory */ - struct devfs_entry *next; /* Next entry in the parent directory */ - struct devfs_entry *parent; /* The parent directory */ - struct devfs_inode inode; - umode_t mode; - unsigned short namelen; /* I think 64k+ filenames are a way off... */ - unsigned char vfs_deletable:1;/* Whether the VFS may delete the entry */ - char name[1]; /* This is just a dummy: the allocated array - is bigger. This is NULL-terminated */ + void *info; + atomic_t refcount; /* 0 => unused */ + union { + struct directory_type dir; + struct bdev_type bdev; + struct cdev_type cdev; + struct symlink_type symlink; + const char *name; /* Only used for (mode == 0) */ + } u; + struct devfs_entry *prev; /* Previous entry in the parent dir */ + struct devfs_entry *next; /* Next entry in the parent dir */ + struct devfs_entry *parent; /* The parent directory */ + struct devfs_inode inode; + umode_t mode; + unsigned short namelen; + unsigned char vfs_deletable:1; /* Whether VFS may delete the entry */ + char name[1]; /* NULL terminated dummy pointer */ }; -/* The root of the device tree */ +/* The root of the device tree */ static struct devfs_entry *root_entry; -struct devfsd_buf_entry -{ - struct devfs_entry *de; /* The name is generated with this */ - unsigned short type; /* The type of event */ - umode_t mode; - uid_t uid; - gid_t gid; - struct devfsd_buf_entry *next; +struct devfsd_buf_entry { + struct devfs_entry *de; /* The name is generated with this */ + unsigned short type; /* The type of event */ + umode_t mode; + uid_t uid; + gid_t gid; + struct devfsd_buf_entry *next; }; -struct fs_info /* This structure is for the mounted devfs */ -{ - struct super_block *sb; - spinlock_t devfsd_buffer_lock; /* Lock when inserting/deleting events */ - struct devfsd_buf_entry *devfsd_first_event; - struct devfsd_buf_entry *devfsd_last_event; - volatile int devfsd_sleeping; - volatile struct task_struct *devfsd_task; - volatile pid_t devfsd_pgrp; - 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; /* Wake devfsd on input */ - wait_queue_head_t revalidate_wait_queue; /* Wake when devfsd sleeps */ +/* This structure is for the mounted devfs */ +struct fs_info { + struct super_block *sb; + spinlock_t devfsd_buffer_lock; /* Lock when inserting/deleting events */ + struct devfsd_buf_entry *devfsd_first_event; + struct devfsd_buf_entry *devfsd_last_event; + volatile int devfsd_sleeping; + volatile struct task_struct *devfsd_task; + volatile pid_t devfsd_pgrp; + 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; /* Wake devfsd on input */ + wait_queue_head_t revalidate_wait_queue; /* Wake when devfsd sleeps */ }; static struct fs_info fs_info = {.devfsd_buffer_lock = SPIN_LOCK_UNLOCKED}; @@ -858,7 +851,7 @@ static unsigned int stat_num_bytes; #endif static unsigned char poison_array[8] = - {0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a}; + { 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a }; #ifdef CONFIG_DEVFS_MOUNT static unsigned int boot_options = OPTION_MOUNT; @@ -866,78 +859,88 @@ static unsigned int boot_options = OPTION_NONE; #endif -/* Forward function declarations */ -static devfs_handle_t _devfs_walk_path (struct devfs_entry *dir, - const char *name, int namelen, - int traverse_symlink); -static ssize_t devfsd_read (struct file *file, char *buf, size_t len, - loff_t *ppos); -static int devfsd_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static int devfsd_close (struct inode *inode, struct file *file); +/* Forward function declarations */ +static devfs_handle_t _devfs_walk_path(struct devfs_entry *dir, + const char *name, int namelen, + int traverse_symlink); +static ssize_t devfsd_read(struct file *file, char *buf, size_t len, + loff_t * ppos); +static int devfsd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int devfsd_close(struct inode *inode, struct file *file); #ifdef CONFIG_DEVFS_DEBUG -static ssize_t stat_read (struct file *file, char *buf, size_t len, - loff_t *ppos); -static struct file_operations stat_fops = -{ - .read = stat_read, +static ssize_t stat_read(struct file *file, char *buf, size_t len, + loff_t * ppos); + +static struct file_operations stat_fops = { + .read = stat_read, }; #endif - -/* Devfs daemon file operations */ -static struct file_operations devfsd_fops = -{ - .read = devfsd_read, - .ioctl = devfsd_ioctl, - .release = devfsd_close, +/* Devfs daemon file operations */ +static struct file_operations devfsd_fops = { + .read = devfsd_read, + .ioctl = devfsd_ioctl, + .release = devfsd_close, }; - -/* Support functions follow */ - +/* Support functions follow */ /** * devfs_get - Get a reference to a devfs entry. * @de: The devfs entry. */ - -static struct devfs_entry *devfs_get (struct devfs_entry *de) +static struct devfs_entry *devfs_get(struct devfs_entry *de) { - VERIFY_ENTRY (de); - if (de) atomic_inc (&de->refcount); - return de; -} /* End Function devfs_get */ + VERIFY_ENTRY(de); + if (de) + atomic_inc(&de->refcount); + return de; +} /** * devfs_put - Put (release) a reference to a devfs entry. * @de: The handle to the devfs entry. */ - -static void devfs_put (devfs_handle_t de) +static void devfs_put(devfs_handle_t de) { - if (!de) return; - VERIFY_ENTRY (de); - if (de->info == POISON_PTR) OOPS ("(%p): poisoned pointer\n", de); - if ( !atomic_dec_and_test (&de->refcount) ) return; - if (de == root_entry) OOPS ("(%p): root entry being freed\n", de); - DPRINTK (DEBUG_FREE, "(%s): de: %p, parent: %p \"%s\"\n", - de->name, de, de->parent, - de->parent ? de->parent->name : "no parent"); - if ( S_ISLNK (de->mode) ) kfree (de->u.symlink.linkname); - if ( S_ISCHR (de->mode) && de->u.cdev.autogen ) - devfs_dealloc_devnum (de->mode, de->u.cdev.dev); - WRITE_ENTRY_MAGIC (de, 0); + if (!de) + return; + + VERIFY_ENTRY(de); + if (de->info == POISON_PTR) + OOPS("(%p): poisoned pointer\n", de); + + if (!atomic_dec_and_test(&de->refcount)) + return; + + if (de == root_entry) + OOPS("(%p): root entry being freed\n", de); + + DPRINTK(DEBUG_FREE, "(%s): de: %p, parent: %p \"%s\"\n", + de->name, de, de->parent, + de->parent ? de->parent->name : "no parent"); + + if (S_ISLNK(de->mode)) + kfree(de->u.symlink.linkname); + + if (S_ISCHR(de->mode) && de->u.cdev.autogen) + devfs_dealloc_devnum(de->mode, de->u.cdev.dev); + + WRITE_ENTRY_MAGIC(de, 0); + #ifdef CONFIG_DEVFS_DEBUG - spin_lock (&stat_lock); - --stat_num_entries; - stat_num_bytes -= sizeof *de + de->namelen; - if ( S_ISLNK (de->mode) ) stat_num_bytes -= de->u.symlink.length + 1; - spin_unlock (&stat_lock); + spin_lock(&stat_lock); + --stat_num_entries; + stat_num_bytes -= sizeof *de + de->namelen; + if (S_ISLNK(de->mode)) + stat_num_bytes -= de->u.symlink.length + 1; + spin_unlock(&stat_lock); #endif - de->info = POISON_PTR; - kfree (de); -} /* End Function devfs_put */ + + de->info = POISON_PTR; + kfree(de); +} /** * _devfs_search_dir - Search for a devfs entry in a directory. @@ -949,68 +952,75 @@ * on success, else %NULL. The directory must be locked already. * An implicit devfs_get() is performed on the returned entry. */ - -static struct devfs_entry *_devfs_search_dir (struct devfs_entry *dir, - const char *name, - unsigned int namelen) +static struct devfs_entry *_devfs_search_dir(struct devfs_entry *dir, + const char *name, + unsigned int namelen) { - struct devfs_entry *curr; + struct devfs_entry *curr; + + if (!S_ISDIR(dir->mode)) { + PRINTK("(%s): not a directory\n", dir->name); + return NULL; + } - if ( !S_ISDIR (dir->mode) ) - { - PRINTK ("(%s): not a directory\n", dir->name); - return NULL; - } - for (curr = dir->u.dir.first; curr != NULL; curr = curr->next) - { - if (curr->namelen != namelen) continue; - if (memcmp (curr->name, name, namelen) == 0) break; - /* Not found: try the next one */ - } - return devfs_get (curr); -} /* End Function _devfs_search_dir */ + for (curr = dir->u.dir.first; curr != NULL; curr = curr->next) { + if (curr->namelen != namelen) + continue; + + if (memcmp(curr->name, name, namelen) == 0) + break; + } + return devfs_get(curr); +} /** * _devfs_alloc_entry - Allocate a devfs entry. - * @name: the name of the entry - * @namelen: the number of characters in @name - * @mode: the mode for the entry + * @name: The name of the entry + * @namelen: The number of characters in @name + * @mode: The mode for the entry * - * Allocate a devfs entry and returns a pointer to the entry on success, else - * %NULL. + * Allocate a devfs entry and returns a pointer to the entry on success, + * else %NULL. */ +static struct devfs_entry *_devfs_alloc_entry(const char *name, + unsigned int namelen, + umode_t mode) +{ + struct devfs_entry *new; + static unsigned long inode_counter = FIRST_INODE; + static spinlock_t counter_lock = SPIN_LOCK_UNLOCKED; + + if (name && (namelen < 1)) + namelen = strlen(name); + + if ((new = kmalloc(sizeof *new + namelen, GFP_KERNEL)) == NULL) + return NULL; + + memset(new, 0, sizeof *new + namelen); /* Will set '\0' on name */ + new->mode = mode; + if (S_ISDIR(mode)) + rwlock_init(&new->u.dir.lock); + atomic_set(&new->refcount, 1); + spin_lock(&counter_lock); + new->inode.ino = inode_counter++; + spin_unlock(&counter_lock); + + if (name) + memcpy(new->name, name, namelen); + new->namelen = namelen; + + WRITE_ENTRY_MAGIC(new, MAGIC_VALUE); -static struct devfs_entry *_devfs_alloc_entry (const char *name, - unsigned int namelen, - umode_t mode) -{ - struct devfs_entry *new; - static unsigned long inode_counter = FIRST_INODE; - static spinlock_t counter_lock = SPIN_LOCK_UNLOCKED; - - if ( name && (namelen < 1) ) namelen = strlen (name); - if ( ( new = kmalloc (sizeof *new + namelen, GFP_KERNEL) ) == NULL ) - return NULL; - memset (new, 0, sizeof *new + namelen); /* Will set '\0' on name */ - new->mode = mode; - if ( S_ISDIR (mode) ) rwlock_init (&new->u.dir.lock); - atomic_set (&new->refcount, 1); - spin_lock (&counter_lock); - new->inode.ino = inode_counter++; - spin_unlock (&counter_lock); - if (name) memcpy (new->name, name, namelen); - new->namelen = namelen; - WRITE_ENTRY_MAGIC (new, MAGIC_VALUE); #ifdef CONFIG_DEVFS_DEBUG - spin_lock (&stat_lock); - ++stat_num_entries; - stat_num_bytes += sizeof *new + namelen; - spin_unlock (&stat_lock); + spin_lock(&stat_lock); + ++stat_num_entries; + stat_num_bytes += sizeof *new + namelen; + spin_unlock(&stat_lock); #endif - return new; -} /* End Function _devfs_alloc_entry */ + return new; +} /** * _devfs_append_entry - Append a devfs entry to a directory's child list. @@ -1024,85 +1034,102 @@ * The value 0 is returned on success, else a negative error code. * On failure, an implicit devfs_put() is performed on %de. */ - -static int _devfs_append_entry (devfs_handle_t dir, devfs_handle_t de, - devfs_handle_t *old_de) +static int _devfs_append_entry(devfs_handle_t dir, + devfs_handle_t de, devfs_handle_t * old_de) { - int retval; + int retval; + + if (old_de) + *old_de = NULL; + + if (!S_ISDIR(dir->mode)) { + PRINTK("(%s): dir: \"%s\" is not a directory\n", de->name, + dir->name); + devfs_put(de); + return -ENOTDIR; + } + + write_lock(&dir->u.dir.lock); + if (dir->u.dir.no_more_additions) + retval = -ENOENT; + else { + struct devfs_entry *old; + + old = _devfs_search_dir(dir, de->name, de->namelen); + + if (old_de) + *old_de = old; + else + devfs_put(old); + + if (old == NULL) { + de->parent = dir; + de->prev = dir->u.dir.last; + + /* Append to the directory's list of children */ + if (dir->u.dir.first == NULL) + dir->u.dir.first = de; + else + dir->u.dir.last->next = de; + + dir->u.dir.last = de; + retval = 0; + } else + retval = -EEXIST; + } + write_unlock(&dir->u.dir.lock); - if (old_de) *old_de = NULL; - if ( !S_ISDIR (dir->mode) ) - { - PRINTK ("(%s): dir: \"%s\" is not a directory\n", de->name, dir->name); - devfs_put (de); - return -ENOTDIR; - } - write_lock (&dir->u.dir.lock); - if (dir->u.dir.no_more_additions) retval = -ENOENT; - else - { - struct devfs_entry *old; - - old = _devfs_search_dir (dir, de->name, de->namelen); - if (old_de) *old_de = old; - else devfs_put (old); - if (old == NULL) - { - de->parent = dir; - de->prev = dir->u.dir.last; - /* Append to the directory's list of children */ - if (dir->u.dir.first == NULL) dir->u.dir.first = de; - else dir->u.dir.last->next = de; - dir->u.dir.last = de; - retval = 0; - } - else retval = -EEXIST; - } - write_unlock (&dir->u.dir.lock); - if (retval) devfs_put (de); - return retval; -} /* End Function _devfs_append_entry */ + if (retval) + devfs_put(de); + return retval; +} /** * _devfs_get_root_entry - Get the root devfs entry. * * Returns the root devfs entry on success, else %NULL. */ - -static struct devfs_entry *_devfs_get_root_entry (void) +static struct devfs_entry * _devfs_get_root_entry(void) { - struct devfs_entry *new; - static spinlock_t root_lock = SPIN_LOCK_UNLOCKED; + struct devfs_entry *new; + static spinlock_t root_lock = SPIN_LOCK_UNLOCKED; + + /* Always ensure the root is created */ + if (root_entry) + return root_entry; + + if ((new = _devfs_alloc_entry(NULL, 0, MODE_DIR)) == NULL) + return NULL; + + spin_lock(&root_lock); + if (root_entry) { + spin_unlock(&root_lock); + devfs_put(new); + return (root_entry); + } + root_entry = new; + spin_unlock(&root_lock); + + /* And create the entry for ".devfsd" */ + if ((new = _devfs_alloc_entry(".devfsd", 0, S_IFCHR | S_IRUSR | S_IWUSR)) == NULL) + return NULL; + + new->u.cdev.dev = devfs_alloc_devnum(S_IFCHR | S_IRUSR | S_IWUSR); + new->u.cdev.ops = &devfsd_fops; + _devfs_append_entry(root_entry, new, NULL); - /* Always ensure the root is created */ - if (root_entry) return root_entry; - if ( ( new = _devfs_alloc_entry (NULL, 0,MODE_DIR) ) == NULL ) return NULL; - spin_lock (&root_lock); - if (root_entry) - { - spin_unlock (&root_lock); - devfs_put (new); - return (root_entry); - } - root_entry = new; - spin_unlock (&root_lock); - /* And create the entry for ".devfsd" */ - if ( ( new = _devfs_alloc_entry (".devfsd", 0, S_IFCHR |S_IRUSR |S_IWUSR) ) - == NULL ) return NULL; - new->u.cdev.dev = devfs_alloc_devnum (S_IFCHR |S_IRUSR |S_IWUSR); - new->u.cdev.ops = &devfsd_fops; - _devfs_append_entry (root_entry, new, NULL); #ifdef CONFIG_DEVFS_DEBUG - if ( ( new = _devfs_alloc_entry (".stat", 0, S_IFCHR | S_IRUGO | S_IWUGO) ) - == NULL ) return NULL; - new->u.cdev.dev = devfs_alloc_devnum (S_IFCHR | S_IRUGO | S_IWUGO); - new->u.cdev.ops = &stat_fops; - _devfs_append_entry (root_entry, new, NULL); + if ((new = _devfs_alloc_entry(".stat", 0, S_IFCHR | S_IRUGO | S_IWUGO)) == NULL) + return NULL; + + new->u.cdev.dev = devfs_alloc_devnum(S_IFCHR | S_IRUGO | S_IWUGO); + new->u.cdev.ops = &stat_fops; + _devfs_append_entry(root_entry, new, NULL); #endif - return root_entry; -} /* End Function _devfs_get_root_entry */ + return root_entry; +} /** * _devfs_descend - Descend down a tree using the next component name. @@ -1117,188 +1144,202 @@ * no matching entry, %NULL is returned. * An implicit devfs_get() is performed on the returned entry. */ +static struct devfs_entry * _devfs_descend(struct devfs_entry *dir, + const char *name, + int namelen, int *next_pos) +{ + const char *stop, *ptr; + struct devfs_entry *entry; + + /* Special-case going to parent directory */ + if ((namelen >= 3) && (strncmp(name, "../", 3) == 0)) { + *next_pos = 3; + return devfs_get(dir->parent); + } + + stop = name + namelen; + /* Search for a possible '/' */ + for (ptr = name; (ptr < stop) && (*ptr != '/'); ++ptr) ; + *next_pos = ptr - name; + + read_lock(&dir->u.dir.lock); + entry = _devfs_search_dir(dir, name, *next_pos); + read_unlock(&dir->u.dir.lock); + + return entry; +} + +static devfs_handle_t _devfs_make_parent_for_leaf(struct devfs_entry *dir, + const char *name, + int namelen, int *leaf_pos) +{ + int next_pos = 0; + + if (dir == NULL) + dir = _devfs_get_root_entry(); + + if (dir == NULL) + return NULL; + + devfs_get(dir); + /* Search for possible trailing component and ignore it */ + for (--namelen; (namelen > 0) && (name[namelen] != '/'); --namelen) ; + *leaf_pos = (name[namelen] == '/') ? (namelen + 1) : 0; + + for (; namelen > 0; name += next_pos, namelen -= next_pos) { + struct devfs_entry *de, *old = NULL; + + if ((de = _devfs_descend(dir, name, namelen, &next_pos)) == NULL) { + de = _devfs_alloc_entry(name, next_pos, MODE_DIR); + devfs_get(de); + + if (!de || _devfs_append_entry(dir, de, &old)) { + devfs_put(de); + + if (!old || !S_ISDIR(old->mode)) { + devfs_put(old); + devfs_put(dir); + return NULL; + } + + /* Use the existing directory */ + de = old; + } + } -static struct devfs_entry *_devfs_descend (struct devfs_entry *dir, - const char *name, int namelen, - int *next_pos) -{ - const char *stop, *ptr; - struct devfs_entry *entry; - - if ( (namelen >= 3) && (strncmp (name, "../", 3) == 0) ) - { /* Special-case going to parent directory */ - *next_pos = 3; - return devfs_get (dir->parent); - } - stop = name + namelen; - /* Search for a possible '/' */ - for (ptr = name; (ptr < stop) && (*ptr != '/'); ++ptr); - *next_pos = ptr - name; - read_lock (&dir->u.dir.lock); - entry = _devfs_search_dir (dir, name, *next_pos); - read_unlock (&dir->u.dir.lock); - return entry; -} /* End Function _devfs_descend */ - - -static devfs_handle_t _devfs_make_parent_for_leaf (struct devfs_entry *dir, - const char *name, - int namelen, int *leaf_pos) -{ - int next_pos = 0; - - if (dir == NULL) dir = _devfs_get_root_entry (); - if (dir == NULL) return NULL; - devfs_get (dir); - /* Search for possible trailing component and ignore it */ - for (--namelen; (namelen > 0) && (name[namelen] != '/'); --namelen); - *leaf_pos = (name[namelen] == '/') ? (namelen + 1) : 0; - for (; namelen > 0; name += next_pos, namelen -= next_pos) - { - struct devfs_entry *de, *old = NULL; - - if ( ( de = _devfs_descend (dir, name, namelen, &next_pos) ) == NULL ) - { - de = _devfs_alloc_entry (name, next_pos, MODE_DIR); - devfs_get (de); - if ( !de || _devfs_append_entry (dir, de, &old) ) - { - devfs_put (de); - if ( !old || !S_ISDIR (old->mode) ) - { - devfs_put (old); - devfs_put (dir); - return NULL; + if (de == dir->parent) { + devfs_put(dir); + devfs_put(de); + return NULL; } - de = old; /* Use the existing directory */ - } + + devfs_put(dir); + dir = de; + + if (name[next_pos] == '/') + ++next_pos; + } + + return dir; +} + +static devfs_handle_t _devfs_prepare_leaf(devfs_handle_t * dir, + const char *name, umode_t mode) +{ + int namelen, leaf_pos; + struct devfs_entry *de; + + namelen = strlen(name); + if ((*dir = _devfs_make_parent_for_leaf(*dir, name, namelen, &leaf_pos)) == NULL) { + PRINTK("(%s): could not create parent path\n", name); + return NULL; } - if (de == dir->parent) - { - devfs_put (dir); - devfs_put (de); - return NULL; - } - devfs_put (dir); - dir = de; - if (name[next_pos] == '/') ++next_pos; - } - return dir; -} /* End Function _devfs_make_parent_for_leaf */ - - -static devfs_handle_t _devfs_prepare_leaf (devfs_handle_t *dir, - const char *name, umode_t mode) -{ - int namelen, leaf_pos; - struct devfs_entry *de; - - namelen = strlen (name); - if ( ( *dir = _devfs_make_parent_for_leaf (*dir, name, namelen, - &leaf_pos) ) == NULL ) - { - PRINTK ("(%s): could not create parent path\n", name); - return NULL; - } - if ( ( de = _devfs_alloc_entry (name + leaf_pos, namelen - leaf_pos,mode) ) - == NULL ) - { - PRINTK ("(%s): could not allocate entry\n", name); - devfs_put (*dir); - return NULL; - } - return de; -} /* End Function _devfs_prepare_leaf */ - - -static devfs_handle_t _devfs_walk_path (struct devfs_entry *dir, - const char *name, int namelen, - int traverse_symlink) -{ - int next_pos = 0; - - if (dir == NULL) dir = _devfs_get_root_entry (); - if (dir == NULL) return NULL; - devfs_get (dir); - for (; namelen > 0; name += next_pos, namelen -= next_pos) - { - struct devfs_entry *de, *link; - - if (!S_ISDIR (dir->mode)) - { - devfs_put (dir); - return NULL; - } - - if ( ( de = _devfs_descend (dir, name, namelen, &next_pos) ) == NULL ) - { - devfs_put (dir); - return NULL; - } - if (S_ISLNK (de->mode) && traverse_symlink) - { /* Need to follow the link: this is a stack chomper */ - link = _devfs_walk_path (dir, de->u.symlink.linkname, - de->u.symlink.length, TRUE); - devfs_put (de); - if (!link) - { - devfs_put (dir); + + if ((de = _devfs_alloc_entry(name + leaf_pos, namelen - leaf_pos, mode)) == NULL) { + PRINTK("(%s): could not allocate entry\n", name); + devfs_put(*dir); + return NULL; + } + + return de; +} + +static devfs_handle_t _devfs_walk_path(struct devfs_entry *dir, + const char *name, + int namelen, int traverse_symlink) +{ + int next_pos = 0; + + if (dir == NULL) + dir = _devfs_get_root_entry(); + + if (dir == NULL) return NULL; - } - de = link; + + devfs_get(dir); + for (; namelen > 0; name += next_pos, namelen -= next_pos) { + struct devfs_entry *de, *link; + + if (!S_ISDIR(dir->mode)) { + devfs_put(dir); + return NULL; + } + + if ((de = _devfs_descend(dir, name, namelen, &next_pos)) == NULL) { + devfs_put(dir); + return NULL; + } + + if (S_ISLNK(de->mode) && traverse_symlink) { + /* Need to follow the link: this is a stack chomper */ + link = _devfs_walk_path(dir, de->u.symlink.linkname, + de->u.symlink.length, TRUE); + devfs_put(de); + + if (!link) { + devfs_put(dir); + return NULL; + } + de = link; + } + devfs_put(dir); + + dir = de; + + if (name[next_pos] == '/') + ++next_pos; } - devfs_put (dir); - dir = de; - if (name[next_pos] == '/') ++next_pos; - } - return dir; -} /* End Function _devfs_walk_path */ + + return dir; +} /** * _devfs_find_entry - Find a devfs entry. - * @dir: The handle to the parent devfs directory entry. If this is %NULL the - * name is relative to the root of the devfs. + * @dir: The handle to the parent devfs directory entry. + * If this is %NULL the name is relative to the root of the devfs. * @name: The name of the entry. This may be %NULL. * @traverse_symlink: If %TRUE then symbolic links are traversed. * * Returns the devfs_entry pointer on success, else %NULL. An implicit * devfs_get() is performed. */ - -static struct devfs_entry *_devfs_find_entry (devfs_handle_t dir, +static struct devfs_entry * _devfs_find_entry(devfs_handle_t dir, const char *name, int traverse_symlink) { - unsigned int namelen = strlen (name); + unsigned int namelen = strlen(name); - if (name[0] == '/') - { - /* Skip leading pathname component */ - if (namelen < 2) - { - PRINTK ("(%s): too short\n", name); - return NULL; - } - for (++name, --namelen; (*name != '/') && (namelen > 0); - ++name, --namelen); - if (namelen < 2) - { - PRINTK ("(%s): too short\n", name); - return NULL; - } - ++name; - --namelen; - } - return _devfs_walk_path (dir, name, namelen, traverse_symlink); -} /* End Function _devfs_find_entry */ - -static struct devfs_entry *get_devfs_entry_from_vfs_inode (struct inode *inode) -{ - if (inode == NULL) return NULL; - VERIFY_ENTRY ( (struct devfs_entry *) inode->u.generic_ip ); - return inode->u.generic_ip; -} /* End Function get_devfs_entry_from_vfs_inode */ + if (name[0] == '/') { + /* Skip leading pathname component */ + if (namelen < 2) { + PRINTK("(%s): too short\n", name); + return NULL; + } + + for (++name, --namelen; + (*name != '/') && (namelen > 0); + ++name, --namelen) ; + + if (namelen < 2) { + PRINTK("(%s): too short\n", name); + return NULL; + } + + ++name; + --namelen; + } + return _devfs_walk_path(dir, name, namelen, traverse_symlink); +} + +static struct devfs_entry * get_devfs_entry_from_vfs_inode(struct inode *inode) +{ + if (inode == NULL) + return NULL; + + VERIFY_ENTRY((struct devfs_entry *) inode->u.generic_ip); + return inode->u.generic_ip; +} /** * free_dentry - Free the dentry for a device entry and invalidate inode. @@ -1307,21 +1348,24 @@ * This must only be called after the entry has been unhooked from its * parent directory. */ - -static void free_dentry (struct devfs_entry *de) +static void free_dentry(struct devfs_entry *de) { - struct dentry *dentry = de->inode.dentry; + struct dentry *dentry = de->inode.dentry; - if (!dentry) return; - spin_lock (&dcache_lock); - dget_locked (dentry); - spin_unlock (&dcache_lock); - /* Forcefully remove the inode */ - if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0; - d_drop (dentry); - dput (dentry); -} /* End Function free_dentry */ + if (!dentry) + return; + spin_lock(&dcache_lock); + dget_locked(dentry); + spin_unlock(&dcache_lock); + + /* Forcefully remove the inode */ + if (dentry->d_inode != NULL) + dentry->d_inode->i_nlink = 0; + + d_drop(dentry); + dput(dentry); +} /** * is_devfsd_or_child - Test if the current process is devfsd or one of its children. @@ -1329,26 +1373,27 @@ * * Returns %TRUE if devfsd or child, else %FALSE. */ - -static int is_devfsd_or_child (struct fs_info *fs_info) +static int is_devfsd_or_child(struct fs_info *fs_info) { - struct task_struct *p = current; + struct task_struct *p = current; - if (p == fs_info->devfsd_task) return (TRUE); - if (process_group(p) == fs_info->devfsd_pgrp) return (TRUE); - read_lock(&tasklist_lock); - for ( ; p != &init_task; p = p->real_parent) - { if (p == fs_info->devfsd_task) - { - read_unlock (&tasklist_lock); - return (TRUE); - } - } - read_unlock (&tasklist_lock); - return (FALSE); -} /* End Function is_devfsd_or_child */ + return (TRUE); + if (process_group(p) == fs_info->devfsd_pgrp) + return (TRUE); + + read_lock(&tasklist_lock); + for (; p != &init_task; p = p->real_parent) { + if (p == fs_info->devfsd_task) { + read_unlock(&tasklist_lock); + return (TRUE); + } + } + read_unlock(&tasklist_lock); + + return (FALSE); +} /** * devfsd_queue_empty - Test if devfsd has work pending in its event queue. @@ -1356,12 +1401,10 @@ * * Returns %TRUE if the queue is empty, else %FALSE. */ - -static inline int devfsd_queue_empty (struct fs_info *fs_info) +static inline int devfsd_queue_empty(struct fs_info *fs_info) { - return (fs_info->devfsd_last_event) ? FALSE : TRUE; -} /* End Function devfsd_queue_empty */ - + return (fs_info->devfsd_last_event) ? FALSE : TRUE; +} /** * wait_for_devfsd_finished - Wait for devfsd to finish processing its event queue. @@ -1369,66 +1412,83 @@ * * Returns %TRUE if no more waiting will be required, else %FALSE. */ - -static int wait_for_devfsd_finished (struct fs_info *fs_info) +static int wait_for_devfsd_finished(struct fs_info *fs_info) { - DECLARE_WAITQUEUE (wait, current); + DECLARE_WAITQUEUE(wait, current); + + if (fs_info->devfsd_task == NULL) + return (TRUE); + + if (devfsd_queue_empty(fs_info) && fs_info->devfsd_sleeping) + return TRUE; + + if (is_devfsd_or_child(fs_info)) + return (FALSE); + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&fs_info->revalidate_wait_queue, &wait); + + if (!devfsd_queue_empty(fs_info) || !fs_info->devfsd_sleeping) + if (fs_info->devfsd_task) + schedule(); - if (fs_info->devfsd_task == NULL) return (TRUE); - if (devfsd_queue_empty (fs_info) && fs_info->devfsd_sleeping) return TRUE; - if ( is_devfsd_or_child (fs_info) ) return (FALSE); - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&fs_info->revalidate_wait_queue, &wait); - if (!devfsd_queue_empty (fs_info) || !fs_info->devfsd_sleeping) - if (fs_info->devfsd_task) schedule (); - remove_wait_queue (&fs_info->revalidate_wait_queue, &wait); - __set_current_state (TASK_RUNNING); - return (TRUE); -} /* End Function wait_for_devfsd_finished */ + remove_wait_queue(&fs_info->revalidate_wait_queue, &wait); + __set_current_state(TASK_RUNNING); + return (TRUE); +} /** * devfsd_notify_de - Notify the devfsd daemon of a change. - * @de: The devfs entry that has changed. This and all parent entries will - * have their reference counts incremented if the event was queued. + * @de: The devfs entry that has changed. This and all parent entries + * will have their reference counts incremented if the event was queued. * @type: The type of change. * @mode: The mode of the entry. * @uid: The user ID. * @gid: The group ID. - * @fs_info: The filesystem info. + * @fs_info: The filesystem info. * * Returns %TRUE if an event was queued and devfsd woken up, else %FALSE. */ +static int devfsd_notify_de(struct devfs_entry *de, + unsigned short type, umode_t mode, uid_t uid, + gid_t gid, struct fs_info *fs_info) +{ + struct devfsd_buf_entry *entry; + struct devfs_entry *curr; -static int devfsd_notify_de (struct devfs_entry *de, - unsigned short type, umode_t mode, - uid_t uid, gid_t gid, struct fs_info *fs_info) -{ - struct devfsd_buf_entry *entry; - struct devfs_entry *curr; - - if ( !( fs_info->devfsd_event_mask & (1 << type) ) ) return (FALSE); - if ( ( entry = kmem_cache_alloc (devfsd_buf_cache, SLAB_KERNEL) ) == NULL ) - { - atomic_inc (&fs_info->devfsd_overrun_count); - return (FALSE); - } - for (curr = de; curr != NULL; curr = curr->parent) devfs_get (curr); - entry->de = de; - entry->type = type; - entry->mode = mode; - entry->uid = uid; - entry->gid = gid; - entry->next = NULL; - spin_lock (&fs_info->devfsd_buffer_lock); - if (!fs_info->devfsd_first_event) fs_info->devfsd_first_event = entry; - if (fs_info->devfsd_last_event) fs_info->devfsd_last_event->next = entry; - fs_info->devfsd_last_event = entry; - spin_unlock (&fs_info->devfsd_buffer_lock); - wake_up_interruptible (&fs_info->devfsd_wait_queue); - return (TRUE); -} /* End Function devfsd_notify_de */ + if (!(fs_info->devfsd_event_mask & (1 << type))) + return (FALSE); + + if ((entry = kmem_cache_alloc(devfsd_buf_cache, SLAB_KERNEL)) == NULL) { + atomic_inc(&fs_info->devfsd_overrun_count); + return (FALSE); + } + + for (curr = de; curr != NULL; curr = curr->parent) + devfs_get(curr); + + entry->de = de; + entry->type = type; + entry->mode = mode; + entry->uid = uid; + entry->gid = gid; + entry->next = NULL; + spin_lock(&fs_info->devfsd_buffer_lock); + if (!fs_info->devfsd_first_event) + fs_info->devfsd_first_event = entry; + + if (fs_info->devfsd_last_event) + fs_info->devfsd_last_event->next = entry; + + fs_info->devfsd_last_event = entry; + spin_unlock(&fs_info->devfsd_buffer_lock); + + wake_up_interruptible(&fs_info->devfsd_wait_queue); + + return (TRUE); +} /** * devfsd_notify - Notify the devfsd daemon of a change. @@ -1437,12 +1497,11 @@ * @wait: If TRUE, the function waits for the daemon to finish processing * the event. */ - -static void devfsd_notify (struct devfs_entry *de,unsigned short type) +static void devfsd_notify(struct devfs_entry *de, unsigned short type) { devfsd_notify_de(de, type, de->mode, current->euid, current->egid, &fs_info); -} +} int devfs_mk_bdev(dev_t dev, umode_t mode, const char *fmt, ...) { @@ -1452,44 +1511,45 @@ int error, n; va_start(args, fmt); + n = vsnprintf(buf, 64, fmt, args); if (n >= 64 || !buf[0]) { printk(KERN_WARNING "%s: invalid format string\n", - __FUNCTION__); + __FUNCTION__); return -EINVAL; } - + if (!S_ISBLK(mode)) { printk(KERN_WARNING "%s: invalide mode (%u) for %s\n", - __FUNCTION__, mode, buf); + __FUNCTION__, mode, buf); return -EINVAL; } de = _devfs_prepare_leaf(&dir, buf, mode); if (!de) { printk(KERN_WARNING "%s: could not prepare leaf for %s\n", - __FUNCTION__, buf); - return -ENOMEM; /* could be more accurate... */ + __FUNCTION__, buf); + return -ENOMEM; /* could be more accurate... */ } de->u.bdev.dev = dev; error = _devfs_append_entry(dir, de, NULL); if (error) { - printk(KERN_WARNING "%s: could not append to parent for %s\n", - __FUNCTION__, buf); + printk(KERN_WARNING + "%s: could not append to parent for %s\n", + __FUNCTION__, buf); goto out; } devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED); - out: +out: devfs_put(dir); return error; } EXPORT_SYMBOL(devfs_mk_bdev); - int devfs_mk_cdev(dev_t dev, umode_t mode, const char *fmt, ...) { struct devfs_entry *dir = NULL, *de; @@ -1498,68 +1558,80 @@ int error, n; va_start(args, fmt); + n = vsnprintf(buf, 64, fmt, args); if (n >= 64 || !buf[0]) { printk(KERN_WARNING "%s: invalid format string\n", - __FUNCTION__); + __FUNCTION__); return -EINVAL; } if (!S_ISCHR(mode)) { printk(KERN_WARNING "%s: invalide mode (%u) for %s\n", - __FUNCTION__, mode, buf); + __FUNCTION__, mode, buf); return -EINVAL; } de = _devfs_prepare_leaf(&dir, buf, mode); if (!de) { printk(KERN_WARNING "%s: could not prepare leaf for %s\n", - __FUNCTION__, buf); - return -ENOMEM; /* could be more accurate... */ + __FUNCTION__, buf); + return -ENOMEM; /* could be more accurate... */ } de->u.cdev.dev = dev; error = _devfs_append_entry(dir, de, NULL); if (error) { - printk(KERN_WARNING "%s: could not append to parent for %s\n", - __FUNCTION__, buf); + printk(KERN_WARNING + "%s: could not append to parent for %s\n", + __FUNCTION__, buf); goto out; } devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED); - out: +out: devfs_put(dir); return error; } EXPORT_SYMBOL(devfs_mk_cdev); - /** * _devfs_unhook - Unhook a device entry from its parents list * @de: The entry to unhook. * * Returns %TRUE if the entry was unhooked, else %FALSE if it was - * previously unhooked. - * The caller must have a write lock on the parent directory. + * previously unhooked. The caller must have a write lock on the + * parent directory. */ - -static int _devfs_unhook (struct devfs_entry *de) +static int _devfs_unhook(struct devfs_entry *de) { - struct devfs_entry *parent; + struct devfs_entry *parent; - if ( !de || (de->prev == de) ) return FALSE; - parent = de->parent; - 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; - de->prev = de; /* Indicate we're unhooked */ - de->next = NULL; /* Force early termination for */ - return TRUE; -} /* End Function _devfs_unhook */ + if (!de || (de->prev == de)) + return FALSE; + parent = de->parent; + + 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; + + /* Indicate we're unhooked */ + de->prev = de; + + /* Force early termination for */ + de->next = NULL; + + return TRUE; +} /** * _devfs_unregister - Unregister a device entry from its parent. @@ -1569,84 +1641,105 @@ * The caller must have a write lock on the parent directory, which is * unlocked by this function. */ +static void _devfs_unregister(struct devfs_entry *dir, struct devfs_entry *de) +{ + int unhooked = _devfs_unhook(de); + + write_unlock(&dir->u.dir.lock); + + if (!unhooked) + return; + + devfs_get(dir); + devfsd_notify(de, DEVFSD_NOTIFY_UNREGISTERED); + free_dentry(de); + devfs_put(dir); + + if (!S_ISDIR(de->mode)) + return; + + /* Recursively unregister: this is a stack chomper */ + while (TRUE) { + struct devfs_entry *child; + + write_lock(&de->u.dir.lock); + de->u.dir.no_more_additions = TRUE; -static void _devfs_unregister (struct devfs_entry *dir, struct devfs_entry *de) + child = de->u.dir.first; + VERIFY_ENTRY(child); + + _devfs_unregister(de, child); + + if (!child) + break; + + DPRINTK(DEBUG_UNREGISTER, + "(%s): child: %p refcount: %d\n", child->name, + child, atomic_read(&child->refcount)); + + devfs_put(child); + } +} + +static int devfs_do_symlink(devfs_handle_t dir, + const char *name, + const char *link, devfs_handle_t * handle) { - int unhooked = _devfs_unhook (de); + int err; + unsigned int linklength; + char *newlink; + struct devfs_entry *de; + + if (handle != NULL) + *handle = NULL; + + if (name == NULL) { + PRINTK("(): NULL name pointer\n"); + return -EINVAL; + } + + if (link == NULL) { + PRINTK("(%s): NULL link pointer\n", name); + return -EINVAL; + } + + linklength = strlen(link); + if ((newlink = kmalloc(linklength + 1, GFP_KERNEL)) == NULL) + return -ENOMEM; + + memcpy(newlink, link, linklength); + newlink[linklength] = '\0'; + + if ((de = _devfs_prepare_leaf(&dir, name, S_IFLNK | S_IRUGO | S_IXUGO)) == NULL) { + PRINTK("(%s): could not prepare leaf\n", name); + kfree(newlink); + return -ENOTDIR; + } + + de->info = NULL; + de->u.symlink.linkname = newlink; + de->u.symlink.length = linklength; + + if ((err = _devfs_append_entry(dir, de, NULL)) != 0) { + PRINTK("(%s): could not append to parent, err: %d\n", name, + err); + devfs_put(dir); + return err; + } + + devfs_put(dir); - write_unlock (&dir->u.dir.lock); - if (!unhooked) return; - devfs_get (dir); - devfsd_notify (de, DEVFSD_NOTIFY_UNREGISTERED); - free_dentry (de); - devfs_put (dir); - if ( !S_ISDIR (de->mode) ) return; - while (TRUE) /* Recursively unregister: this is a stack chomper */ - { - struct devfs_entry *child; - - write_lock (&de->u.dir.lock); - de->u.dir.no_more_additions = TRUE; - child = de->u.dir.first; - VERIFY_ENTRY (child); - _devfs_unregister (de, child); - if (!child) break; - DPRINTK (DEBUG_UNREGISTER, "(%s): child: %p refcount: %d\n", - child->name, child, atomic_read (&child->refcount) ); - devfs_put (child); - } -} /* End Function _devfs_unregister */ - -static int devfs_do_symlink (devfs_handle_t dir, const char *name, - const char *link, devfs_handle_t *handle) -{ - int err; - unsigned int linklength; - char *newlink; - struct devfs_entry *de; - - if (handle != NULL) *handle = NULL; - if (name == NULL) - { - PRINTK ("(): NULL name pointer\n"); - return -EINVAL; - } - if (link == NULL) - { - PRINTK ("(%s): NULL link pointer\n", name); - return -EINVAL; - } - linklength = strlen (link); - if ( ( newlink = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL ) - return -ENOMEM; - memcpy (newlink, link, linklength); - newlink[linklength] = '\0'; - if ( ( de = _devfs_prepare_leaf (&dir, name, S_IFLNK | S_IRUGO | S_IXUGO) ) - == NULL ) - { - PRINTK ("(%s): could not prepare leaf\n", name); - kfree (newlink); - return -ENOTDIR; - } - de->info = NULL; - de->u.symlink.linkname = newlink; - de->u.symlink.length = linklength; - if ( ( err = _devfs_append_entry (dir, de, NULL) ) != 0 ) - { - PRINTK ("(%s): could not append to parent, err: %d\n", name, err); - devfs_put (dir); - return err; - } - devfs_put (dir); #ifdef CONFIG_DEVFS_DEBUG - spin_lock (&stat_lock); - stat_num_bytes += linklength + 1; - spin_unlock (&stat_lock); + spin_lock(&stat_lock); + stat_num_bytes += linklength + 1; + spin_unlock(&stat_lock); #endif - if (handle != NULL) *handle = de; - return 0; -} /* End Function devfs_do_symlink */ + if (handle != NULL) + *handle = de; + + return 0; +} /** * devfs_mk_symlink Create a symbolic link in the devfs namespace. @@ -1655,7 +1748,6 @@ * * Returns 0 on success, else a negative error code is returned. */ - int devfs_mk_symlink(const char *from, const char *to) { devfs_handle_t de; @@ -1670,10 +1762,8 @@ return err; } - /** - * devfs_mk_dir - Create a directory in the devfs namespace. - * new name is relative to the root of the devfs. + * devfs_mk_dir - Create a directory in the devfs namespace relative to the root of the devfs. * @fmt: The name of the entry. * * Use of this function is optional. The devfs_register() function @@ -1681,7 +1771,6 @@ * is provided for efficiency reasons, as it provides a handle to a directory. * On failure %NULL is returned. */ - int devfs_mk_dir(const char *fmt, ...) { struct devfs_entry *dir = NULL, *de = NULL, *old; @@ -1690,6 +1779,7 @@ int error, n; va_start(args, fmt); + n = vsnprintf(buf, 64, fmt, args); if (n >= 64 || !buf[0]) { printk(KERN_WARNING "%s: invalid argument.", __FUNCTION__); @@ -1712,19 +1802,18 @@ goto out_put; } else if (error) { PRINTK("(%s): could not append to dir: %p \"%s\"\n", - buf, dir, dir->name); + buf, dir, dir->name); devfs_put(old); goto out_put; } - + devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED); - out_put: +out_put: devfs_put(dir); return error; } - void devfs_remove(const char *fmt, ...) { char buf[64]; @@ -1732,12 +1821,14 @@ int n; va_start(args, fmt); + n = vsnprintf(buf, 64, fmt, args); if (n < 64 && buf[0]) { devfs_handle_t de = _devfs_find_entry(NULL, buf, 0); if (!de) { - printk(KERN_ERR "%s: %s not found, cannot remove\n", + printk(KERN_ERR + "%s: %s not found, cannot remove\n", __FUNCTION__, buf); dump_stack(); return; @@ -1750,129 +1841,146 @@ } } - /** * devfs_generate_path - Generate a pathname for an entry, relative to the devfs root. * @de: The devfs entry. * @path: The buffer to write the pathname to. The pathname and '\0' - * terminator will be written at the end of the buffer. + * terminator will be written at the end of the buffer. * @buflen: The length of the buffer. * * Returns the offset in the buffer where the pathname starts on success, * else a negative error code. */ - -static int devfs_generate_path (devfs_handle_t de, char *path, int buflen) +static int devfs_generate_path(devfs_handle_t de, char *path, int buflen) { - int pos; -#define NAMEOF(de) ( (de)->mode ? (de)->name : (de)->u.name ) + int pos; + + if (de == NULL) + return -EINVAL; - if (de == NULL) return -EINVAL; - VERIFY_ENTRY (de); - if (de->namelen >= buflen) return -ENAMETOOLONG; /* Must be first */ - path[buflen - 1] = '\0'; - if (de->parent == NULL) return buflen - 1; /* Don't prepend root */ - pos = buflen - de->namelen - 1; - memcpy (path + pos, NAMEOF (de), de->namelen); - for (de = de->parent; de->parent != NULL; de = de->parent) - { - if (pos - de->namelen - 1 < 0) return -ENAMETOOLONG; - path[--pos] = '/'; - pos -= de->namelen; - memcpy (path + pos, NAMEOF (de), de->namelen); - } - return pos; -} /* End Function devfs_generate_path */ + VERIFY_ENTRY(de); + if (de->namelen >= buflen) + return -ENAMETOOLONG; + + path[buflen - 1] = '\0'; + + /* Don't prepend root */ + if (de->parent == NULL) + return buflen - 1; + + pos = buflen - de->namelen - 1; + memcpy(path + pos, NAMEOF(de), de->namelen); + + for (de = de->parent; de->parent != NULL; de = de->parent) { + if (pos - de->namelen - 1 < 0) + return -ENAMETOOLONG; + + path[--pos] = '/'; + pos -= de->namelen; + memcpy(path + pos, NAMEOF(de), de->namelen); + } + + return pos; +} /** * devfs_get_ops - Get the device operations for a devfs entry. * @de: The handle to the device entry. * * Returns a pointer to the device operations on success, else NULL. - * The use count for the module owning the operations will be incremented. + * The use count for the module owning the operations will be + * incremented. */ - -static struct file_operations *devfs_get_ops (devfs_handle_t de) +static struct file_operations * devfs_get_ops(devfs_handle_t de) { - struct file_operations *ops = de->u.cdev.ops; - struct module *owner; + struct file_operations *ops = de->u.cdev.ops; + struct module *owner; + + if (!ops) + return NULL; + + owner = ops->owner; - if (!ops) - return NULL; - owner = ops->owner; - read_lock (&de->parent->u.dir.lock); /* Prevent module from unloading */ - if ( (de->next == de) || !try_module_get (owner) ) - { /* Entry is already unhooked or module is unloading */ - read_unlock (&de->parent->u.dir.lock); - return NULL; - } - read_unlock (&de->parent->u.dir.lock); /* Module can continue unloading*/ - return ops; -} /* End Function devfs_get_ops */ + /* Prevent module from unloading */ + read_lock(&de->parent->u.dir.lock); + if ((de->next == de) || !try_module_get(owner)) { + /* Entry is already unhooked or module is unloading */ + read_unlock(&de->parent->u.dir.lock); + return NULL; + } + /* Module can continue unloading */ + read_unlock(&de->parent->u.dir.lock); + + return ops; +} /** * devfs_setup - Process kernel boot options. * @str: The boot options after the "devfs=". */ - -static int __init devfs_setup (char *str) +static int __init devfs_setup(char *str) { - static struct - { - char *name; - unsigned int mask; - unsigned int *opt; - } devfs_options_tab[] __initdata = - { + + static struct { + 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}, - {"dfree", DEBUG_FREE, &devfs_debug_init}, - {"diget", DEBUG_I_GET, &devfs_debug_init}, - {"dchange", DEBUG_SET_FLAGS, &devfs_debug_init}, - {"dsread", DEBUG_S_READ, &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 */ - {"mount", OPTION_MOUNT, &boot_options}, - {NULL, 0, NULL} - }; - - while ( (*str != '\0') && !isspace (*str) ) - { - int i, found = 0, invert = 0; - - if (strncmp (str, "no", 2) == 0) - { - invert = 1; - str += 2; - } - for (i = 0; devfs_options_tab[i].name != NULL; i++) - { - 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; - } + {"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}, + {"dfree", DEBUG_FREE, &devfs_debug_init}, + {"diget", DEBUG_I_GET, &devfs_debug_init}, + {"dchange", DEBUG_SET_FLAGS, &devfs_debug_init}, + {"dsread", DEBUG_S_READ, &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 */ + {"mount", OPTION_MOUNT, &boot_options}, + {NULL, 0, NULL} + }; + + while ((*str != '\0') && !isspace(*str)) { + int i, found = 0, invert = 0; + + if (strncmp(str, "no", 2) == 0) { + invert = 1; + str += 2; + } + + for (i = 0; devfs_options_tab[i].name != NULL; i++) { + 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; + } + } + + if (!found) + return 0; + + /* No more options */ + if (*str != ',') + return 0; + + ++str; } - if (!found) return 0; /* No match */ - if (*str != ',') return 0; /* No more options */ - ++str; - } - return 1; -} /* End Function devfs_setup */ + + return 1; +} __setup("devfs=", devfs_setup); @@ -1881,7 +1989,6 @@ EXPORT_SYMBOL(devfs_mk_dir); EXPORT_SYMBOL(devfs_remove); - /** * try_modload - Notify devfsd of an inode lookup by a non-devfsd process. * @parent: The parent devfs entry. @@ -1893,27 +2000,31 @@ * * Returns 0 on success (event was queued), else a negative error code. */ +static int try_modload(struct devfs_entry *parent, + struct fs_info *fs_info, const char *name, + unsigned namelen, struct devfs_entry *buf) +{ + if (!(fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP))) + return -ENOENT; + + if (is_devfsd_or_child(fs_info)) + return -ENOENT; + + memset(buf, 0, sizeof *buf); + atomic_set(&buf->refcount, 1); + buf->parent = parent; + buf->namelen = namelen; + buf->u.name = name; + + WRITE_ENTRY_MAGIC(buf, MAGIC_VALUE); + + if (!devfsd_notify_de(buf, DEVFSD_NOTIFY_LOOKUP, 0, + current->euid, current->egid, fs_info)) + return -ENOENT; -static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info, - const char *name, unsigned namelen, - struct devfs_entry *buf) -{ - if ( !( fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP) ) ) - return -ENOENT; - if ( is_devfsd_or_child (fs_info) ) return -ENOENT; - memset (buf, 0, sizeof *buf); - atomic_set (&buf->refcount, 1); - buf->parent = parent; - buf->namelen = namelen; - buf->u.name = name; - WRITE_ENTRY_MAGIC (buf, MAGIC_VALUE); - if ( !devfsd_notify_de (buf, DEVFSD_NOTIFY_LOOKUP, 0, - current->euid, current->egid, fs_info) ) - return -ENOENT; - /* Possible success: event has been queued */ - return 0; -} /* End Function try_modload */ - + /* Possible success: event has been queued */ + return 0; +} /* Superblock operations follow */ @@ -1923,44 +2034,55 @@ static struct file_operations devfs_dir_fops; static struct inode_operations devfs_symlink_iops; -static int devfs_notify_change (struct dentry *dentry, struct iattr *iattr) +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->s_fs_info; - - de = get_devfs_entry_from_vfs_inode (inode); - 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; - DPRINTK (DEBUG_I_CHANGE, "(%d): VFS inode: %p devfs_entry: %p\n", - (int) inode->i_ino, inode, de); - DPRINTK (DEBUG_I_CHANGE, "(): mode: 0%o uid: %d gid: %d\n", - (int) inode->i_mode, (int) inode->i_uid, (int) inode->i_gid); - /* Inode is not on hash chains, thus must save permissions here rather - than in a write_inode() method */ - de->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; - if ( ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) ) && - !is_devfsd_or_child (fs_info) ) - devfsd_notify_de (de, DEVFSD_NOTIFY_CHANGE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - return 0; -} /* End Function devfs_notify_change */ - -static struct super_operations devfs_sops = -{ - .drop_inode = generic_delete_inode, - .statfs = simple_statfs, -}; + int retval; + struct devfs_entry *de; + struct inode *inode = dentry->d_inode; + struct fs_info *fs_info = inode->i_sb->s_fs_info; + + de = get_devfs_entry_from_vfs_inode(inode); + 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; + + DPRINTK(DEBUG_I_CHANGE, "(%d): VFS inode: %p devfs_entry: %p\n", + (int) inode->i_ino, inode, de); + + DPRINTK(DEBUG_I_CHANGE, "(): mode: 0%o uid: %d gid: %d\n", + (int) inode->i_mode, (int) inode->i_uid, + (int) inode->i_gid); + + /* + * Inode is not on hash chains, thus must save permissions here + * rather than in a write_inode() method + */ + de->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; + + if ((iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) && + !is_devfsd_or_child(fs_info)) + devfsd_notify_de(de, DEVFSD_NOTIFY_CHANGE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + + return 0; +} +static struct super_operations devfs_sops = { + .drop_inode = generic_delete_inode, + .statfs = simple_statfs, +}; /** * _devfs_get_vfs_inode - Get a VFS inode. @@ -1971,878 +2093,1094 @@ * Returns the inode on success, else %NULL. An implicit devfs_get() is * performed if the inode is created. */ - -static struct inode *_devfs_get_vfs_inode (struct super_block *sb, +static struct inode * _devfs_get_vfs_inode(struct super_block *sb, struct devfs_entry *de, struct dentry *dentry) { - struct inode *inode; + struct inode *inode; + + if (de->prev == de) + return NULL; /* Quick check to see if unhooked */ + + if ((inode = new_inode(sb)) == NULL) { + PRINTK("(%s): new_inode() failed, de: %p\n", de->name, de); + return NULL; + } - if (de->prev == de) return NULL; /* Quick check to see if unhooked */ - if ( ( inode = new_inode (sb) ) == NULL ) - { - PRINTK ("(%s): new_inode() failed, de: %p\n", de->name, de); - return NULL; - } - if (de->parent) - { - read_lock (&de->parent->u.dir.lock); - if (de->prev != de) de->inode.dentry = dentry; /* Not unhooked */ - read_unlock (&de->parent->u.dir.lock); - } - else de->inode.dentry = dentry; /* Root: no locking needed */ - if (de->inode.dentry != dentry) - { /* Must have been unhooked */ - iput (inode); - return NULL; - } - inode->u.generic_ip = devfs_get (de); - inode->i_ino = de->inode.ino; - DPRINTK (DEBUG_I_GET, "(%d): VFS inode: %p devfs_entry: %p\n", - (int) inode->i_ino, inode, de); - inode->i_blocks = 0; - inode->i_blksize = FAKE_BLOCK_SIZE; - inode->i_op = &devfs_iops; - inode->i_fop = &devfs_fops; - if ( S_ISCHR (de->mode) ) - { - inode->i_rdev = de->u.cdev.dev; - } - else if ( S_ISBLK (de->mode) ) - init_special_inode(inode, de->mode, de->u.bdev.dev); - else if ( S_ISFIFO (de->mode) ) - inode->i_fop = &def_fifo_fops; - else if ( S_ISDIR (de->mode) ) - { - inode->i_op = &devfs_dir_iops; - inode->i_fop = &devfs_dir_fops; - } - else if ( S_ISLNK (de->mode) ) - { - inode->i_op = &devfs_symlink_iops; - inode->i_size = de->u.symlink.length; - } - inode->i_mode = de->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; - DPRINTK (DEBUG_I_GET, "(): mode: 0%o uid: %d gid: %d\n", - (int) inode->i_mode, (int) inode->i_uid, (int) inode->i_gid); - return inode; -} /* End Function _devfs_get_vfs_inode */ - - -/* File operations for device entries follow */ - -static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) -{ - int err, count; - int stored = 0; - struct fs_info *fs_info; - struct devfs_entry *parent, *de, *next = NULL; - struct inode *inode = file->f_dentry->d_inode; - - fs_info = inode->i_sb->s_fs_info; - parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode); - if ( (long) file->f_pos < 0 ) return -EINVAL; - DPRINTK (DEBUG_F_READDIR, "(%s): fs_info: %p pos: %ld\n", - parent->name, fs_info, (long) file->f_pos); - switch ( (long) file->f_pos ) - { - case 0: - err = (*filldir) (dirent, "..", 2, file->f_pos, - parent_ino (file->f_dentry), DT_DIR); - if (err == -EINVAL) break; - if (err < 0) return err; - file->f_pos++; - ++stored; - /* Fall through */ - case 1: - err = (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino, DT_DIR); - if (err == -EINVAL) break; - if (err < 0) return err; - file->f_pos++; - ++stored; - /* Fall through */ - default: - /* Skip entries */ - count = file->f_pos - 2; - read_lock (&parent->u.dir.lock); - for (de = parent->u.dir.first; de && (count > 0); de = de->next) - --count; - devfs_get (de); - read_unlock (&parent->u.dir.lock); - /* Now add all remaining entries */ - while (de) - { - err = (*filldir) (dirent, de->name, de->namelen, - file->f_pos, de->inode.ino, de->mode >> 12); - if (err < 0) devfs_put (de); - else - { - file->f_pos++; - ++stored; - } - if (err == -EINVAL) break; - if (err < 0) return err; - read_lock (&parent->u.dir.lock); - next = devfs_get (de->next); - read_unlock (&parent->u.dir.lock); - devfs_put (de); - de = next; - } - break; - } - return stored; -} /* End Function devfs_readdir */ - -static int devfs_open (struct inode *inode, struct file *file) -{ - int err = -ENODEV; - struct devfs_entry *de; - struct file_operations *ops; - - de = get_devfs_entry_from_vfs_inode (inode); - if (de == NULL) return -ENODEV; - if ( S_ISDIR (de->mode) ) return 0; - file->private_data = de->info; - if (S_ISCHR(inode->i_mode)) { - ops = devfs_get_ops (de); /* Now have module refcount */ - file->f_op = ops; - if (file->f_op) - { - lock_kernel (); - err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0; - unlock_kernel (); + if (de->parent) { + read_lock(&de->parent->u.dir.lock); + /* Not unhooked */ + if (de->prev != de) + de->inode.dentry = dentry; + read_unlock(&de->parent->u.dir.lock); + } else + /* Root: no locking needed */ + de->inode.dentry = dentry; + + /* Must have been unhooked */ + if (de->inode.dentry != dentry) { + iput(inode); + return NULL; } - else - err = chrdev_open (inode, file); - } - return err; -} /* End Function devfs_open */ -static struct file_operations devfs_fops = + inode->u.generic_ip = devfs_get(de); + inode->i_ino = de->inode.ino; + + DPRINTK(DEBUG_I_GET, "(%d): VFS inode: %p devfs_entry: %p\n", + (int) inode->i_ino, inode, de); + + inode->i_blocks = 0; + inode->i_blksize = FAKE_BLOCK_SIZE; + inode->i_op = &devfs_iops; + inode->i_fop = &devfs_fops; + + if (S_ISCHR(de->mode)) { + inode->i_rdev = de->u.cdev.dev; + } else if (S_ISBLK(de->mode)) + init_special_inode(inode, de->mode, de->u.bdev.dev); + else if (S_ISFIFO(de->mode)) + inode->i_fop = &def_fifo_fops; + else if (S_ISDIR(de->mode)) { + inode->i_op = &devfs_dir_iops; + inode->i_fop = &devfs_dir_fops; + } else if (S_ISLNK(de->mode)) { + inode->i_op = &devfs_symlink_iops; + inode->i_size = de->u.symlink.length; + } + + inode->i_mode = de->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; + + DPRINTK(DEBUG_I_GET, "(): mode: 0%o uid: %d gid: %d\n", + (int) inode->i_mode, (int) inode->i_uid, + (int) inode->i_gid); + + return inode; +} + +/* File operations for device entries follow */ + +static int devfs_readdir(struct file *file, void *dirent, filldir_t filldir) { - .open = devfs_open, -}; + int err, count; + int stored = 0; + struct fs_info *fs_info; + struct devfs_entry *parent, *de, *next = NULL; + struct inode *inode = file->f_dentry->d_inode; + + fs_info = inode->i_sb->s_fs_info; + parent = get_devfs_entry_from_vfs_inode(file->f_dentry->d_inode); + + if ((long) file->f_pos < 0) + return -EINVAL; + + DPRINTK(DEBUG_F_READDIR, "(%s): fs_info: %p pos: %ld\n", + parent->name, fs_info, (long) file->f_pos); -static struct file_operations devfs_dir_fops = + switch ((long) file->f_pos) { + case 0: + err = (*filldir) (dirent, "..", 2, file->f_pos, + parent_ino(file->f_dentry), DT_DIR); + if (err == -EINVAL) + break; + if (err < 0) + return err; + file->f_pos++; + ++stored; + /* Fall through */ + case 1: + err = (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino, DT_DIR); + if (err == -EINVAL) + break; + if (err < 0) + return err; + file->f_pos++; + ++stored; + /* Fall through */ + default: + /* Skip entries */ + count = file->f_pos - 2; + read_lock(&parent->u.dir.lock); + for (de = parent->u.dir.first; de && (count > 0); de = de->next) + --count; + devfs_get(de); + read_unlock(&parent->u.dir.lock); + + /* Now add all remaining entries */ + while (de) { + err = (*filldir) (dirent, de->name, de->namelen, + file->f_pos, de->inode.ino, + de->mode >> 12); + if (err < 0) + devfs_put(de); + else { + file->f_pos++; + ++stored; + } + + if (err == -EINVAL) + break; + if (err < 0) + return err; + + read_lock(&parent->u.dir.lock); + next = devfs_get(de->next); + read_unlock(&parent->u.dir.lock); + devfs_put(de); + de = next; + } + break; + } + + return stored; +} + +static int devfs_open(struct inode *inode, struct file *file) { - .read = generic_read_dir, - .readdir = devfs_readdir, - .open = devfs_open, -}; + int err = -ENODEV; + struct devfs_entry *de; + struct file_operations *ops; + de = get_devfs_entry_from_vfs_inode(inode); + if (de == NULL) + return -ENODEV; -/* Dentry operations for device entries follow */ + if (S_ISDIR(de->mode)) + return 0; + file->private_data = de->info; + + if (S_ISCHR(inode->i_mode)) { + /* Now have module refcount */ + ops = devfs_get_ops(de); + file->f_op = ops; + + if (file->f_op) { + lock_kernel(); + err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0; + unlock_kernel(); + } else + err = chrdev_open(inode, file); + } + + return err; +} + +static struct file_operations devfs_fops = { + .open = devfs_open, +}; + +static struct file_operations devfs_dir_fops = { + .read = generic_read_dir, + .readdir = devfs_readdir, + .open = devfs_open, +}; + +/* Dentry operations for device entries follow */ /** * devfs_d_release - Callback for when a dentry is freed. * @dentry: The dentry. */ - -static void devfs_d_release (struct dentry *dentry) +static void devfs_d_release(struct dentry *dentry) { - DPRINTK (DEBUG_D_RELEASE, "(%p): inode: %p\n", dentry, dentry->d_inode); -} /* End Function devfs_d_release */ + DPRINTK(DEBUG_D_RELEASE, "(%p): inode: %p\n", dentry, + dentry->d_inode); +} /** * devfs_d_iput - Callback for when a dentry loses its inode. * @dentry: The dentry. - * @inode: The inode. + * @inode: The inode. */ - -static void devfs_d_iput (struct dentry *dentry, struct inode *inode) +static void devfs_d_iput(struct dentry *dentry, struct inode *inode) { - struct devfs_entry *de; + struct devfs_entry *de; - de = get_devfs_entry_from_vfs_inode (inode); - DPRINTK (DEBUG_D_IPUT,"(%s): dentry: %p inode: %p de: %p de->dentry: %p\n", - de->name, dentry, inode, de, de->inode.dentry); - if ( de->inode.dentry && (de->inode.dentry != dentry) ) - OOPS ("(%s): de: %p dentry: %p de->dentry: %p\n", - de->name, de, dentry, de->inode.dentry); - de->inode.dentry = NULL; - iput (inode); - devfs_put (de); -} /* End Function devfs_d_iput */ + de = get_devfs_entry_from_vfs_inode(inode); + DPRINTK(DEBUG_D_IPUT, + "(%s): dentry: %p inode: %p de: %p de->dentry: %p\n", + de->name, dentry, inode, de, de->inode.dentry); -static int devfs_d_delete (struct dentry *dentry); + if (de->inode.dentry && (de->inode.dentry != dentry)) + OOPS("(%s): de: %p dentry: %p de->dentry: %p\n", + de->name, de, dentry, de->inode.dentry); -static struct dentry_operations devfs_dops = -{ - .d_delete = devfs_d_delete, - .d_release = devfs_d_release, - .d_iput = devfs_d_iput, + de->inode.dentry = NULL; + iput(inode); + devfs_put(de); +} + +static int devfs_d_delete(struct dentry *dentry); + +static struct dentry_operations devfs_dops = { + .d_delete = devfs_d_delete, + .d_release = devfs_d_release, + .d_iput = devfs_d_iput, }; -static int devfs_d_revalidate_wait (struct dentry *dentry, struct nameidata *); +static int devfs_d_revalidate_wait(struct dentry *dentry, struct nameidata *); -static struct dentry_operations devfs_wait_dops = -{ - .d_delete = devfs_d_delete, - .d_release = devfs_d_release, - .d_iput = devfs_d_iput, - .d_revalidate = devfs_d_revalidate_wait, +static struct dentry_operations devfs_wait_dops = { + .d_delete = devfs_d_delete, + .d_release = devfs_d_release, + .d_iput = devfs_d_iput, + .d_revalidate = devfs_d_revalidate_wait, }; /** * devfs_d_delete - Callback for when all files for a dentry are closed. * @dentry: The dentry. */ - -static int devfs_d_delete (struct dentry *dentry) +static int devfs_d_delete(struct dentry *dentry) { - struct inode *inode = dentry->d_inode; + struct inode *inode = dentry->d_inode; - if (dentry->d_op == &devfs_wait_dops) dentry->d_op = &devfs_dops; - /* Unhash dentry if negative (has no inode) */ - if (inode == NULL) - { - DPRINTK (DEBUG_D_DELETE, "(%p): dropping negative dentry\n", dentry); - return 1; - } - return 0; -} /* End Function devfs_d_delete */ + if (dentry->d_op == &devfs_wait_dops) + dentry->d_op = &devfs_dops; -struct devfs_lookup_struct -{ - devfs_handle_t de; - wait_queue_head_t wait_queue; + /* Unhash dentry if negative (has no inode) */ + if (inode == NULL) { + DPRINTK(DEBUG_D_DELETE, "(%p): dropping negative dentry\n", + dentry); + return 1; + } + + return 0; +} + +struct devfs_lookup_struct { + devfs_handle_t de; + wait_queue_head_t wait_queue; }; /* XXX: this doesn't handle the case where we got a negative dentry but a devfs entry has been registered in the meanwhile */ -static int devfs_d_revalidate_wait (struct dentry *dentry, struct nameidata *nd) +static int devfs_d_revalidate_wait(struct dentry *dentry, struct nameidata *nd) { - struct inode *dir = dentry->d_parent->d_inode; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir); - struct devfs_lookup_struct *lookup_info = dentry->d_fsdata; - DECLARE_WAITQUEUE (wait, current); - - if ( is_devfsd_or_child (fs_info) ) - { - devfs_handle_t de = lookup_info->de; - struct inode *inode; + struct inode *dir = dentry->d_parent->d_inode; + struct fs_info *fs_info = dir->i_sb->s_fs_info; + devfs_handle_t parent = get_devfs_entry_from_vfs_inode(dir); + struct devfs_lookup_struct *lookup_info = dentry->d_fsdata; + DECLARE_WAITQUEUE(wait, current); + + if (is_devfsd_or_child(fs_info)) { + devfs_handle_t de = lookup_info->de; + struct inode *inode; + + DPRINTK(DEBUG_I_LOOKUP, + "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n", + dentry->d_name.name, dentry, dentry->d_inode, de, + current->comm); + + if (dentry->d_inode) + return 1; + + if (de == NULL) { + read_lock(&parent->u.dir.lock); + de = _devfs_search_dir(parent, dentry->d_name.name, + dentry->d_name.len); + read_unlock(&parent->u.dir.lock); + + if (de == NULL) + return 1; + + lookup_info->de = de; + } + + /* + * Create an inode, now that the driver + * information is available + */ + inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry); + if (!inode) + return 1; + + DPRINTK(DEBUG_I_LOOKUP, + "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n", + de->name, de->inode.ino, inode, de, current->comm); + + d_instantiate(dentry, inode); + + return 1; + } + + if (lookup_info == NULL) + return 1; + + read_lock(&parent->u.dir.lock); + if (dentry->d_fsdata) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&lookup_info->wait_queue, &wait); + read_unlock(&parent->u.dir.lock); + schedule(); + /* + * This does not need nor should remove wait from wait_queue. + * Wait queue head is never reused - nothing is ever added + * to it after all waiters have been waked up and head + * itself disappears very soon after it. Moreover it is + * local variable on stack that is likely to have already + * disappeared so any reference to it at this point is buggy. + */ + } else + read_unlock(&parent->u.dir.lock); - DPRINTK (DEBUG_I_LOOKUP, - "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n", - dentry->d_name.name, dentry, dentry->d_inode, de, - current->comm); - if (dentry->d_inode) return 1; - if (de == NULL) - { - read_lock (&parent->u.dir.lock); - de = _devfs_search_dir (parent, dentry->d_name.name, - dentry->d_name.len); - read_unlock (&parent->u.dir.lock); - if (de == NULL) return 1; - lookup_info->de = de; - } - /* Create an inode, now that the driver information is available */ - inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry); - if (!inode) return 1; - DPRINTK (DEBUG_I_LOOKUP, - "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n", - de->name, de->inode.ino, inode, de, current->comm); - d_instantiate (dentry, inode); return 1; - } - if (lookup_info == NULL) return 1; /* Early termination */ - read_lock (&parent->u.dir.lock); - if (dentry->d_fsdata) - { - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&lookup_info->wait_queue, &wait); - read_unlock (&parent->u.dir.lock); - schedule (); +} + +/* Inode operations for device entries follow */ + +static struct dentry * devfs_lookup(struct inode *dir, + struct dentry *dentry, struct nameidata *nd) +{ + /* Must stay in scope until devfsd idle again */ + struct devfs_entry tmp; + struct devfs_lookup_struct lookup_info; + struct fs_info *fs_info = dir->i_sb->s_fs_info; + struct devfs_entry *parent, *de; + struct inode *inode; + struct dentry *retval = NULL; + + /* + * Set up the dentry operations before anything else, + * to ensure cleaning up on any error + */ + dentry->d_op = &devfs_dops; + + /* First try to get the devfs entry for this directory */ + parent = get_devfs_entry_from_vfs_inode(dir); + + DPRINTK(DEBUG_I_LOOKUP, "(%s): dentry: %p parent: %p by: \"%s\"\n", + dentry->d_name.name, dentry, parent, current->comm); + + if (parent == NULL) + return ERR_PTR(-ENOENT); + + read_lock(&parent->u.dir.lock); + de = _devfs_search_dir(parent, dentry->d_name.name, + dentry->d_name.len); + read_unlock(&parent->u.dir.lock); + + lookup_info.de = de; + + init_waitqueue_head(&lookup_info.wait_queue); + dentry->d_fsdata = &lookup_info; + + if (de == NULL) { + /* + * Try with devfsd. For any kind of failure, leave a negative + * dentry so someone else can deal with it (in the case where + * the sysadmin does a mknod()). It's important to do this + * before hashing the dentry, so that the devfsd queue is + * filled before revalidates can start + */ + if (try_modload(parent, fs_info, + dentry->d_name.name, dentry->d_name.len, &tmp) < 0) { + /* Lookup event was not queued to devfsd */ + d_add(dentry, NULL); + return NULL; + } + } + + dentry->d_op = &devfs_wait_dops; + d_add(dentry, NULL); + /* - * This does not need nor should remove wait from wait_queue. - * Wait queue head is never reused - nothing is ever added to it - * after all waiters have been waked up and head itself disappears - * very soon after it. Moreover it is local variable on stack that - * is likely to have already disappeared so any reference to it - * at this point is buggy. + * Unlock directory semaphore, which will release any waiters. They + * will get the hashed dentry, and may be forced to wait for + * revalidation */ + up(&dir->i_sem); + + /* If I'm not devfsd, must wait */ + wait_for_devfsd_finished(fs_info); + de = lookup_info.de; + + /* + * If someone else has been so kind as to make the inode + * we go home early. + */ + if (dentry->d_inode) + goto out; - } - else read_unlock (&parent->u.dir.lock); - return 1; -} /* End Function devfs_d_revalidate_wait */ - - -/* Inode operations for device entries follow */ - -static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry, struct nameidata *nd) -{ - struct devfs_entry tmp; /* Must stay in scope until devfsd idle again */ - struct devfs_lookup_struct lookup_info; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct devfs_entry *parent, *de; - struct inode *inode; - struct dentry *retval = NULL; - - /* Set up the dentry operations before anything else, to ensure cleaning - up on any error */ - dentry->d_op = &devfs_dops; - /* First try to get the devfs entry for this directory */ - parent = get_devfs_entry_from_vfs_inode (dir); - DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p parent: %p by: \"%s\"\n", - dentry->d_name.name, dentry, parent, current->comm); - if (parent == NULL) return ERR_PTR (-ENOENT); - read_lock (&parent->u.dir.lock); - de = _devfs_search_dir (parent, dentry->d_name.name, dentry->d_name.len); - read_unlock (&parent->u.dir.lock); - lookup_info.de = de; - init_waitqueue_head (&lookup_info.wait_queue); - dentry->d_fsdata = &lookup_info; - if (de == NULL) - { /* Try with devfsd. For any kind of failure, leave a negative dentry - so someone else can deal with it (in the case where the sysadmin - does a mknod()). It's important to do this before hashing the - dentry, so that the devfsd queue is filled before revalidates - can start */ - if (try_modload (parent, fs_info, - dentry->d_name.name, dentry->d_name.len, &tmp) < 0) - { /* Lookup event was not queued to devfsd */ - d_add (dentry, NULL); - return NULL; - } - } - dentry->d_op = &devfs_wait_dops; - d_add (dentry, NULL); /* Open the floodgates */ - /* Unlock directory semaphore, which will release any waiters. They - will get the hashed dentry, and may be forced to wait for - revalidation */ - up (&dir->i_sem); - wait_for_devfsd_finished (fs_info); /* If I'm not devfsd, must wait */ - de = lookup_info.de; - /* If someone else has been so kind as to make the inode, we go home - early */ - if (dentry->d_inode) goto out; - if (de == NULL) - { - read_lock (&parent->u.dir.lock); - de = _devfs_search_dir (parent, dentry->d_name.name, - dentry->d_name.len); - read_unlock (&parent->u.dir.lock); - if (de == NULL) goto out; - /* OK, there's an entry now, but no VFS inode yet */ - } - /* Create an inode, now that the driver information is available */ - inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry); - if (!inode) - { - retval = ERR_PTR (-ENOMEM); - goto out; - } - DPRINTK (DEBUG_I_LOOKUP, "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n", - de->name, de->inode.ino, inode, de, current->comm); - d_instantiate (dentry, inode); + if (de == NULL) { + read_lock(&parent->u.dir.lock); + de = _devfs_search_dir(parent, dentry->d_name.name, + dentry->d_name.len); + read_unlock(&parent->u.dir.lock); + if (de == NULL) + goto out; + /* OK, there's an entry now, but no VFS inode yet */ + } + + /* Create an inode, now that the driver information is available */ + inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry); + if (!inode) { + retval = ERR_PTR(-ENOMEM); + goto out; + } + + DPRINTK(DEBUG_I_LOOKUP, + "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n", + de->name, de->inode.ino, inode, de, current->comm); + + d_instantiate(dentry, inode); out: - write_lock (&parent->u.dir.lock); - dentry->d_op = &devfs_dops; - dentry->d_fsdata = NULL; - wake_up (&lookup_info.wait_queue); - write_unlock (&parent->u.dir.lock); - down (&dir->i_sem); /* Grab it again because them's the rules */ - devfs_put (de); - return retval; -} /* End Function devfs_lookup */ - -static int devfs_unlink (struct inode *dir, struct dentry *dentry) -{ - int unhooked; - struct devfs_entry *de; - struct inode *inode = dentry->d_inode; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - - de = get_devfs_entry_from_vfs_inode (inode); - DPRINTK (DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de); - if (de == NULL) return -ENOENT; - if (!de->vfs_deletable) return -EPERM; - write_lock (&de->parent->u.dir.lock); - unhooked = _devfs_unhook (de); - write_unlock (&de->parent->u.dir.lock); - if (!unhooked) return -ENOENT; - if ( !is_devfsd_or_child (fs_info) ) - devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - free_dentry (de); - devfs_put (de); - return 0; -} /* End Function devfs_unlink */ - -static int devfs_symlink (struct inode *dir, struct dentry *dentry, - const char *symname) -{ - int err; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct devfs_entry *parent, *de; - struct inode *inode; - - /* First try to get the devfs entry for this directory */ - parent = get_devfs_entry_from_vfs_inode (dir); - if (parent == NULL) return -ENOENT; - err = devfs_do_symlink (parent, dentry->d_name.name, symname, &de); - DPRINTK (DEBUG_DISABLED, "(%s): errcode from : %d\n", - dentry->d_name.name, err); - if (err < 0) return err; - de->vfs_deletable = TRUE; - de->inode.uid = current->euid; - de->inode.gid = current->egid; - de->inode.atime = CURRENT_TIME; - de->inode.mtime = CURRENT_TIME; - de->inode.ctime = CURRENT_TIME; - if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) - return -ENOMEM; - DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n", - dentry->d_name.name, de->inode.ino, inode, dentry); - d_instantiate (dentry, inode); - if ( !is_devfsd_or_child (fs_info) ) - devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - return 0; -} /* End Function devfs_symlink */ - -static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode) -{ - int err; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct devfs_entry *parent, *de; - struct inode *inode; - - mode = (mode & ~S_IFMT) | S_IFDIR; /* VFS doesn't pass S_IFMT part */ - parent = get_devfs_entry_from_vfs_inode (dir); - if (parent == NULL) return -ENOENT; - de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode); - if (!de) return -ENOMEM; - de->vfs_deletable = TRUE; - if ( ( err = _devfs_append_entry (parent, de, NULL) ) != 0 ) - return err; - de->inode.uid = current->euid; - de->inode.gid = current->egid; - de->inode.atime = CURRENT_TIME; - de->inode.mtime = CURRENT_TIME; - de->inode.ctime = CURRENT_TIME; - if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) - return -ENOMEM; - DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n", - dentry->d_name.name, de->inode.ino, inode, dentry); - d_instantiate (dentry, inode); - if ( !is_devfsd_or_child (fs_info) ) - devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - return 0; -} /* End Function devfs_mkdir */ - -static int devfs_rmdir (struct inode *dir, struct dentry *dentry) -{ - int err = 0; - struct devfs_entry *de; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct inode *inode = dentry->d_inode; - - if (dir->i_sb->s_fs_info != inode->i_sb->s_fs_info) return -EINVAL; - de = get_devfs_entry_from_vfs_inode (inode); - if (de == NULL) return -ENOENT; - if ( !S_ISDIR (de->mode) ) return -ENOTDIR; - if (!de->vfs_deletable) return -EPERM; - /* First ensure the directory is empty and will stay that way */ - write_lock (&de->u.dir.lock); - if (de->u.dir.first) err = -ENOTEMPTY; - else de->u.dir.no_more_additions = TRUE; - write_unlock (&de->u.dir.lock); - if (err) return err; - /* Now unhook the directory from its parent */ - write_lock (&de->parent->u.dir.lock); - if ( !_devfs_unhook (de) ) err = -ENOENT; - write_unlock (&de->parent->u.dir.lock); - if (err) return err; - if ( !is_devfsd_or_child (fs_info) ) - devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - free_dentry (de); - devfs_put (de); - return 0; -} /* End Function devfs_rmdir */ - -static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode, - dev_t rdev) -{ - int err; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct devfs_entry *parent, *de; - struct inode *inode; - - DPRINTK (DEBUG_I_MKNOD, "(%s): mode: 0%o dev: %u:%u\n", - dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); - parent = get_devfs_entry_from_vfs_inode (dir); - if (parent == NULL) return -ENOENT; - de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode); - if (!de) return -ENOMEM; - de->vfs_deletable = TRUE; - if (S_ISCHR (mode)) - de->u.cdev.dev = rdev; - else if (S_ISBLK (mode)) - de->u.bdev.dev = rdev; - if ( ( err = _devfs_append_entry (parent, de, NULL) ) != 0 ) + write_lock(&parent->u.dir.lock); + dentry->d_op = &devfs_dops; + dentry->d_fsdata = NULL; + wake_up(&lookup_info.wait_queue); + write_unlock(&parent->u.dir.lock); + + /* Grab it again because them's the rules */ + down(&dir->i_sem); + devfs_put(de); + + return retval; +} + +static int devfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int unhooked; + struct devfs_entry *de; + struct inode *inode = dentry->d_inode; + struct fs_info *fs_info = dir->i_sb->s_fs_info; + + de = get_devfs_entry_from_vfs_inode(inode); + + DPRINTK(DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de); + + if (de == NULL) + return -ENOENT; + + if (!de->vfs_deletable) + return -EPERM; + + write_lock(&de->parent->u.dir.lock); + unhooked = _devfs_unhook(de); + write_unlock(&de->parent->u.dir.lock); + if (!unhooked) + return -ENOENT; + + if (!is_devfsd_or_child(fs_info)) + devfsd_notify_de(de, DEVFSD_NOTIFY_DELETE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + + free_dentry(de); + devfs_put(de); + + return 0; +} + +static int devfs_symlink(struct inode *dir, + struct dentry *dentry, const char *symname) +{ + int err; + struct fs_info *fs_info = dir->i_sb->s_fs_info; + struct devfs_entry *parent, *de; + struct inode *inode; + + /* First try to get the devfs entry for this directory */ + parent = get_devfs_entry_from_vfs_inode(dir); + if (parent == NULL) + return -ENOENT; + + err = devfs_do_symlink(parent, dentry->d_name.name, symname, &de); + + DPRINTK(DEBUG_DISABLED, + "(%s): errcode from : %d\n", + dentry->d_name.name, err); + + if (err < 0) + return err; + + de->vfs_deletable = TRUE; + de->inode.uid = current->euid; + de->inode.gid = current->egid; + de->inode.atime = CURRENT_TIME; + de->inode.mtime = CURRENT_TIME; + de->inode.ctime = CURRENT_TIME; + + if ((inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry)) == NULL) + return -ENOMEM; + + DPRINTK(DEBUG_DISABLED, + "(%s): new VFS inode(%u): %p dentry: %p\n", + dentry->d_name.name, de->inode.ino, inode, dentry); + + d_instantiate(dentry, inode); + + if (!is_devfsd_or_child(fs_info)) + devfsd_notify_de(de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + + return 0; +} + +static int devfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int err; + struct fs_info *fs_info = dir->i_sb->s_fs_info; + struct devfs_entry *parent, *de; + struct inode *inode; + + /* VFS doesn't pass S_IFMT part */ + mode = (mode & ~S_IFMT) | S_IFDIR; + parent = get_devfs_entry_from_vfs_inode(dir); + if (parent == NULL) + return -ENOENT; + + de = _devfs_alloc_entry(dentry->d_name.name, dentry->d_name.len, mode); + if (!de) + return -ENOMEM; + + de->vfs_deletable = TRUE; + + if ((err = _devfs_append_entry(parent, de, NULL)) != 0) + return err; + + de->inode.uid = current->euid; + de->inode.gid = current->egid; + de->inode.atime = CURRENT_TIME; + de->inode.mtime = CURRENT_TIME; + de->inode.ctime = CURRENT_TIME; + + if ((inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry)) == NULL) + return -ENOMEM; + + DPRINTK(DEBUG_DISABLED, + "(%s): new VFS inode(%u): %p dentry: %p\n", + dentry->d_name.name, de->inode.ino, inode, dentry); + + d_instantiate(dentry, inode); + + if (!is_devfsd_or_child(fs_info)) + devfsd_notify_de(de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + + return 0; +} + +static int devfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err = 0; + struct devfs_entry *de; + struct fs_info *fs_info = dir->i_sb->s_fs_info; + struct inode *inode = dentry->d_inode; + + if (dir->i_sb->s_fs_info != inode->i_sb->s_fs_info) + return -EINVAL; + + de = get_devfs_entry_from_vfs_inode(inode); + if (de == NULL) + return -ENOENT; + + if (!S_ISDIR(de->mode)) + return -ENOTDIR; + + if (!de->vfs_deletable) + return -EPERM; + + /* First ensure the directory is empty and will stay that way */ + write_lock(&de->u.dir.lock); + if (de->u.dir.first) + err = -ENOTEMPTY; + else + de->u.dir.no_more_additions = TRUE; + write_unlock(&de->u.dir.lock); + + if (err) + return err; + + /* Now unhook the directory from its parent */ + write_lock(&de->parent->u.dir.lock); + if (!_devfs_unhook(de)) + err = -ENOENT; + write_unlock(&de->parent->u.dir.lock); + + if (err) + return err; + + if (!is_devfsd_or_child(fs_info)) + devfsd_notify_de(de, DEVFSD_NOTIFY_DELETE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + + free_dentry(de); + devfs_put(de); + + return 0; +} + +static int devfs_mknod(struct inode *dir, + struct dentry *dentry, int mode, dev_t rdev) +{ + int err; + struct fs_info *fs_info = dir->i_sb->s_fs_info; + struct devfs_entry *parent, *de; + struct inode *inode; + + DPRINTK(DEBUG_I_MKNOD, "(%s): mode: 0%o dev: %u:%u\n", + dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); + + parent = get_devfs_entry_from_vfs_inode(dir); + if (parent == NULL) + return -ENOENT; + + de = _devfs_alloc_entry(dentry->d_name.name, dentry->d_name.len, mode); + if (!de) + return -ENOMEM; + + de->vfs_deletable = TRUE; + + if (S_ISCHR(mode)) + de->u.cdev.dev = rdev; + else if (S_ISBLK(mode)) + de->u.bdev.dev = rdev; + if ((err = _devfs_append_entry(parent, de, NULL)) != 0) + return err; + + de->inode.uid = current->euid; + de->inode.gid = current->egid; + de->inode.atime = CURRENT_TIME; + de->inode.mtime = CURRENT_TIME; + de->inode.ctime = CURRENT_TIME; + + if ((inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry)) == NULL) + return -ENOMEM; + + DPRINTK(DEBUG_I_MKNOD, ": new VFS inode(%u): %p dentry: %p\n", + de->inode.ino, inode, dentry); + + d_instantiate(dentry, inode); + + if (!is_devfsd_or_child(fs_info)) + devfsd_notify_de(de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + + return 0; +} + +static int devfs_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + int err; + struct devfs_entry *de; + + de = get_devfs_entry_from_vfs_inode(dentry->d_inode); + if (!de) + return -ENODEV; + + err = vfs_readlink(dentry, buffer, buflen, de->u.symlink.linkname); + return err; - de->inode.uid = current->euid; - de->inode.gid = current->egid; - de->inode.atime = CURRENT_TIME; - de->inode.mtime = CURRENT_TIME; - de->inode.ctime = CURRENT_TIME; - if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) - return -ENOMEM; - DPRINTK (DEBUG_I_MKNOD, ": new VFS inode(%u): %p dentry: %p\n", - de->inode.ino, inode, dentry); - d_instantiate (dentry, inode); - if ( !is_devfsd_or_child (fs_info) ) - devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - return 0; -} /* End Function devfs_mknod */ - -static int devfs_readlink (struct dentry *dentry, char *buffer, int buflen) -{ - int err; - struct devfs_entry *de; - - de = get_devfs_entry_from_vfs_inode (dentry->d_inode); - if (!de) return -ENODEV; - err = 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; - - de = get_devfs_entry_from_vfs_inode (dentry->d_inode); - if (!de) return -ENODEV; - err = vfs_follow_link (nd, de->u.symlink.linkname); - return err; -} /* End Function devfs_follow_link */ +} -static struct inode_operations devfs_iops = +static int devfs_follow_link(struct dentry *dentry, struct nameidata *nd) { - .setattr = devfs_notify_change, + int err; + struct devfs_entry *de; + + de = get_devfs_entry_from_vfs_inode(dentry->d_inode); + if (!de) + return -ENODEV; + + err = vfs_follow_link(nd, de->u.symlink.linkname); + + return err; +} + +static struct inode_operations devfs_iops = { + .setattr = devfs_notify_change, }; -static struct inode_operations devfs_dir_iops = -{ - .lookup = devfs_lookup, - .unlink = devfs_unlink, - .symlink = devfs_symlink, - .mkdir = devfs_mkdir, - .rmdir = devfs_rmdir, - .mknod = devfs_mknod, - .setattr = devfs_notify_change, +static struct inode_operations devfs_dir_iops = { + .lookup = devfs_lookup, + .unlink = devfs_unlink, + .symlink = devfs_symlink, + .mkdir = devfs_mkdir, + .rmdir = devfs_rmdir, + .mknod = devfs_mknod, + .setattr = devfs_notify_change, }; -static struct inode_operations devfs_symlink_iops = -{ - .readlink = devfs_readlink, - .follow_link = devfs_follow_link, - .setattr = devfs_notify_change, +static struct inode_operations devfs_symlink_iops = { + .readlink = devfs_readlink, + .follow_link = devfs_follow_link, + .setattr = devfs_notify_change, }; -static int devfs_fill_super (struct super_block *sb, void *data, int silent) +static int devfs_fill_super(struct super_block *sb, void *data, int silent) { - struct inode *root_inode = NULL; + struct inode *root_inode = NULL; + + if (_devfs_get_root_entry() == NULL) + goto out_no_root; + + atomic_set(&fs_info.devfsd_overrun_count, 0); + init_waitqueue_head(&fs_info.devfsd_wait_queue); + init_waitqueue_head(&fs_info.revalidate_wait_queue); + fs_info.sb = sb; + sb->s_fs_info = &fs_info; + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = DEVFS_SUPER_MAGIC; + sb->s_op = &devfs_sops; + + if ((root_inode = _devfs_get_vfs_inode(sb, root_entry, NULL)) == NULL) + goto out_no_root; + + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) + goto out_no_root; - if (_devfs_get_root_entry () == NULL) goto out_no_root; - atomic_set (&fs_info.devfsd_overrun_count, 0); - init_waitqueue_head (&fs_info.devfsd_wait_queue); - init_waitqueue_head (&fs_info.revalidate_wait_queue); - fs_info.sb = sb; - sb->s_fs_info = &fs_info; - sb->s_blocksize = 1024; - sb->s_blocksize_bits = 10; - sb->s_magic = DEVFS_SUPER_MAGIC; - sb->s_op = &devfs_sops; - if ( ( root_inode = _devfs_get_vfs_inode (sb, root_entry, NULL) ) == NULL ) - goto out_no_root; - sb->s_root = d_alloc_root (root_inode); - if (!sb->s_root) goto out_no_root; - DPRINTK (DEBUG_S_READ, "(): made devfs ptr: %p\n", sb->s_fs_info); - return 0; + DPRINTK(DEBUG_S_READ, "(): made devfs ptr: %p\n", sb->s_fs_info); + + return 0; out_no_root: - PRINTK ("(): get root inode failed\n"); - if (root_inode) iput (root_inode); - return -EINVAL; -} /* End Function devfs_fill_super */ + PRINTK("(): get root inode failed\n"); + if (root_inode) + iput(root_inode); -static struct super_block * -devfs_get_sb (struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) -{ - return get_sb_single (fs_type, flags, data, devfs_fill_super); + return -EINVAL; } -static struct file_system_type devfs_fs_type = +static struct super_block * devfs_get_sb(struct file_system_type *fs_type, + int flags, + const char *dev_name, void *data) { - .name = DEVFS_NAME, - .get_sb = devfs_get_sb, - .kill_sb = kill_anon_super, + return get_sb_single(fs_type, flags, data, devfs_fill_super); +} + +static struct file_system_type devfs_fs_type = { + .name = DEVFS_NAME, + .get_sb = devfs_get_sb, + .kill_sb = kill_anon_super, }; -/* File operations for devfsd follow */ +/* File operations for devfsd follow */ -static ssize_t devfsd_read (struct file *file, char *buf, size_t len, - loff_t *ppos) +static ssize_t devfsd_read(struct file *file, + char *buf, size_t len, loff_t * ppos) { - int done = FALSE; - int ival; - loff_t pos, devname_offset, tlen, rpos; - devfs_handle_t de; - struct devfsd_buf_entry *entry; - struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->s_fs_info; - 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; - /* Block for a new entry */ - set_current_state (TASK_INTERRUPTIBLE); - add_wait_queue (&fs_info->devfsd_wait_queue, &wait); - while ( devfsd_queue_empty (fs_info) ) - { - fs_info->devfsd_sleeping = TRUE; - wake_up (&fs_info->revalidate_wait_queue); - schedule (); - fs_info->devfsd_sleeping = FALSE; - if ( signal_pending (current) ) - { - remove_wait_queue (&fs_info->devfsd_wait_queue, &wait); - __set_current_state (TASK_RUNNING); - return -EINTR; - } - set_current_state (TASK_INTERRUPTIBLE); - } - remove_wait_queue (&fs_info->devfsd_wait_queue, &wait); - __set_current_state (TASK_RUNNING); - /* Now play with the data */ - ival = atomic_read (&fs_info->devfsd_overrun_count); - info->overrun_count = ival; - entry = fs_info->devfsd_first_event; - info->type = entry->type; - info->mode = entry->mode; - info->uid = entry->uid; - info->gid = entry->gid; - de = entry->de; - if (S_ISCHR(de->mode)) { - info->major = MAJOR(de->u.cdev.dev); - info->minor = MINOR(de->u.cdev.dev); - } else if (S_ISBLK (de->mode)) { - info->major = MAJOR(de->u.bdev.dev); - info->minor = MINOR(de->u.bdev.dev); - } - 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; - 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) ) - { - return -EFAULT; - } - rpos += tlen; - buf += tlen; - len -= tlen; - } - if ( (rpos >= devname_offset) && (len > 0) ) - { - /* Copy the name */ - tlen = info->namelen + 1; - if (tlen > len) tlen = len; - else done = TRUE; - if ( copy_to_user (buf, info->devname + pos + rpos - devname_offset, - tlen) ) - { - return -EFAULT; - } - rpos += tlen; - } - tlen = rpos - *ppos; - if (done) - { - devfs_handle_t parent; - - spin_lock (&fs_info->devfsd_buffer_lock); - fs_info->devfsd_first_event = entry->next; - if (entry->next == NULL) fs_info->devfsd_last_event = NULL; - spin_unlock (&fs_info->devfsd_buffer_lock); - for (; de != NULL; de = parent) - { - parent = de->parent; - devfs_put (de); - } - kmem_cache_free (devfsd_buf_cache, entry); - if (ival > 0) atomic_sub (ival, &fs_info->devfsd_overrun_count); - *ppos = 0; - } - else *ppos = rpos; - return tlen; -} /* End Function devfsd_read */ - -static int devfsd_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ival; - struct fs_info *fs_info = inode->i_sb->s_fs_info; - - switch (cmd) - { - case DEVFSDIOC_GET_PROTO_REV: - ival = DEVFSD_PROTOCOL_REVISION_KERNEL; - if ( copy_to_user ( (void *)arg, &ival, sizeof ival ) ) return -EFAULT; - break; - case DEVFSDIOC_SET_EVENT_MASK: - /* Ensure only one reader has access to the queue. This scheme will - work even if the global kernel lock were to be removed, because it - doesn't matter who gets in first, as long as only one gets it */ - if (fs_info->devfsd_task == NULL) - { - static spinlock_t lock = SPIN_LOCK_UNLOCKED; + int done = FALSE; + int ival; + loff_t pos, devname_offset, tlen, rpos; + devfs_handle_t de; + struct devfsd_buf_entry *entry; + struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->s_fs_info; + 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; + + /* Block for a new entry */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&fs_info->devfsd_wait_queue, &wait); + + while (devfsd_queue_empty(fs_info)) { + fs_info->devfsd_sleeping = TRUE; + wake_up(&fs_info->revalidate_wait_queue); + schedule(); + fs_info->devfsd_sleeping = FALSE; + + if (signal_pending(current)) { + remove_wait_queue(&fs_info->devfsd_wait_queue, &wait); + __set_current_state(TASK_RUNNING); + return -EINTR; + } - if ( !spin_trylock (&lock) ) return -EBUSY; - if (fs_info->devfsd_task != NULL) - { /* We lost the race... */ - spin_unlock (&lock); - return -EBUSY; - } - fs_info->devfsd_task = current; - spin_unlock (&lock); - fs_info->devfsd_pgrp = (process_group(current) == current->pid) ? - process_group(current) : 0; - fs_info->devfsd_file = file; - fs_info->devfsd_info = kmalloc (sizeof *fs_info->devfsd_info, - GFP_KERNEL); - if (!fs_info->devfsd_info) - { - devfsd_close (inode, file); - return -ENOMEM; - } + set_current_state(TASK_INTERRUPTIBLE); + } + + remove_wait_queue(&fs_info->devfsd_wait_queue, &wait); + __set_current_state(TASK_RUNNING); + + /* Now play with the data */ + ival = atomic_read(&fs_info->devfsd_overrun_count); + info->overrun_count = ival; + entry = fs_info->devfsd_first_event; + info->type = entry->type; + info->mode = entry->mode; + info->uid = entry->uid; + info->gid = entry->gid; + de = entry->de; + + if (S_ISCHR(de->mode)) { + info->major = MAJOR(de->u.cdev.dev); + info->minor = MINOR(de->u.cdev.dev); + } else if (S_ISBLK(de->mode)) { + info->major = MAJOR(de->u.bdev.dev); + info->minor = MINOR(de->u.bdev.dev); + } + + 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; + + 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)) { + return -EFAULT; + } + + rpos += tlen; + buf += tlen; + len -= tlen; } - 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: - if (fs_info->devfsd_file != file) return -EPERM; - return devfsd_close (inode, file); - /*break;*/ + + if ((rpos >= devname_offset) && (len > 0)) { + /* Copy the name */ + tlen = info->namelen + 1; + + if (tlen > len) + tlen = len; + else + done = TRUE; + + if (copy_to_user(buf, info->devname + pos + rpos - devname_offset, tlen)) { + return -EFAULT; + } + + rpos += tlen; + } + + tlen = rpos - *ppos; + + if (done) { + devfs_handle_t parent; + + spin_lock(&fs_info->devfsd_buffer_lock); + fs_info->devfsd_first_event = entry->next; + if (entry->next == NULL) + fs_info->devfsd_last_event = NULL; + spin_unlock(&fs_info->devfsd_buffer_lock); + + for (; de != NULL; de = parent) { + parent = de->parent; + devfs_put(de); + } + + kmem_cache_free(devfsd_buf_cache, entry); + + if (ival > 0) + atomic_sub(ival, &fs_info->devfsd_overrun_count); + + *ppos = 0; + } else + *ppos = rpos; + + return tlen; +} + +static int devfsd_ioctl(struct inode *inode, + struct file *file, unsigned int cmd, unsigned long arg) +{ + int ival; + struct fs_info *fs_info = inode->i_sb->s_fs_info; + + switch (cmd) { + case DEVFSDIOC_GET_PROTO_REV: + ival = DEVFSD_PROTOCOL_REVISION_KERNEL; + if (copy_to_user((void *) arg, &ival, sizeof ival)) + return -EFAULT; + break; + + case DEVFSDIOC_SET_EVENT_MASK: + /* + * Ensure only one reader has access to the queue. This + * scheme will work even if the global kernel lock were + * to be removed, because it doesn't matter who gets in + * first, as long as only one gets it. + */ + if (fs_info->devfsd_task == NULL) { + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + + if (!spin_trylock(&lock)) + return -EBUSY; + + /* We lost the race... */ + if (fs_info->devfsd_task != NULL) { + spin_unlock(&lock); + return -EBUSY; + } + + fs_info->devfsd_task = current; + spin_unlock(&lock); + + fs_info->devfsd_pgrp = + (process_group(current) == + current->pid) ? process_group(current) : 0; + fs_info->devfsd_file = file; + fs_info->devfsd_info = + kmalloc(sizeof *fs_info->devfsd_info, GFP_KERNEL); + + if (!fs_info->devfsd_info) { + devfsd_close(inode, file); + return -ENOMEM; + } + } else if (fs_info->devfsd_task != current) + return -EBUSY; + + /* Let the masses come forth */ + fs_info->devfsd_event_mask = arg; + break; + + case DEVFSDIOC_RELEASE_EVENT_QUEUE: + if (fs_info->devfsd_file != file) + return -EPERM; + return devfsd_close(inode, file); + /*break; */ + #ifdef CONFIG_DEVFS_DEBUG - case DEVFSDIOC_SET_DEBUG_MASK: - if ( copy_from_user (&ival, (void *) arg, sizeof ival) )return -EFAULT; - devfs_debug = ival; - break; + case DEVFSDIOC_SET_DEBUG_MASK: + if (copy_from_user(&ival, (void *) arg, sizeof ival)) + return -EFAULT; + devfs_debug = ival; + break; #endif - default: - return -ENOIOCTLCMD; - } - return 0; -} /* End Function devfsd_ioctl */ - -static int devfsd_close (struct inode *inode, struct file *file) -{ - struct devfsd_buf_entry *entry, *next; - struct fs_info *fs_info = inode->i_sb->s_fs_info; - - if (fs_info->devfsd_file != file) return 0; - fs_info->devfsd_event_mask = 0; - fs_info->devfsd_file = NULL; - spin_lock (&fs_info->devfsd_buffer_lock); - entry = fs_info->devfsd_first_event; - fs_info->devfsd_first_event = NULL; - fs_info->devfsd_last_event = NULL; - if (fs_info->devfsd_info) - { - kfree (fs_info->devfsd_info); - fs_info->devfsd_info = NULL; - } - spin_unlock (&fs_info->devfsd_buffer_lock); - fs_info->devfsd_pgrp = 0; - fs_info->devfsd_task = NULL; - wake_up (&fs_info->revalidate_wait_queue); - for (; entry; entry = next) - { - next = entry->next; - kmem_cache_free (devfsd_buf_cache, entry); - } - return 0; -} /* End Function devfsd_close */ + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static int devfsd_close(struct inode *inode, struct file *file) +{ + struct devfsd_buf_entry *entry, *next; + struct fs_info *fs_info = inode->i_sb->s_fs_info; + + if (fs_info->devfsd_file != file) + return 0; + + fs_info->devfsd_event_mask = 0; + fs_info->devfsd_file = NULL; + + spin_lock(&fs_info->devfsd_buffer_lock); + entry = fs_info->devfsd_first_event; + fs_info->devfsd_first_event = NULL; + fs_info->devfsd_last_event = NULL; + + if (fs_info->devfsd_info) { + kfree(fs_info->devfsd_info); + fs_info->devfsd_info = NULL; + } + spin_unlock(&fs_info->devfsd_buffer_lock); + + fs_info->devfsd_pgrp = 0; + fs_info->devfsd_task = NULL; + wake_up(&fs_info->revalidate_wait_queue); + + for (; entry; entry = next) { + next = entry->next; + kmem_cache_free(devfsd_buf_cache, entry); + } + + return 0; +} #ifdef CONFIG_DEVFS_DEBUG -static ssize_t stat_read (struct file *file, char *buf, size_t len, - loff_t *ppos) +static ssize_t stat_read(struct file *file, + char *buf, size_t len, loff_t * ppos) { - ssize_t num; - char txt[80]; + ssize_t num; + char txt[80]; - num = sprintf (txt, "Number of entries: %u number of bytes: %u\n", - stat_num_entries, stat_num_bytes) + 1; - /* Can't seek (pread) on this device */ - if (ppos != &file->f_pos) return -ESPIPE; - if (*ppos >= num) return 0; - if (*ppos + len > num) len = num - *ppos; - if ( copy_to_user (buf, txt + *ppos, len) ) return -EFAULT; - *ppos += len; - return len; -} /* End Function stat_read */ -#endif + num = sprintf(txt, "Number of entries: %u number of bytes: %u\n", + stat_num_entries, stat_num_bytes) + 1; + + /* Can't seek (pread) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (*ppos >= num) + return 0; + + if (*ppos + len > num) + len = num - *ppos; + + if (copy_to_user(buf, txt + *ppos, len)) + return -EFAULT; + + *ppos += len; + return len; +} +#endif -static int __init init_devfs_fs (void) +static int __init init_devfs_fs(void) { - int err; + int err; + + printk(KERN_INFO "%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", + DEVFS_NAME, DEVFS_VERSION); + + devfsd_buf_cache = + kmem_cache_create("devfsd_event", + sizeof (struct devfsd_buf_entry), 0, + 0, NULL, NULL); + if (!devfsd_buf_cache) + OOPS("(): unable to allocate event slab\n"); - printk (KERN_INFO "%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", - DEVFS_NAME, DEVFS_VERSION); - devfsd_buf_cache = kmem_cache_create ("devfsd_event", - sizeof (struct devfsd_buf_entry), - 0, 0, NULL, NULL); - if (!devfsd_buf_cache) OOPS ("(): unable to allocate event slab\n"); #ifdef CONFIG_DEVFS_DEBUG - devfs_debug = devfs_debug_init; - printk (KERN_INFO "%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug); + devfs_debug = devfs_debug_init; + printk(KERN_INFO "%s: devfs_debug: 0x%0x\n", DEVFS_NAME, + devfs_debug); #endif - printk (KERN_INFO "%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options); - err = register_filesystem (&devfs_fs_type); - if (!err) - { - struct vfsmount *devfs_mnt = kern_mount (&devfs_fs_type); - err = PTR_ERR (devfs_mnt); - if ( !IS_ERR (devfs_mnt) ) err = 0; - } - return err; -} /* End Function init_devfs_fs */ - -void __init mount_devfs_fs (void) -{ - int err; - - if ( !(boot_options & OPTION_MOUNT) ) return; - err = do_mount ("none", "/dev", "devfs", 0, ""); - if (err == 0) printk (KERN_INFO "Mounted devfs on /dev\n"); - else PRINTK ("(): unable to mount devfs, err: %d\n", err); -} /* End Function mount_devfs_fs */ + + printk(KERN_INFO "%s: boot_options: 0x%0x\n", DEVFS_NAME, + boot_options); + + err = register_filesystem(&devfs_fs_type); + if (!err) { + struct vfsmount *devfs_mnt = kern_mount(&devfs_fs_type); + err = PTR_ERR(devfs_mnt); + if (!IS_ERR(devfs_mnt)) + err = 0; + } + + return err; +} + +void __init mount_devfs_fs(void) +{ + int err; + + if (!(boot_options & OPTION_MOUNT)) + return; + + err = do_mount("none", "/dev", "devfs", 0, ""); + if (err == 0) + printk(KERN_INFO "Mounted devfs on /dev\n"); + else + PRINTK("(): unable to mount devfs, err: %d\n", err); +} module_init(init_devfs_fs) diff -Nur linux-2.6.0.devfs.orig/fs/devfs/util.c linux-2.6.0.devfs.1/fs/devfs/util.c --- linux-2.6.0.devfs.orig/fs/devfs/util.c 2003-12-27 13:06:03.000000000 +0800 +++ linux-2.6.0.devfs.1/fs/devfs/util.c 2003-12-27 00:39:51.000000000 +0800 @@ -87,6 +87,7 @@ return n; } + EXPORT_SYMBOL(devfs_register_tape); void devfs_unregister_tape(int num) @@ -102,6 +103,7 @@ spinlock_t lock; unsigned long bits[256 / BITS_PER_LONG]; }; + #if BITS_PER_LONG == 32 # define INITIALISER64(low,high) (low), (high) #else @@ -134,15 +136,12 @@ } }; - /** * devfs_alloc_major - Allocate a major number. * @mode: The file mode (must be block device or character device). * Returns the allocated major, else -1 if none are available. * This routine is thread safe and does not block. */ - - struct minor_list { int major; @@ -158,7 +157,6 @@ static DECLARE_MUTEX(device_list_mutex); - /** * devfs_alloc_devnum - Allocate a device number. * @mode: The file mode (must be block device or character device). @@ -216,18 +214,17 @@ list->last = entry; minor = 0; - out_done: +out_done: __set_bit(minor, entry->bits); up(&device_list_mutex); return MKDEV(entry->major, minor); - out_full: +out_full: list->none_free = 1; - out_unlock: +out_unlock: up(&device_list_mutex); return 0; } - /** * devfs_dealloc_devnum - Dellocate a device number. * @mode: The file mode (must be block device or character device). @@ -235,7 +232,6 @@ * * This routine is thread safe and may block. */ - void devfs_dealloc_devnum(umode_t mode, dev_t devnum) { struct device_list *list = S_ISCHR(mode) ? &char_list : &block_list;