diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-06-15 01:55:58 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-06-15 01:55:58 +0000 |
commit | 53b3988d474435254a3b053a68bb24ce9e439295 (patch) | |
tree | f8da8e40f01f4ad02bbd76b8c9920749b118235f /fs | |
parent | b0cb48abe83d1a4389ea938bf624f8baa82c5047 (diff) |
Merge with 2.3.99-pre9.
Diffstat (limited to 'fs')
69 files changed, 1398 insertions, 1653 deletions
diff --git a/fs/Makefile b/fs/Makefile index 9219a138f..0693daf69 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -21,7 +21,7 @@ ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ nfsd nls devpts devfs adfs partitions qnx4 udf bfs cramfs \ openpromfs autofs4 ramfs -SUB_DIRS := partitions +SUB_DIRS := ifeq ($(CONFIG_QUOTA),y) O_OBJS += dquot.o @@ -29,6 +29,14 @@ else O_OBJS += noquot.o endif +ifdef CONFIG_PROC_FS +SUB_DIRS += proc +endif + +SUB_DIRS += partitions + +# Do not add any filesystems before this line + ifeq ($(CONFIG_EXT2_FS),y) SUB_DIRS += ext2 else @@ -93,10 +101,6 @@ else endif endif -ifdef CONFIG_PROC_FS -SUB_DIRS += proc -endif - ifeq ($(CONFIG_BFS_FS),y) SUB_DIRS += bfs else diff --git a/fs/affs/namei.c b/fs/affs/namei.c index c1a849a70..e81e321e3 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -249,7 +249,6 @@ affs_unlink(struct inode *dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_version = ++event; mark_inode_dirty(inode); - d_delete(dentry); mark_inode_dirty(dir); retval = 0; @@ -380,7 +379,6 @@ affs_rmdir(struct inode *dir, struct dentry *dentry) dir->i_version = ++event; mark_inode_dirty(dir); mark_inode_dirty(inode); - d_delete(dentry); rmdir_done: affs_brelse(bh); diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index b71c3a819..9c114f1c2 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -12,29 +12,10 @@ #include "autofs_i.h" -static int autofs_dir_readdir(struct file *filp, - void *dirent, filldir_t filldir) -{ - struct inode *inode=filp->f_dentry->d_inode; - - switch((unsigned long) filp->f_pos) - { - case 0: - if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0) - return 0; - filp->f_pos++; - /* fall through */ - case 1: - if (filldir(dirent, "..", 2, 1, AUTOFS_ROOT_INO) < 0) - return 0; - filp->f_pos++; - /* fall through */ - } - return 1; -} - /* - * No entries except for "." and "..", both of which are handled by the VFS layer + * No entries except for "." and "..", both of which are handled by the VFS + * layer. So all children are negative and dcache-based versions of operations + * are OK. */ static struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry) { @@ -44,7 +25,7 @@ static struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry) struct file_operations autofs_dir_operations = { read: generic_read_dir, - readdir: autofs_dir_readdir, + readdir: dcache_readdir, }; struct inode_operations autofs_dir_inode_operations = { diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 168d7861b..448143fd0 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -133,8 +133,8 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) autofs_say(ent->name,ent->len); autofs_init_usage(dh,ent); - if ( ent->dentry ) - ent->dentry->d_count++; + if (ent->dentry) + dget(ent->dentry); dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE]; ent->next = *dhnp; diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 7f7337802..00951bf8e 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -16,7 +16,6 @@ #include <linux/param.h> #include "autofs_i.h" -static int autofs4_dir_readdir(struct file *,void *,filldir_t); static struct dentry *autofs4_dir_lookup(struct inode *,struct dentry *); static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *); static int autofs4_dir_unlink(struct inode *,struct dentry *); @@ -27,13 +26,13 @@ static struct dentry *autofs4_root_lookup(struct inode *,struct dentry *); struct file_operations autofs4_root_operations = { read: generic_read_dir, - readdir: autofs4_dir_readdir, + readdir: dcache_readdir, ioctl: autofs4_root_ioctl, }; struct file_operations autofs4_dir_operations = { read: generic_read_dir, - readdir: autofs4_dir_readdir, + readdir: dcache_readdir, }; struct inode_operations autofs4_root_inode_operations = { @@ -52,67 +51,6 @@ struct inode_operations autofs4_dir_inode_operations = { rmdir: autofs4_dir_rmdir, }; -static inline struct dentry *nth_child(struct dentry *dir, int nr) -{ - struct list_head *tmp = dir->d_subdirs.next; - - while(tmp != &dir->d_subdirs) { - if (nr-- == 0) - return list_entry(tmp, struct dentry, d_child); - tmp = tmp->next; - } - return NULL; -} - -static int autofs4_dir_readdir(struct file *filp, void *dirent, - filldir_t filldir) -{ - struct autofs_sb_info *sbi; - struct autofs_info *ino; - struct dentry *dentry = filp->f_dentry; - struct dentry *dent_ptr; - struct inode *dir = dentry->d_inode; - struct list_head *cursor; - off_t nr; - - sbi = autofs4_sbi(dir->i_sb); - ino = autofs4_dentry_ino(dentry); - nr = filp->f_pos; - - switch(nr) - { - case 0: - if (filldir(dirent, ".", 1, nr, dir->i_ino) < 0) - return 0; - filp->f_pos = ++nr; - /* fall through */ - case 1: - if (filldir(dirent, "..", 2, nr, dentry->d_parent->d_inode->i_ino) < 0) - return 0; - filp->f_pos = ++nr; - /* fall through */ - default: - dent_ptr = nth_child(dentry, nr-2); - if (dent_ptr == NULL) - break; - - cursor = &dent_ptr->d_child; - - while(cursor != &dentry->d_subdirs) { - dent_ptr = list_entry(cursor, struct dentry, d_child); - if (dent_ptr->d_inode && - filldir(dirent, dent_ptr->d_name.name, dent_ptr->d_name.len, nr, - dent_ptr->d_inode->i_ino) < 0) - return 0; - filp->f_pos = ++nr; - cursor = cursor->next; - } - break; - } - - return 0; -} - /* Update usage from here to top of tree, so that scan of top-level directories will give a useful result */ static void autofs4_update_usage(struct dentry *dentry) diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index 38881a1b6..bd8be88c3 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -32,11 +32,6 @@ static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir) unsigned int offset; int block; - if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { - printf("Bad inode or not a directory %s:%08lx\n", bdevname(dev), dir->i_ino); - return -EBADF; - } - if (f->f_pos & (BFS_DIRENT_SIZE-1)) { printf("Bad f_pos=%08lx for %s:%08lx\n", (unsigned long)f->f_pos, bdevname(dev), dir->i_ino); @@ -188,7 +183,6 @@ static int bfs_unlink(struct inode * dir, struct dentry * dentry) inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); - d_delete(dentry); error = 0; out_brelse: diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 49d818e21..ef4af4dfe 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -86,6 +86,8 @@ static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file) struct user dump; #if defined(__alpha__) # define START_DATA(u) (u.start_data) +#elif defined(__arm__) +# define START_DATA(u) ((u.u_tsize << PAGE_SHIFT) + u.start_code) #elif defined(__sparc__) # define START_DATA(u) (u.u_tsize) #elif defined(__i386__) || defined(__mc68000__) @@ -217,7 +219,7 @@ static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm) envp = (char **) sp; sp -= argc+1; argv = (char **) sp; -#if defined(__i386__) || defined(__mc68000__) +#if defined(__i386__) || defined(__mc68000__) || defined(__arm__) put_user((unsigned long) envp,--sp); put_user((unsigned long) argv,--sp); #endif diff --git a/fs/buffer.c b/fs/buffer.c index a7a6f79a8..47d690fa4 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1324,7 +1324,7 @@ int block_flushpage(struct page *page, unsigned long offset) * instead. */ if (!offset) { - if (!try_to_free_buffers(page)) { + if (!try_to_free_buffers(page, 0)) { atomic_inc(&buffermem_pages); return 0; } @@ -2121,15 +2121,17 @@ out: * This all is required so that we can free up memory * later. */ -static void sync_page_buffers(struct buffer_head *bh) +static void sync_page_buffers(struct buffer_head *bh, int wait) { - struct buffer_head * tmp; + struct buffer_head * tmp = bh; - tmp = bh; do { struct buffer_head *p = tmp; tmp = tmp->b_this_page; - if (buffer_dirty(p) && !buffer_locked(p)) + if (buffer_locked(p)) { + if (wait) + __wait_on_buffer(p); + } else if (buffer_dirty(p)) ll_rw_block(WRITE, 1, &p); } while (tmp != bh); } @@ -2151,7 +2153,7 @@ static void sync_page_buffers(struct buffer_head *bh) * obtain a reference to a buffer head within a page. So we must * lock out all of these paths to cleanly toss the page. */ -int try_to_free_buffers(struct page * page) +int try_to_free_buffers(struct page * page, int wait) { struct buffer_head * tmp, * bh = page->buffers; int index = BUFSIZE_INDEX(bh->b_size); @@ -2201,7 +2203,7 @@ busy_buffer_page: spin_unlock(&free_list[index].lock); write_unlock(&hash_table_lock); spin_unlock(&lru_list_lock); - sync_page_buffers(bh); + sync_page_buffers(bh, wait); return 0; } diff --git a/fs/coda/cache.c b/fs/coda/cache.c index fa8a66e0c..68be7a69d 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -259,8 +259,6 @@ int coda_cache_check(struct inode *inode, int mask) void coda_purge_dentries(struct inode *inode) { - struct list_head *tmp, *head = &inode->i_dentry; - if (!inode) return ; @@ -268,23 +266,7 @@ void coda_purge_dentries(struct inode *inode) iget(inode->i_sb, inode->i_ino); /* catch the dentries later if some are still busy */ coda_flag_inode(inode, C_PURGE); - -restart: - tmp = head; - while ((tmp = tmp->next) != head) { - struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); - if (!dentry->d_count) { - CDEBUG(D_DOWNCALL, - "coda_free_dentries: freeing %s/%s, i_count=%d\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - inode->i_count); - dget(dentry); - d_drop(dentry); - dput(dentry); - goto restart; - } - - } + d_prune_aliases(inode); iput(inode); } @@ -311,28 +293,18 @@ static void coda_flag_children(struct dentry *parent, int flag) void coda_flag_inode_children(struct inode *inode, int flag) { - struct list_head *alias; struct dentry *alias_de; ENTRY; - if ( !inode ) + if ( !inode || !S_ISDIR(inode->i_mode)) return; - if (list_empty(&inode->i_dentry)) - return; - - /* I believe that shrink_dcache_parent will not - remove dentries from the alias list. If it - does we are toast. - */ - alias = inode->i_dentry.next; - while ( alias != &inode->i_dentry ) { - alias_de = list_entry(alias, struct dentry, d_alias); - coda_flag_children(alias_de, flag); - alias = alias->next; - shrink_dcache_parent(alias_de); - } - + alias_de = d_find_alias(inode); + if (!alias_de) + return; + coda_flag_children(alias_de, flag); + shrink_dcache_parent(alias_de); + dput(alias_de); } /* this will not zap the inode away */ diff --git a/fs/coda/dir.c b/fs/coda/dir.c index bb51b0c05..83f7bbcc5 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -45,7 +45,7 @@ static int coda_readdir(struct file *file, void *dirent, filldir_t filldir); /* dentry ops */ static int coda_dentry_revalidate(struct dentry *de, int); -static void coda_dentry_delete(struct dentry *); +static int coda_dentry_delete(struct dentry *); /* support routines */ static int coda_venus_readdir(struct file *filp, void *dirent, @@ -434,7 +434,6 @@ int coda_unlink(struct inode *dir, struct dentry *de) /* cache management: mtime has changed, ask Venus */ dircnp->c_flags |= C_VATTR; de->d_inode->i_nlink--; - d_delete(de); return 0; } @@ -801,20 +800,21 @@ static int coda_dentry_revalidate(struct dentry *de, int flags) * This is the callback from dput() when d_count is going to 0. * We use this to unhash dentries with bad inodes. */ -static void coda_dentry_delete(struct dentry * dentry) +static int coda_dentry_delete(struct dentry * dentry) { int flags; if (!dentry->d_inode) - return ; + return 0; flags = (ITOC(dentry->d_inode)->c_flags) & C_PURGE; if (is_bad_inode(dentry->d_inode) || flags) { CDEBUG(D_DOWNCALL, "bad inode, unhashing %s/%s, %ld\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_inode->i_ino); - d_drop(dentry); + return 1; } + return 0; } diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 1a7fb195c..b88c602c6 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -296,6 +296,8 @@ static int coda_psdev_release(struct inode * inode, struct file * file) vcp->vc_inuse, vcp->vc_pid, current->pid); if ( vcp->vc_pid != current->pid ) { + /* FIXME: this is broken. If venus does fork(), accounting goes wrong */ + printk( "Closed by someone else than caller?\n" ); return 0; } diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 279b0bfef..3f51c2aa1 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -84,15 +84,17 @@ static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inod #define NEXT_BUFFER(_ix) ((_ix) ^ 1) /* - * BLKS_PER_BUF_SHIFT must be at least 1 to allow for "compressed" - * data that takes up more space than the original. 1 is guaranteed - * to suffice, though. Larger values provide more read-ahead and - * proportionally less wastage at the end of the buffer. + * BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed" + * data that takes up more space than the original and with unlucky + * alignment. */ -#define BLKS_PER_BUF_SHIFT (2) -#define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) -static unsigned char read_buffers[READ_BUFFERS][BLKS_PER_BUF][PAGE_CACHE_SIZE]; +#define BLKS_PER_BUF_SHIFT (2) +#define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) +#define BUFFER_SIZE (BLKS_PER_BUF*PAGE_CACHE_SIZE) + +static unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE]; static unsigned buffer_blocknr[READ_BUFFERS]; +static struct super_block * buffer_dev[READ_BUFFERS]; static int next_buffer = 0; /* @@ -102,19 +104,27 @@ static int next_buffer = 0; static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len) { struct buffer_head * bh_array[BLKS_PER_BUF]; - unsigned i, blocknr, last_blocknr, buffer; + unsigned i, blocknr, buffer; + char *data; if (!len) return NULL; blocknr = offset >> PAGE_CACHE_SHIFT; - last_blocknr = (offset + len - 1) >> PAGE_CACHE_SHIFT; - if (last_blocknr - blocknr >= BLKS_PER_BUF) - goto eek; resume: offset &= PAGE_CACHE_SIZE - 1; + + /* Check if an existing buffer already has the data.. */ for (i = 0; i < READ_BUFFERS; i++) { - if ((blocknr >= buffer_blocknr[i]) && - (last_blocknr - buffer_blocknr[i] < BLKS_PER_BUF)) - return &read_buffers[i][blocknr - buffer_blocknr[i]][offset]; + unsigned int blk_offset; + + if (buffer_dev[i] != sb) + continue; + if (blocknr < buffer_blocknr[i]) + continue; + blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_CACHE_SHIFT; + blk_offset += offset; + if (blk_offset + len > BUFFER_SIZE) + continue; + return read_buffers[i] + blk_offset; } /* Ok, read in BLKS_PER_BUF pages completely first. */ @@ -125,24 +135,19 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i buffer = next_buffer; next_buffer = NEXT_BUFFER(buffer); buffer_blocknr[buffer] = blocknr; + buffer_dev[buffer] = sb; + + data = read_buffers[buffer]; for (i = 0; i < BLKS_PER_BUF; i++) { struct buffer_head * bh = bh_array[i]; if (bh) { - memcpy(read_buffers[buffer][i], bh->b_data, PAGE_CACHE_SIZE); + memcpy(data, bh->b_data, PAGE_CACHE_SIZE); bforget(bh); } else - memset(read_buffers[buffer][i], 0, PAGE_CACHE_SIZE); + memset(data, 0, PAGE_CACHE_SIZE); + data += PAGE_CACHE_SIZE; } - return read_buffers[buffer][0] + offset; - - eek: - printk(KERN_ERR - "cramfs (device %s): requested chunk (%u:+%u) bigger than buffer\n", - bdevname(sb->s_dev), offset, len); - /* TODO: return EIO to process or kill the current process - instead of resuming. */ - *((int *)0) = 0; /* XXX: doesn't work on all archs */ - goto resume; + return read_buffers[buffer] + offset; } diff --git a/fs/dcache.c b/fs/dcache.c index 1b3ff98b2..ad897ff38 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -131,7 +131,8 @@ repeat: * Each fs will have to watch for this. */ if (dentry->d_op && dentry->d_op->d_delete) { - dentry->d_op->d_delete(dentry); + if (dentry->d_op->d_delete(dentry)) + d_drop(dentry); count = dentry->d_count - 1; if (count != 0) @@ -220,6 +221,53 @@ int d_invalidate(struct dentry * dentry) return 0; } +/** + * d_find_alias - grab a hashed alias of inode + * @inode: inode in question + * + * If inode has a hashed alias - acquire the reference to alias and + * return it. Otherwise return NULL. Notice that if inode is a directory + * there can be only one alias and it can be unhashed only if it has + * no children. + */ + +struct dentry * d_find_alias(struct inode *inode) +{ + struct list_head *head, *next, *tmp; + struct dentry *alias; + + head = &inode->i_dentry; + next = inode->i_dentry.next; + while (next != head) { + tmp = next; + next = tmp->next; + alias = list_entry(tmp, struct dentry, d_alias); + if (!d_unhashed(alias)) + return dget(alias); + } + return NULL; +} + +/* + * Try to kill dentries associated with this inode. + * WARNING: you must own a reference to inode. + */ +void d_prune_aliases(struct inode *inode) +{ + struct list_head *tmp, *head = &inode->i_dentry; +restart: + tmp = head; + while ((tmp = tmp->next) != head) { + struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); + if (!dentry->d_count) { + dget(dentry); + d_drop(dentry); + dput(dentry); + goto restart; + } + } +} + /* * Throw away a dentry - free the inode, dput the parent. * This requires that the LRU list has already been @@ -384,37 +432,6 @@ resume: return 0; /* No mount points found in tree */ } -int d_active_refs(struct dentry *root) -{ - struct dentry *this_parent = root; - struct list_head *next; - int count = root->d_count; - -repeat: - next = this_parent->d_subdirs.next; -resume: - while (next != &this_parent->d_subdirs) { - struct list_head *tmp = next; - struct dentry *dentry = list_entry(tmp, struct dentry, d_child); - next = tmp->next; - /* Decrement count for unused children */ - count += (dentry->d_count - 1); - if (!list_empty(&dentry->d_subdirs)) { - this_parent = dentry; - goto repeat; - } - } - /* - * All done at this level ... ascend and resume the search. - */ - if (this_parent != root) { - next = this_parent->d_child.next; - this_parent = this_parent->d_parent; - goto resume; - } - return count; -} - /* * Search the dentry child list for the specified parent, * and move any unused dentries to the end of the unused @@ -788,7 +805,7 @@ void d_rehash(struct dentry * entry) * Note that we have to be a lot more careful about getting the hash * switched - we have to switch the hash value properly even if it * then no longer matches the actual (corrupted) string of the target. - * The has value has to match the hash queue that the dentry is on.. + * The hash value has to match the hash queue that the dentry is on.. */ static inline void switch_names(struct dentry * dentry, struct dentry * target) { @@ -948,7 +965,12 @@ global_root: asmlinkage long sys_getcwd(char *buf, unsigned long size) { int error; - struct dentry *pwd = current->fs->pwd; + struct vfsmount *pwdmnt; + struct dentry *pwd; + + lock_kernel(); + pwdmnt = mntget(current->fs->pwdmnt); + pwd = dget(current->fs->pwd); error = -ENOENT; /* Has the current directory has been unlinked? */ @@ -959,9 +981,7 @@ asmlinkage long sys_getcwd(char *buf, unsigned long size) unsigned long len; char * cwd; - lock_kernel(); cwd = d_path(pwd, current->fs->pwdmnt, page, PAGE_SIZE); - unlock_kernel(); error = -ERANGE; len = PAGE_SIZE + page - cwd; @@ -973,6 +993,9 @@ asmlinkage long sys_getcwd(char *buf, unsigned long size) free_page((unsigned long) page); } } + dput(pwd); + mntput(pwdmnt); + unlock_kernel(); return error; } @@ -1010,6 +1033,34 @@ int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry) return result; } +void d_genocide(struct dentry *root) +{ + struct dentry *this_parent = root; + struct list_head *next; + +repeat: + next = this_parent->d_subdirs.next; +resume: + while (next != &this_parent->d_subdirs) { + struct list_head *tmp = next; + struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + next = tmp->next; + if (d_unhashed(dentry)||!dentry->d_inode) + continue; + if (!list_empty(&dentry->d_subdirs)) { + this_parent = dentry; + goto repeat; + } + dentry->d_count--; + } + if (this_parent != root) { + next = this_parent->d_child.next; + this_parent->d_count--; + this_parent = this_parent->d_parent; + goto resume; + } +} + /** * find_inode_number - check for dentry with name * @dir: directory to check diff --git a/fs/devfs/base.c b/fs/devfs/base.c index 567156868..040e0b79a 100644 --- a/fs/devfs/base.c +++ b/fs/devfs/base.c @@ -555,7 +555,7 @@ typedef struct wait_queue *wait_queue_head_t; #define OOPS(format, args...) {printk (format, ## args); \ printk ("Forcing Oops\n"); \ - *(int *) 0 = 0;} + BUG();} struct directory_type { @@ -729,17 +729,21 @@ static struct file_operations devfsd_fops = /* Support functions follow */ + +/** + * search_for_entry_in_dir - Search for a devfs entry inside another devfs entry. + * @parent: The parent devfs entry. + * @name: The name of the entry. + * @namelen: The number of characters in @name. + * @traverse_symlink: If %TRUE then the entry is traversed if it is a symlink. + * + * Returns a pointer to the entry on success, else %NULL. + */ + static struct devfs_entry *search_for_entry_in_dir (struct devfs_entry *parent, const char *name, unsigned int namelen, int traverse_symlink) -/* [SUMMARY] Search for a devfs entry inside another devfs entry. - <parent> The parent devfs entry. - <name> The name of the entry. - <namelen> The number of characters in <<name>>. - <traverse_symlink> If TRUE then the entry is traversed if it is a symlink. - [RETURNS] A pointer to the entry on success, else NULL. -*/ { struct devfs_entry *curr; @@ -783,10 +787,14 @@ static struct devfs_entry *create_entry (struct devfs_entry *parent, return new; } /* End Function create_entry */ + +/** + * get_root_entry - Get the root devfs entry. + * + * Returns the root devfs entry on success, else %NULL. + */ + static struct devfs_entry *get_root_entry (void) -/* [SUMMARY] Get the root devfs entry. - [RETURNS] The root devfs entry on success, else NULL. -*/ { struct devfs_entry *new; @@ -809,23 +817,27 @@ static struct devfs_entry *get_root_entry (void) return root_entry; } /* End Function get_root_entry */ + +/** + * search_for_entry - Search for an entry in the devfs tree. + * @dir: The parent directory to search from. If this is %NULL the root is used + * @name: The name of the entry. + * @namelen: The number of characters in @name. + * @mkdir: If %TRUE intermediate directories are created as needed. + * @mkfile: If %TRUE the file entry is created if it doesn't exist. + * @is_new: If the returned entry was newly made, %TRUE is written here. If + * this is %NULL nothing is written here. + * @traverse_symlink: If %TRUE then symbolic links are traversed. + * + * If the entry is created, then it will be in the unregistered state. + * Returns a pointer to the entry on success, else %NULL. + */ + static struct devfs_entry *search_for_entry (struct devfs_entry *dir, const char *name, unsigned int namelen, int mkdir, int mkfile, int *is_new, int traverse_symlink) -/* [SUMMARY] Search for an entry in the devfs tree. - <dir> The parent directory to search from. If this is NULL the root is used - <name> The name of the entry. - <namelen> The number of characters in <<name>>. - <mkdir> If TRUE intermediate directories are created as needed. - <mkfile> If TRUE the file entry is created if it doesn't exist. - <is_new> If the returned entry was newly made, TRUE is written here. If - this is NULL nothing is written here. - <traverse_symlink> If TRUE then symbolic links are traversed. - [NOTE] If the entry is created, then it will be in the unregistered state. - [RETURNS] A pointer to the entry on success, else NULL. -*/ { int len; const char *subname, *stop, *ptr; @@ -887,16 +899,20 @@ static struct devfs_entry *search_for_entry (struct devfs_entry *dir, return NULL; } /* End Function search_for_entry */ + +/** + * find_by_dev - Find a devfs entry in a directory. + * @major: The major number to search for. + * @minor: The minor number to search for. + * @type: The type of special file to search for. This may be either + * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK. + * + * Returns the devfs_entry pointer on success, else %NULL. + */ + static struct devfs_entry *find_by_dev (struct devfs_entry *dir, unsigned int major, unsigned int minor, char type) -/* [SUMMARY] Find a devfs entry in a directory. - <major> The major number to search for. - <minor> The minor number to search for. - <type> The type of special file to search for. This may be either - DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. - [RETURNS] The devfs_entry pointer on success, else NULL. -*/ { struct devfs_entry *entry, *de; @@ -926,26 +942,31 @@ static struct devfs_entry *find_by_dev (struct devfs_entry *dir, return NULL; } /* End Function find_by_dev */ + +/** + * 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. + * @name: The name of the entry. This is ignored if @handle is not %NULL. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @major: The major number. This is used if @handle and @name are %NULL. + * @minor: The minor number. This is used if @handle and @name are %NULL. + * NOTE: If @major and @minor are both 0, searching by major and minor + * numbers is disabled. + * @type: The type of special file to search for. This may be either + * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK. + * @traverse_symlink: If %TRUE then symbolic links are traversed. + * + * FIXME: What the hell is @handle? - ch + * Returns the devfs_entry pointer on success, else %NULL. + */ + static struct devfs_entry *find_entry (devfs_handle_t dir, const char *name, unsigned int namelen, unsigned int major, unsigned int minor, char type, int traverse_symlink) -/* [SUMMARY] 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. - <name> The name of the entry. This is ignored if <<handle>> is not NULL. - <namelen> The number of characters in <<name>>, not including a NULL - terminator. If this is 0, then <<name>> must be NULL-terminated and the - length is computed internally. - <major> The major number. This is used if <<handle>> and <<name>> are NULL. - <minor> The minor number. This is used if <<handle>> and <<name>> are NULL. - [NOTE] If <<major>> and <<minor>> are both 0, searching by major and minor - numbers is disabled. - <type> The type of special file to search for. This may be either - DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. - <traverse_symlink> If TRUE then symbolic links are traversed. - [RETURNS] The devfs_entry pointer on success, else NULL. -*/ { struct devfs_entry *entry; @@ -991,11 +1012,13 @@ static struct devfs_inode *get_devfs_inode_from_vfs_inode (struct inode *inode) return fs_info->table[inode->i_ino - FIRST_INODE]; } /* End Function get_devfs_inode_from_vfs_inode */ + +/** + * free_dentries - Free the dentries for a device entry and invalidate inodes. + * @de: The entry. + */ + static void free_dentries (struct devfs_entry *de) -/* [SUMMARY] Free the dentries for a device entry and invalidate inodes. - <de> The entry. - [RETURNS] Nothing. -*/ { struct devfs_inode *di; struct dentry *dentry; @@ -1015,11 +1038,15 @@ static void free_dentries (struct devfs_entry *de) } } /* End Function free_dentries */ + +/** + * is_devfsd_or_child - Test if the current process is devfsd or one of its children. + * fs_info: The filesystem information. + * + * Returns %TRUE if devfsd or child, else %FALSE. + */ + static int is_devfsd_or_child (struct fs_info *fs_info) -/* [SUMMARY] Test if the current process is devfsd or one of its children. - <fs_info> The filesystem information. - [RETURNS] TRUE if devfsd or child, else FALSE. -*/ { struct task_struct *p; @@ -1030,20 +1057,28 @@ static int is_devfsd_or_child (struct fs_info *fs_info) return (FALSE); } /* End Function is_devfsd_or_child */ + +/** + * devfsd_queue_empty - Test if devfsd has work pending in its event queue. + * @fs_info: The filesystem information. + * + * Returns %TRUE if the queue is empty, else %FALSE. + */ + static inline int devfsd_queue_empty (struct fs_info *fs_info) -/* [SUMMARY] Test if devfsd has work pending in its event queue. - <fs_info> The filesystem information. - [RETURNS] TRUE if the queue is empty, else FALSE. -*/ { return (fs_info->devfsd_buf_out == fs_info->devfsd_buf_in) ? TRUE : FALSE; } /* End Function devfsd_queue_empty */ + +/** + * wait_for_devfsd_finished - Wait for devfsd to finish processing its event queue. + * @fs_info: The filesystem information. + * + * Returns %TRUE if no more waiting will be required, else %FALSE. + */ + static int wait_for_devfsd_finished (struct fs_info *fs_info) -/* [SUMMARY] Wait for devfsd to finish processing its event queue. - <fs_info> The filesystem information. - [RETURNS] TRUE if no more waiting will be required, else FALSE. -*/ { DECLARE_WAITQUEUE (wait, current); @@ -1059,17 +1094,21 @@ static int wait_for_devfsd_finished (struct fs_info *fs_info) return (TRUE); } /* End Function wait_for_devfsd_finished */ + +/** + * devfsd_notify_one - Notify a single devfsd daemon of a change. + * @data: Data to be passed. + * @type: The type of change. + * @mode: The mode of the entry. + * @uid: The user ID. + * @gid: The group ID. + * @fs_info: The filesystem info. + * + * Returns %TRUE if an event was queued and devfsd woken up, else %FALSE. + */ + static int devfsd_notify_one (void *data, unsigned int type, umode_t mode, uid_t uid, gid_t gid, struct fs_info *fs_info) -/* [SUMMARY] Notify a single devfsd daemon of a change. - <data> Data to be passed. - <type> The type of change. - <mode> The mode of the entry. - <uid> The user ID. - <gid> The group ID. - <fs_info> The filesystem info. - [RETURNS] TRUE if an event was queued and devfsd woken up, else FALSE. -*/ { unsigned int next_pos; unsigned long flags; @@ -1103,14 +1142,16 @@ static int devfsd_notify_one (void *data, unsigned int type, umode_t mode, return (TRUE); } /* End Function devfsd_notify_one */ + +/** + * devfsd_notify - Notify all devfsd daemons of a change. + * @de: The devfs entry that has changed. + * @type: The type of change event. + * @wait: If TRUE, the functions waits for all daemons to finish processing + * the event. + */ + static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait) -/* [SUMMARY] Notify all devfsd daemons of a change. - <de> The devfs entry that has changed. - <type> The type of change event. - <wait> If TRUE, the functions waits for all daemons to finish processing - the event. - [RETURNS] Nothing. -*/ { struct fs_info *fs_info; @@ -1122,35 +1163,38 @@ static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait) } } /* End Function devfsd_notify */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register - Register a device entry. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * new name is relative to the root of the devfs. + * @name: The name of the entry. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). + * @major: The major number. Not needed for regular files. + * @minor: The minor number. Not needed for regular files. + * @mode: The default file mode. + * @uid: The default UID of the file. + * @guid: The default GID of the file. + * @ops: The &file_operations or &block_device_operations structure. + * This must not be externally deallocated. + * @info: An arbitrary pointer which will be written to the @private_data + * field of the &file structure passed to the device driver. You can set + * this to whatever you like, and change it once the file is opened (the next + * file opened will not see this change). + * + * Returns a handle which may later be used in a call to devfs_unregister(). + * On failure %NULL is returned. + */ + devfs_handle_t devfs_register (devfs_handle_t dir, const char *name, unsigned int namelen, unsigned int flags, unsigned int major, unsigned int minor, umode_t mode, uid_t uid, gid_t gid, void *ops, void *info) -/* [SUMMARY] Register a device entry. - <dir> The handle to the parent devfs directory entry. If this is NULL the - new name is relative to the root of the devfs. - <name> The name of the entry. - <namelen> The number of characters in <<name>>, not including a NULL - terminator. If this is 0, then <<name>> must be NULL-terminated and the - length is computed internally. - <flags> A set of bitwise-ORed flags (DEVFS_FL_*). - <major> The major number. Not needed for regular files. - <minor> The minor number. Not needed for regular files. - <mode> The default file mode. - <uid> The default UID of the file. - <guid> The default GID of the file. - <ops> The <<file_operations>> or <<block_device_operations>> structure. - This must not be externally deallocated. - <info> An arbitrary pointer which will be written to the <<private_data>> - field of the <<file>> structure passed to the device driver. You can set - this to whatever you like, and change it once the file is opened (the next - file opened will not see this change). - [RETURNS] A handle which may later be used in a call to - [<devfs_unregister>]. On failure NULL is returned. -*/ { int is_new; struct devfs_entry *de; @@ -1276,11 +1320,13 @@ devfs_handle_t devfs_register (devfs_handle_t dir, return de; } /* End Function devfs_register */ + +/** + * unregister - Unregister a device entry. + * @de: The entry to unregister. + */ + static void unregister (struct devfs_entry *de) -/* [SUMMARY] Unregister a device entry. - <de> The entry to unregister. - [RETURNS] Nothing. -*/ { struct devfs_entry *child; @@ -1332,13 +1378,14 @@ static void unregister (struct devfs_entry *de) } } /* End Function unregister */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_unregister - Unregister a device entry. + * de: A handle previously created by devfs_register() or returned from + * devfs_find_handle(). If this is %NULL the routine does nothing. + */ + void devfs_unregister (devfs_handle_t de) -/* [SUMMARY] Unregister a device entry. - <de> A handle previously created by [<devfs_register>] or returned from - [<devfs_find_handle>]. If this is NULL the routine does nothing. - [RETURNS] Nothing. -*/ { if (de == NULL) return; #ifdef CONFIG_DEVFS_DEBUG @@ -1349,28 +1396,31 @@ void devfs_unregister (devfs_handle_t de) unregister (de); } /* End Function devfs_unregister */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_mk_symlink Create a symbolic link in the devfs namespace. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * new name is relative to the root of the devfs. + * @name: The name of the entry. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). + * @link: The destination name. + * @linklength: The number of characters in @link, not including a %NULL + * terminator. If this is 0, then @link must be %NULL-terminated and the + * length is computed internally. + * @handle: The handle to the symlink entry is written here. This may be %NULL. + * @info: An arbitrary pointer which will be associated with the entry. + * + * Returns 0 on success, else a negative error code is returned. + */ + int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int namelen, unsigned int flags, const char *link, unsigned int linklength, devfs_handle_t *handle, void *info) -/* [SUMMARY] Create a symbolic link in the devfs namespace. - <dir> The handle to the parent devfs directory entry. If this is NULL the - new name is relative to the root of the devfs. - <name> The name of the entry. - <namelen> The number of characters in <<name>>, not including a NULL - terminator. If this is 0, then <<name>> must be NULL-terminated and the - length is computed internally. - <flags> A set of bitwise-ORed flags (DEVFS_FL_*). - <link> The destination name. - <linklength> The number of characters in <<link>>, not including a NULL - terminator. If this is 0, then <<link>> must be NULL-terminated and the - length is computed internally. - <handle> The handle to the symlink entry is written here. This may be NULL. - <info> An arbitrary pointer which will be associated with the entry. - [RETURNS] 0 on success, else a negative error code is returned. -*/ { int is_new; char *newname; @@ -1439,23 +1489,26 @@ int devfs_mk_symlink (devfs_handle_t dir, return 0; } /* End Function devfs_mk_symlink */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_mk_dir - Create a directory in the devfs namespace. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * new name is relative to the root of the devfs. + * @name: The name of the entry. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @info: An arbitrary pointer which will be associated with the entry. + * + * Use of this function is optional. The devfs_register() function + * will automatically create intermediate directories as needed. This function + * is provided for efficiency reasons, as it provides a handle to a directory. + * Returns a handle which may later be used in a call to devfs_unregister(). + * On failure %NULL is returned. + */ + devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, unsigned int namelen, void *info) -/* [SUMMARY] Create a directory in the devfs namespace. - <dir> The handle to the parent devfs directory entry. If this is NULL the - new name is relative to the root of the devfs. - <name> The name of the entry. - <namelen> The number of characters in <<name>>, not including a NULL - terminator. If this is 0, then <<name>> must be NULL-terminated and the - length is computed internally. - <info> An arbitrary pointer which will be associated with the entry. - [NOTE] Use of this function is optional. The [<devfs_register>] function - will automatically create intermediate directories as needed. This function - is provided for efficiency reasons, as it provides a handle to a directory. - [RETURNS] A handle which may later be used in a call to - [<devfs_unregister>]. On failure NULL is returned. -*/ { int is_new; struct devfs_entry *de; @@ -1499,29 +1552,31 @@ devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, return de; } /* End Function devfs_mk_dir */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_find_handle - Find the handle of 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. + * @name: The name of the entry. + * @namelen: The number of characters in @name, not including a %NULL + * terminator. If this is 0, then @name must be %NULL-terminated and the + * length is computed internally. + * @major: The major number. This is used if @name is %NULL. + * @minor: The minor number. This is used if @name is %NULL. + * @type: The type of special file to search for. This may be either + * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK. + * @traverse_symlinks: If %TRUE then symlink entries in the devfs namespace are + * traversed. Symlinks pointing out of the devfs namespace will cause a + * failure. Symlink traversal consumes stack space. + * + * Returns a handle which may later be used in a call to devfs_unregister(), + * devfs_get_flags(), or devfs_set_flags(). On failure %NULL is returned. + */ + devfs_handle_t devfs_find_handle (devfs_handle_t dir, const char *name, unsigned int namelen, unsigned int major, unsigned int minor, char type, int traverse_symlinks) -/* [SUMMARY] Find the handle of 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. - <name> The name of the entry. - <namelen> The number of characters in <<name>>, not including a NULL - terminator. If this is 0, then <<name>> must be NULL-terminated and the - length is computed internally. - <major> The major number. This is used if <<name>> is NULL. - <minor> The minor number. This is used if <<name>> is NULL. - <type> The type of special file to search for. This may be either - DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. - <traverse_symlinks> If TRUE then symlink entries in the devfs namespace are - traversed. Symlinks pointing out of the devfs namespace will cause a - failure. Symlink traversal consumes stack space. - [RETURNS] A handle which may later be used in a call to - [<devfs_unregister>], [<devfs_get_flags>], or [<devfs_set_flags>]. - On failure NULL is returned. -*/ { devfs_handle_t de; @@ -1533,13 +1588,16 @@ devfs_handle_t devfs_find_handle (devfs_handle_t dir, return de; } /* End Function devfs_find_handle */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_flags - Get the flags for a devfs entry. + * @de: The handle to the device entry. + * @flags: The flags are written here. + * + * Returns 0 on success, else a negative error code. + */ + int devfs_get_flags (devfs_handle_t de, unsigned int *flags) -/* [SUMMARY] Get the flags for a devfs entry. - <de> The handle to the device entry. - <flags> The flags are written here. - [RETURNS] 0 on success, else a negative error code. -*/ { unsigned int fl = 0; @@ -1557,13 +1615,16 @@ int devfs_get_flags (devfs_handle_t de, unsigned int *flags) return 0; } /* End Function devfs_get_flags */ -/*PUBLIC_FUNCTION*/ + +/* + * devfs_set_flags - Set the flags for a devfs entry. + * @de: The handle to the device entry. + * @flags: The flags to set. Unset flags are cleared. + * + * Returns 0 on success, else a negative error code. + */ + int devfs_set_flags (devfs_handle_t de, unsigned int flags) -/* [SUMMARY] Set the flags for a devfs entry. - <de> The handle to the device entry. - <flags> The flags to set. Unset flags are cleared. - [RETURNS] 0 on success, else a negative error code. -*/ { if (de == NULL) return -EINVAL; if (!de->registered) return -ENODEV; @@ -1592,15 +1653,18 @@ int devfs_set_flags (devfs_handle_t de, unsigned int flags) return 0; } /* End Function devfs_set_flags */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_maj_min - Get the major and minor numbers for a devfs entry. + * @de: The handle to the device entry. + * @major: The major number is written here. This may be %NULL. + * @minor: The minor number is written here. This may be %NULL. + * + * Returns 0 on success, else a negative error code. + */ + int devfs_get_maj_min (devfs_handle_t de, unsigned int *major, unsigned int *minor) -/* [SUMMARY] Get the major and minor numbers for a devfs entry. - <de> The handle to the device entry. - <major> The major number is written here. This may be NULL. - <minor> The minor number is written here. This may be NULL. - [RETURNS] 0 on success, else a negative error code. -*/ { if (de == NULL) return -EINVAL; if (!de->registered) return -ENODEV; @@ -1611,12 +1675,15 @@ int devfs_get_maj_min (devfs_handle_t de, unsigned int *major, return 0; } /* End Function devfs_get_maj_min */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_handle_from_inode - Get the devfs handle for a VFS inode. + * @inode: The VFS inode. + * + * Returns the devfs handle on success, else %NULL. + */ + devfs_handle_t devfs_get_handle_from_inode (struct inode *inode) -/* [SUMMARY] Get the devfs handle for a VFS inode. - <inode> The VFS inode. - [RETURNS] The devfs handle on success, else NULL. -*/ { struct devfs_inode *di; @@ -1627,16 +1694,19 @@ devfs_handle_t devfs_get_handle_from_inode (struct inode *inode) return di->de; } /* End Function devfs_get_handle_from_inode */ -/*PUBLIC_FUNCTION*/ + +/** + * 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. + * @buflen: The length of the buffer. + * + * Returns the offset in the buffer where the pathname starts on success, + * else a negative error code. + */ + int devfs_generate_path (devfs_handle_t de, char *path, int buflen) -/* [SUMMARY] 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. - <buflen> The length of the buffer. - [RETURNS] The offset in the buffer where the pathname starts on success, - else a negative error code. -*/ { int pos; @@ -1656,12 +1726,15 @@ int devfs_generate_path (devfs_handle_t de, char *path, int buflen) return pos; } /* End Function devfs_generate_path */ -/*PUBLIC_FUNCTION*/ + +/** + * 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. + */ + void *devfs_get_ops (devfs_handle_t de) -/* [SUMMARY] 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. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; @@ -1670,13 +1743,16 @@ void *devfs_get_ops (devfs_handle_t de) return NULL; } /* End Function devfs_get_ops */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_set_file_size - Set the file size for a devfs regular file. + * de: The handle to the device entry. + * size: The new file size. + * + * Returns 0 on success, else a negative error code. + */ + int devfs_set_file_size (devfs_handle_t de, unsigned long size) -/* [SUMMARY] Set the file size for a devfs regular file. - <de> The handle to the device entry. - <size> The new file size. - [RETURNS] 0 on success, else a negative error code. -*/ { struct devfs_inode *di; @@ -1694,24 +1770,28 @@ int devfs_set_file_size (devfs_handle_t de, unsigned long size) return 0; } /* End Function devfs_set_file_size */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_info - Get the info pointer written to private_data of @de upon open. + * @de: The handle to the device entry. + * + * Returns the info pointer. + */ void *devfs_get_info (devfs_handle_t de) -/* [SUMMARY] Get the info pointer written to <<private_data>> upon open. - <de> The handle to the device entry. - [RETURNS] The info pointer. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; return de->info; } /* End Function devfs_get_info */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_set_info - Set the info pointer written to private_data upon open. + * @de: The handle to the device entry. + * + * Returns 0 on success, else a negative error code. + */ int devfs_set_info (devfs_handle_t de, void *info) -/* [SUMMARY] Set the info pointer written to <<private_data>> upon open. - <de> The handle to the device entry. - [RETURNS] 0 on success, else a negative error code. -*/ { if (de == NULL) return -EINVAL; if (!de->registered) return -EINVAL; @@ -1719,24 +1799,29 @@ int devfs_set_info (devfs_handle_t de, void *info) return 0; } /* End Function devfs_set_info */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_parent - Get the parent device entry. + * @de: The handle to the device entry. + * + * Returns the parent device entry if it exists, else %NULL. + */ devfs_handle_t devfs_get_parent (devfs_handle_t de) -/* [SUMMARY] Get the parent device entry. - <de> The handle to the device entry. - [RETURNS] The parent device entry if it exists, else NULL. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; return de->parent; } /* End Function devfs_get_parent */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_first_child - Get the first leaf node in a directory. + * @de: The handle to the device entry. + * + * Returns the leaf node device entry if it exists, else %NULL. + */ + devfs_handle_t devfs_get_first_child (devfs_handle_t de) -/* [SUMMARY] Get the first leaf node in a directory. - <de> The handle to the device entry. - [RETURNS] The leaf node device entry if it exists, else NULL. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; @@ -1744,27 +1829,31 @@ devfs_handle_t devfs_get_first_child (devfs_handle_t de) return de->u.dir.first; } /* End Function devfs_get_first_child */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_next_sibling - Get the next sibling leaf node. for a device entry. + * @de: The handle to the device entry. + * + * Returns the leaf node device entry if it exists, else %NULL. + */ + devfs_handle_t devfs_get_next_sibling (devfs_handle_t de) -/* [SUMMARY] Get the next sibling leaf node. for a device entry. - <de> The handle to the device entry. - [RETURNS] The leaf node device entry if it exists, else NULL. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; return de->next; } /* End Function devfs_get_next_sibling */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_auto_unregister - Configure a devfs entry to be automatically unregistered. + * @master: The master devfs entry. Only one slave may be registered. + * @slave: The devfs entry which will be automatically unregistered when the + * master entry is unregistered. It is illegal to call devfs_unregister() + * on this entry. + */ + void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave) -/* [SUMMARY] Configure a devfs entry to be automatically unregistered. - <master> The master devfs entry. Only one slave may be registered. - <slave> The devfs entry which will be automatically unregistered when the - master entry is unregistered. It is illegal to call [<devfs_unregister>] on - this entry. - [RETURNS] Nothing. -*/ { if (master == NULL) return; if (master->slave != NULL) @@ -1779,25 +1868,30 @@ void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave) master->slave = slave; } /* End Function devfs_auto_unregister */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_unregister_slave - Get the slave entry which will be automatically unregistered. + * @master: The master devfs entry. + * + * Returns the slave which will be unregistered when @master is unregistered. + */ + devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master) -/* [SUMMARY] Get the slave entry which will be automatically unregistered. - <master> The master devfs entry. - [RETURNS] The slave which will be unregistered when <<master>> is - unregistered. -*/ { if (master == NULL) return NULL; return master->slave; } /* End Function devfs_get_unregister_slave */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_get_name - Get the name for a device entry in its parent directory. + * @de: The handle to the device entry. + * @namelen: The length of the name is written here. This may be %NULL. + * + * Returns the name on success, else %NULL. + */ + const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen) -/* [SUMMARY] Get the name for a device entry in its parent directory. - <de> The handle to the device entry. - <namelen> The length of the name is written here. This may be NULL. - [RETURNS] The name on success, else NULL. -*/ { if (de == NULL) return NULL; if (!de->registered) return NULL; @@ -1805,61 +1899,73 @@ const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen) return de->name; } /* End Function devfs_get_name */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register_chrdev - Optionally register a conventional character driver. + * @major: The major number for the driver. + * @name: The name of the driver (as seen in /proc/devices). + * @fops: The &file_operations structure pointer. + * + * This function will register a character driver provided the "devfs=only" + * option was not provided at boot time. + * Returns 0 on success, else a negative error code on failure. + */ + int devfs_register_chrdev (unsigned int major, const char *name, struct file_operations *fops) -/* [SUMMARY] Optionally register a conventional character driver. - [PURPOSE] This function will register a character driver provided the - "devfs=only" option was not provided at boot time. - <major> The major number for the driver. - <name> The name of the driver (as seen in /proc/devices). - <fops> The file_operations structure pointer. - [RETURNS] 0 on success, else a negative error code on failure. -*/ { if (boot_options & OPTION_ONLY) return 0; return register_chrdev (major, name, fops); } /* End Function devfs_register_chrdev */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register_blkdev - Optionally register a conventional block driver. + * @major: The major number for the driver. + * @name: The name of the driver (as seen in /proc/devices). + * @bdops: The &block_device_operations structure pointer. + * + * This function will register a block driver provided the "devfs=only" + * option was not provided at boot time. + * Returns 0 on success, else a negative error code on failure. + */ + int devfs_register_blkdev (unsigned int major, const char *name, struct block_device_operations *bdops) -/* [SUMMARY] Optionally register a conventional block driver. - [PURPOSE] This function will register a block driver provided the - "devfs=only" option was not provided at boot time. - <major> The major number for the driver. - <name> The name of the driver (as seen in /proc/devices). - <bdops> The block_device_operations structure pointer. - [RETURNS] 0 on success, else a negative error code on failure. -*/ { if (boot_options & OPTION_ONLY) return 0; return register_blkdev (major, name, bdops); } /* End Function devfs_register_blkdev */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_unregister_chrdev - Optionally unregister a conventional character driver. + * major: The major number for the driver. + * name: The name of the driver (as seen in /proc/devices). + * + * This function will unregister a character driver provided the "devfs=only" + * option was not provided at boot time. + * Returns 0 on success, else a negative error code on failure. + */ + int devfs_unregister_chrdev (unsigned int major, const char *name) -/* [SUMMARY] Optionally unregister a conventional character driver. - [PURPOSE] This function will unregister a character driver provided the - "devfs=only" option was not provided at boot time. - <major> The major number for the driver. - <name> The name of the driver (as seen in /proc/devices). - [RETURNS] 0 on success, else a negative error code on failure. -*/ { if (boot_options & OPTION_ONLY) return 0; return unregister_chrdev (major, name); } /* End Function devfs_unregister_chrdev */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_unregister_blkdev - Optionally unregister a conventional block driver. + * @major: The major number for the driver. + * @name: The name of the driver (as seen in /proc/devices). + * + * This function will unregister a block driver provided the "devfs=only" + * option was not provided at boot time. + * Returns 0 on success, else a negative error code on failure. + */ + int devfs_unregister_blkdev (unsigned int major, const char *name) -/* [SUMMARY] Optionally unregister a conventional block driver. - [PURPOSE] This function will unregister a block driver provided the - "devfs=only" option was not provided at boot time. - <major> The major number for the driver. - <name> The name of the driver (as seen in /proc/devices). - [RETURNS] 0 on success, else a negative error code on failure. -*/ { if (boot_options & OPTION_ONLY) return 0; return unregister_blkdev (major, name); @@ -1867,13 +1973,13 @@ int devfs_unregister_blkdev (unsigned int major, const char *name) #ifndef MODULE -/*UNPUBLISHED_FUNCTION*/ +/** + * devfs_setup - Process kernel boot options. + * @str: The boot options after the "devfs=". + * @unused: Unused. + */ + SETUP_STATIC int __init devfs_setup (char *str) -/* [SUMMARY] Process kernel boot options. - <str> The boot options after the "devfs=". - <unused> Unused. - [RETURNS] Nothing. -*/ { while ( (*str != '\0') && !isspace (*str) ) { @@ -2027,13 +2133,17 @@ static void update_devfs_inode_from_entry (struct devfs_inode *di) } } /* End Function update_devfs_inode_from_entry */ + +/** + * create_devfs_inode - Create a devfs inode entry. + * @de: The devfs entry to associate the new inode with. + * @fs_info: The FS info. + * + * Returns a pointer to the devfs inode on success, else %NULL. + */ + static struct devfs_inode *create_devfs_inode (struct devfs_entry *de, struct fs_info *fs_info) -/* [SUMMARY] Create a devfs inode entry. - <de> The devfs entry to associate the new inode with. - <fs_info> The FS info. - [RETURNS] A pointer to the devfs inode on success, else NULL. -*/ { struct devfs_inode *di, **table; @@ -2077,18 +2187,22 @@ static struct devfs_inode *create_devfs_inode (struct devfs_entry *de, return di; } /* End Function create_devfs_inode */ + +/** + * try_modload - Notify devfsd of an inode lookup. + * @parent: The parent devfs entry. + * @fs_info: The filesystem info. + * @name: The device name. + * @namelen: The number of characters in @name. + * @buf: A working area that will be used. This must not go out of scope until + * devfsd is idle again. + * + * Returns 0 on success, else a negative error code. + */ + static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info, const char *name, unsigned namelen, char buf[STRING_LENGTH]) -/* [SUMMARY] Notify devfsd of an inode lookup. - <parent> The parent devfs entry. - <fs_info> The filesystem info. - <name> The device name. - <namelen> The number of characters in <<name>>. - <buf> A working area that will be used. This must not go out of scope until - devfsd is idle again. - [RETURNS] 0 on success, else a negative error code. -*/ { int pos; @@ -2136,11 +2250,15 @@ static void delete_fs (struct fs_info *fs_info) kfree (fs_info); } /* End Function delete_fs */ + +/** + * check_disc_changed - Check if a removable disc was changed. + * @de: The device. + * + * Returns 1 if the media was changed, else 0. + */ + static int check_disc_changed (struct devfs_entry *de) -/* [SUMMARY] Check if a removable disc was changed. - <de> The device. - [RETURNS] 1 if the media was changed, else 0. -*/ { int tmp; kdev_t dev = MKDEV (de->u.fcb.u.device.major, de->u.fcb.u.device.minor); @@ -2166,11 +2284,13 @@ static int check_disc_changed (struct devfs_entry *de) return 1; } /* End Function check_disc_changed */ + +/** + * scan_dir_for_removable - Scan a directory for removable media devices and check media. + * @dir: The directory. + */ + static void scan_dir_for_removable (struct devfs_entry *dir) -/* [SUMMARY] Scan a directory for removable media devices and check media. - <dir> The directory. - [RETURNS] Nothing. -*/ { struct devfs_entry *de; @@ -2184,14 +2304,17 @@ static void scan_dir_for_removable (struct devfs_entry *dir) } } /* End Function scan_dir_for_removable */ +/** + * get_removable_partition - Get removable media partition. + * @dir: The parent directory. + * @name: The name of the entry. + * @namelen: The number of characters in <<name>>. + * + * Returns 1 if the media was changed, else 0. + */ + static int get_removable_partition (struct devfs_entry *dir, const char *name, unsigned int namelen) -/* [SUMMARY] Get removable media partition. - <dir> The parent directory. - <name> The name of the entry. - <namelen> The number of characters in <<name>>. - [RETURNS] 1 if the media was changed, else 0. -*/ { struct devfs_entry *de; @@ -2365,15 +2488,19 @@ static struct super_operations devfs_sops = statfs: devfs_statfs, }; + +/** + * get_vfs_inode - Get a VFS inode. + * @sb: The super block. + * @di: The devfs inode. + * @dentry The dentry to register with the devfs inode. + * + * Returns the inode on success, else %NULL. + */ + static struct inode *get_vfs_inode (struct super_block *sb, struct devfs_inode *di, struct dentry *dentry) -/* [SUMMARY] Get a VFS inode. - <sb> The super block. - <di> The devfs inode. - <dentry> The dentry to register with the devfs inode. - [RETURNS] The inode on success, else NULL. -*/ { struct inode *inode; @@ -2551,9 +2678,13 @@ static struct file_operations devfs_fops = /* 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) -/* [SUMMARY] Callback for when a dentry is freed. -*/ { #ifdef CONFIG_DEVFS_DEBUG struct inode *inode = dentry->d_inode; @@ -2564,9 +2695,13 @@ static void devfs_d_release (struct dentry *dentry) #endif } /* End Function devfs_d_release */ +/** + * devfs_d_iput - Callback for when a dentry loses its inode. + * @dentry: The dentry. + * @inode: The inode. + */ + static void devfs_d_iput (struct dentry *dentry, struct inode *inode) -/* [SUMMARY] Callback for when a dentry loses its inode. -*/ { struct devfs_inode *di; @@ -2587,7 +2722,7 @@ static void devfs_d_iput (struct dentry *dentry, struct inode *inode) iput (inode); } /* End Function devfs_d_iput */ -static void devfs_d_delete (struct dentry *dentry); +static int devfs_d_delete (struct dentry *dentry); static struct dentry_operations devfs_dops = { @@ -2606,9 +2741,12 @@ static struct dentry_operations devfs_wait_dops = d_revalidate: devfs_d_revalidate_wait, }; -static void devfs_d_delete (struct dentry *dentry) -/* [SUMMARY] Callback for when all files for a dentry are closed. -*/ +/** + * devfs_d_delete - Callback for when all files for a dentry are closed. + * @detry: The dentry. + */ + +static int devfs_d_delete (struct dentry *dentry) { struct inode *inode = dentry->d_inode; struct devfs_inode *di; @@ -2623,8 +2761,7 @@ static void devfs_d_delete (struct dentry *dentry) printk ("%s: d_delete(): dropping negative dentry: %p\n", DEVFS_NAME, dentry); #endif - d_drop (dentry); - return; + return 1; } fs_info = inode->i_sb->u.generic_sbp; di = get_devfs_inode_from_vfs_inode (inode); @@ -2633,16 +2770,16 @@ static void devfs_d_delete (struct dentry *dentry) printk ("%s: d_delete(): dentry: %p inode: %p devfs_inode: %p\n", DEVFS_NAME, dentry, inode, di); #endif - if (di == NULL) return; - if (di->de == NULL) return; + if (di == NULL) return 0; + if (di->de == NULL) return 0; if ( !S_ISCHR (di->mode) && !S_ISBLK (di->mode) && !S_ISREG (di->mode) ) - return; - if (!di->de->u.fcb.open) return; + return 0; + if (!di->de->u.fcb.open) return 0; di->de->u.fcb.open = FALSE; if (di->de->u.fcb.aopen_notify) devfsd_notify_one (di->de, DEVFSD_NOTIFY_CLOSE, inode->i_mode, current->euid, current->egid, fs_info); - if (!di->de->u.fcb.auto_owner) return; + if (!di->de->u.fcb.auto_owner) return 0; /* Change the ownership/protection back */ di->mode = (di->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; di->uid = di->de->u.fcb.default_uid; @@ -2650,6 +2787,7 @@ static void devfs_d_delete (struct dentry *dentry) inode->i_mode = di->mode; inode->i_uid = di->uid; inode->i_gid = di->gid; + return 0; } /* End Function devfs_d_delete */ static int devfs_d_revalidate_wait (struct dentry *dentry, int flags) @@ -3431,11 +3569,9 @@ int __init init_devfs_fs (void) void __init mount_devfs_fs (void) { int err; - extern long do_sys_mount (char *dev_name, char *dir_name, - char *type, int flags, void *data); if ( (boot_options & OPTION_NOMOUNT) ) return; - err = do_sys_mount ("none", "/dev", "devfs", 0, ""); + err = do_mount ("none", "/dev", "devfs", 0, ""); if (err == 0) printk ("Mounted devfs on /dev\n"); else printk ("Warning: unable to mount devfs, err: %d\n", err); } /* End Function mount_devfs_fs */ diff --git a/fs/devfs/util.c b/fs/devfs/util.c index 7e22ae7cb..fe4746448 100644 --- a/fs/devfs/util.c +++ b/fs/devfs/util.c @@ -38,13 +38,14 @@ /* Private functions follow */ +/** + * _devfs_convert_name - Convert from an old style location-based name to new style. + * @new: The new name will be written here. + * @old: The old name. + * @disc: If true, disc partitioning information should be processed. + */ + static void __init _devfs_convert_name (char *new, const char *old, int disc) -/* [SUMMARY] Convert from an old style location-based name to new style. - <new> The new name will be written here. - <old> The old name. - <disc> If true, disc partitioning information should be processed. - [RETURNS] Nothing. -*/ { int host, bus, target, lun; char *ptr; @@ -73,12 +74,12 @@ static void __init _devfs_convert_name (char *new, const char *old, int disc) /* Public functions follow */ -/*PUBLIC_FUNCTION*/ +/** + * devfs_make_root - Create the root FS device entry if required. + * @name: The name of the root FS device, as passed by "root=". + */ + void __init devfs_make_root (const char *name) -/* [SUMMARY] Create the root FS device entry if required. - <name> The name of the root FS device, as passed by "root=". - [RETURNS] Nothing. -*/ { char dest[64]; @@ -97,12 +98,13 @@ void __init devfs_make_root (const char *name) devfs_mk_symlink (NULL, name, 0, DEVFS_FL_DEFAULT, dest, 0, NULL,NULL); } /* End Function devfs_make_root */ -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register_tape - Register a tape device in the "/dev/tapes" hierarchy. + * @de: Any tape device entry in the device directory. + */ + void devfs_register_tape (devfs_handle_t de) -/* [SUMMARY] Register a tape device in the "/dev/tapes" hierarchy. - <de> Any tape device entry in the device directory. - [RETURNS] Nothing. -*/ { int pos; devfs_handle_t parent, slave; @@ -122,30 +124,31 @@ void devfs_register_tape (devfs_handle_t de) } /* End Function devfs_register_tape */ EXPORT_SYMBOL(devfs_register_tape); -/*PUBLIC_FUNCTION*/ + +/** + * devfs_register_series - Register a sequence of device entries. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * new names are relative to the root of the devfs. + * @format: The printf-style format string. A single "\%u" is allowed. + * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). + * @major: The major number. Not needed for regular files. + * @minor_start: The starting minor number. Not needed for regular files. + * @mode: The default file mode. + * @uid: The default UID of the file. + * @guid: The default GID of the file. + * @ops: The &file_operations or &block_device_operations structure. + * This must not be externally deallocated. + * @info: An arbitrary pointer which will be written to the private_data + * field of the &file structure passed to the device driver. You can set + * this to whatever you like, and change it once the file is opened (the next + * file opened will not see this change). + */ + void devfs_register_series (devfs_handle_t dir, const char *format, unsigned int num_entries, unsigned int flags, unsigned int major, unsigned int minor_start, umode_t mode, uid_t uid, gid_t gid, void *ops, void *info) -/* [SUMMARY] Register a sequence of device entries. - <dir> The handle to the parent devfs directory entry. If this is NULL the - new names are relative to the root of the devfs. - <format> The printf-style format string. A single "%u" is allowed. - <flags> A set of bitwise-ORed flags (DEVFS_FL_*). - <major> The major number. Not needed for regular files. - <minor_start> The starting minor number. Not needed for regular files. - <mode> The default file mode. - <uid> The default UID of the file. - <guid> The default GID of the file. - <ops> The <<file_operations>> or <<block_device_operations>> structure. - This must not be externally deallocated. - <info> An arbitrary pointer which will be written to the <<private_data>> - field of the <<file>> structure passed to the device driver. You can set - this to whatever you like, and change it once the file is opened (the next - file opened will not see this change). - [RETURNS] Nothing. -*/ { unsigned int count; char devname[128]; @@ -45,8 +45,8 @@ #include <linux/kmod.h> #endif -static struct linux_binfmt *formats = (struct linux_binfmt *) NULL; -static spinlock_t binfmt_lock = SPIN_LOCK_UNLOCKED; +static struct linux_binfmt *formats; +static rwlock_t binfmt_lock = RW_LOCK_UNLOCKED; int register_binfmt(struct linux_binfmt * fmt) { @@ -56,17 +56,17 @@ int register_binfmt(struct linux_binfmt * fmt) return -EINVAL; if (fmt->next) return -EBUSY; - spin_lock(&binfmt_lock); + write_lock(&binfmt_lock); while (*tmp) { if (fmt == *tmp) { - spin_unlock(&binfmt_lock); + write_unlock(&binfmt_lock); return -EBUSY; } tmp = &(*tmp)->next; } fmt->next = formats; formats = fmt; - spin_unlock(&binfmt_lock); + write_unlock(&binfmt_lock); return 0; } @@ -74,16 +74,16 @@ int unregister_binfmt(struct linux_binfmt * fmt) { struct linux_binfmt ** tmp = &formats; - spin_lock(&binfmt_lock); + write_lock(&binfmt_lock); while (*tmp) { if (fmt == *tmp) { *tmp = fmt->next; - spin_unlock(&binfmt_lock); + write_unlock(&binfmt_lock); return 0; } tmp = &(*tmp)->next; } - spin_unlock(&binfmt_lock); + write_unlock(&binfmt_lock); return -EINVAL; } @@ -103,35 +103,34 @@ asmlinkage long sys_uselib(const char * library) { int fd, retval; struct file * file; - struct linux_binfmt * fmt; - lock_kernel(); fd = sys_open(library, 0, 0); - retval = fd; if (fd < 0) - goto out; + return fd; file = fget(fd); retval = -ENOEXEC; - if (file && file->f_op && file->f_op->read) { - spin_lock(&binfmt_lock); - for (fmt = formats ; fmt ; fmt = fmt->next) { - if (!fmt->load_shlib) - continue; - if (!try_inc_mod_count(fmt->module)) - continue; - spin_unlock(&binfmt_lock); - retval = fmt->load_shlib(file); - spin_lock(&binfmt_lock); - put_binfmt(fmt); - if (retval != -ENOEXEC) - break; + if (file) { + if(file->f_op && file->f_op->read) { + struct linux_binfmt * fmt; + + read_lock(&binfmt_lock); + for (fmt = formats ; fmt ; fmt = fmt->next) { + if (!fmt->load_shlib) + continue; + if (!try_inc_mod_count(fmt->module)) + continue; + read_unlock(&binfmt_lock); + retval = fmt->load_shlib(file); + read_lock(&binfmt_lock); + put_binfmt(fmt); + if (retval != -ENOEXEC) + break; + } + read_unlock(&binfmt_lock); } - spin_unlock(&binfmt_lock); + fput(file); } - fput(file); sys_close(fd); -out: - unlock_kernel(); return retval; } @@ -288,6 +287,7 @@ int setup_arg_pages(struct linux_binprm *bprm) if (!mpnt) return -ENOMEM; + down(¤t->mm->mmap_sem); { mpnt->vm_mm = current->mm; mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; @@ -311,6 +311,7 @@ int setup_arg_pages(struct linux_binprm *bprm) } stack_base += PAGE_SIZE; } + up(¤t->mm->mmap_sem); return 0; } @@ -745,14 +746,14 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) } #endif for (try=0; try<2; try++) { - spin_lock(&binfmt_lock); + read_lock(&binfmt_lock); for (fmt = formats ; fmt ; fmt = fmt->next) { int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary; if (!fn) continue; if (!try_inc_mod_count(fmt->module)) continue; - spin_unlock(&binfmt_lock); + read_unlock(&binfmt_lock); retval = fn(bprm, regs); if (retval >= 0) { put_binfmt(fmt); @@ -762,16 +763,16 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) current->did_exec = 1; return retval; } - spin_lock(&binfmt_lock); + read_lock(&binfmt_lock); put_binfmt(fmt); if (retval != -ENOEXEC) break; if (!bprm->file) { - spin_unlock(&binfmt_lock); + read_unlock(&binfmt_lock); return retval; } } - spin_unlock(&binfmt_lock); + read_unlock(&binfmt_lock); if (retval != -ENOEXEC) { break; #ifdef CONFIG_KMOD diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 90ce121ce..a3f8ae4ce 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -209,7 +209,7 @@ static inline int load_block_bitmap (struct super_block * sb, */ if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 && sb->u.ext2_sb.s_block_bitmap_number[0] == block_group && - sb->u.ext2_sb.s_block_bitmap[block_group]) { + sb->u.ext2_sb.s_block_bitmap[0]) { return 0; } /* diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index dff1b6841..3e471f42b 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -577,7 +577,6 @@ static int ext2_rmdir (struct inode * dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); - d_delete(dentry); end_rmdir: brelse (bh); @@ -619,7 +618,6 @@ static int ext2_unlink(struct inode * dir, struct dentry *dentry) mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; - d_delete(dentry); /* This also frees the inode */ end_unlink: brelse (bh); diff --git a/fs/hfs/balloc.c b/fs/hfs/balloc.c index d7e17e72f..4edd6b748 100644 --- a/fs/hfs/balloc.c +++ b/fs/hfs/balloc.c @@ -86,6 +86,8 @@ static struct hfs_bnode_ref hfs_bnode_init(struct hfs_btree * tree, retval.bn->magic = HFS_BNODE_MAGIC; retval.bn->tree = tree; retval.bn->node = node; + hfs_init_waitqueue(&retval.bn->wqueue); + hfs_init_waitqueue(&retval.bn->rqueue); hfs_bnode_lock(&retval, HFS_LOCK_WRITE); retval.bn->buf = get_new_node(tree, node); diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c index 85e3a909b..e2b975fae 100644 --- a/fs/hfs/catalog.c +++ b/fs/hfs/catalog.c @@ -60,7 +60,7 @@ typedef struct { hfs_byte_t RExtRec[12]; /* first extent record for the resource fork */ hfs_lword_t Resrv; /* reserved by Apple */ -} FIL_REC; +} __attribute__((packed)) FIL_REC; /* the catalog record for a directory */ typedef struct { @@ -74,14 +74,14 @@ typedef struct { hfs_dinfo_t UsrInfo; /* data used by the Finder */ hfs_dxinfo_t FndrInfo; /* more data used by Finder */ hfs_byte_t Resrv[16]; /* reserved by Apple */ -} DIR_REC; +} __attribute__((packed)) DIR_REC; /* the catalog record for a thread */ typedef struct { hfs_byte_t Reserv[8]; /* reserved by Apple */ hfs_lword_t ParID; /* CNID of parent directory */ struct hfs_name CName; /* The name of this entry */ -} THD_REC; +} __attribute__((packed)) THD_REC; /* A catalog tree record */ struct hfs_cat_rec { @@ -92,7 +92,7 @@ struct hfs_cat_rec { DIR_REC dir; THD_REC thd; } u; -}; +} __attribute__((packed)); /*================ File-local variables ================*/ diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index 07f4ab93d..d6de087e9 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -274,7 +274,6 @@ int hfs_unlink(struct inode * dir, struct dentry *dentry) inode->i_nlink--; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); - d_delete(dentry); update_dirs_minus(entry, 0); } @@ -328,7 +327,6 @@ int hfs_rmdir(struct inode * parent, struct dentry *dentry) inode->i_nlink = 0; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); - d_delete(dentry); update_dirs_minus(entry, 1); hfs_rmdir_put: diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index 80e47fc62..ba62615b0 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -158,7 +158,7 @@ struct hdr_hdr { hfs_byte_t filler[16]; hfs_word_t entries; hfs_byte_t descrs[12*HFS_HDR_MAX]; -}; +} __attribute__((packed)); /*================ File-local functions ================*/ diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h index 3819a685f..9552bbb34 100644 --- a/fs/hfs/hfs.h +++ b/fs/hfs/hfs.h @@ -130,7 +130,7 @@ struct hfs_name { hfs_byte_t Len; hfs_byte_t Name[31]; -}; +} __attribute__((packed)); typedef struct { hfs_word_t v; @@ -150,21 +150,21 @@ typedef struct { hfs_word_t fdFlags; hfs_point_t fdLocation; hfs_word_t fdFldr; -} hfs_finfo_t; +} __attribute__((packed)) hfs_finfo_t; typedef struct { hfs_word_t fdIconID; hfs_byte_t fdUnused[8]; hfs_word_t fdComment; hfs_lword_t fdPutAway; -} hfs_fxinfo_t; +} __attribute__((packed)) hfs_fxinfo_t; typedef struct { hfs_rect_t frRect; hfs_word_t frFlags; hfs_point_t frLocation; hfs_word_t frView; -} hfs_dinfo_t; +} __attribute__((packed)) hfs_dinfo_t; typedef struct { hfs_point_t frScroll; @@ -172,7 +172,7 @@ typedef struct { hfs_word_t frUnused; hfs_word_t frComment; hfs_lword_t frPutAway; -} hfs_dxinfo_t; +} __attribute__((packed)) hfs_dxinfo_t; union hfs_finder_info { struct { @@ -189,7 +189,7 @@ union hfs_finder_info { struct hfs_bkey { hfs_byte_t KeyLen; /* number of bytes in the key */ hfs_byte_t value[1]; /* (KeyLen) bytes of key */ -}; +} __attribute__((packed)); /* Cast to a pointer to a generic bkey */ #define HFS_BKEY(X) (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X))) @@ -200,7 +200,7 @@ struct hfs_cat_key { hfs_byte_t Resrv1; /* padding */ hfs_lword_t ParID; /* CNID of the parent dir */ struct hfs_name CName; /* The filename of the entry */ -}; +} __attribute__((packed)); /* The key used in the extents b-tree: */ struct hfs_ext_key { @@ -208,7 +208,7 @@ struct hfs_ext_key { hfs_byte_t FkType; /* HFS_FK_{DATA,RSRC} */ hfs_lword_t FNum; /* The File ID of the file */ hfs_word_t FABN; /* allocation blocks number*/ -}; +} __attribute__((packed)); /*======== Data structures kept in memory ========*/ diff --git a/fs/hfs/hfs_btree.h b/fs/hfs/hfs_btree.h index 97423b350..39d6df4d5 100644 --- a/fs/hfs/hfs_btree.h +++ b/fs/hfs/hfs_btree.h @@ -90,7 +90,7 @@ struct BTHdrRec { hfs_byte_t bthResv2; /* reserved */ hfs_lword_t bthAtrb; /* (F) attributes */ hfs_lword_t bthResv3[16]; /* Reserved */ -}; +} __attribute__((packed)); /* * struct NodeDescriptor @@ -112,7 +112,7 @@ struct NodeDescriptor { hfs_byte_t ndNHeight; /* (F) The level of this node (leaves=1) */ hfs_word_t ndNRecs; /* (V) The number of records in this node */ hfs_word_t ndResv2; /* Reserved */ -}; +} __attribute__((packed)); /* * typedef hfs_cmpfn diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c index 386a6ae79..b44fdafc8 100644 --- a/fs/hfs/mdb.c +++ b/fs/hfs/mdb.c @@ -71,7 +71,7 @@ struct raw_mdb { hfs_byte_t drXTExtRec[12]; /* extents B-tree's first 3 extents */ hfs_lword_t drCTFlSize; /* bytes in the catalog B-tree */ hfs_byte_t drCTExtRec[12]; /* catalog B-tree's first 3 extents */ -}; +} __attribute__((packed)); /*================ Global functions ================*/ diff --git a/fs/hfs/part_tbl.c b/fs/hfs/part_tbl.c index 12922c6d7..2392a0791 100644 --- a/fs/hfs/part_tbl.c +++ b/fs/hfs/part_tbl.c @@ -77,7 +77,7 @@ struct old_pmap { hfs_lword_t pdSize; hfs_lword_t pdFSID; } pdEntry[42]; -}; +} __attribute__((packed)); /*================ File-local functions ================*/ diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index dee75d7d0..b09ad98ea 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -326,7 +326,6 @@ int hpfs_unlink(struct inode *dir, struct dentry *dentry) if (r != 2) { inode->i_nlink--; hpfs_unlock_2inodes(dir, inode); - d_delete(dentry); } else { /* no space for deleting, try to truncate file */ struct iattr newattrs; int err; @@ -388,7 +387,6 @@ int hpfs_rmdir(struct inode *dir, struct dentry *dentry) dir->i_nlink--; inode->i_nlink = 0; hpfs_unlock_2inodes(dir, inode); - d_delete(dentry); } else hpfs_unlock_2inodes(dir, inode); return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0; } diff --git a/fs/inode.c b/fs/inode.c index 1bacb24a7..64373d6ad 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -377,7 +377,7 @@ static int invalidate_list(struct list_head *head, struct super_block * sb, stru * * Discard all of the inodes for a given superblock. If the discard * fails because there are busy inodes then a non zero value is returned. - * If the discard is successful all the inodes are dicarded. + * If the discard is successful all the inodes have been discarded. */ int invalidate_inodes(struct super_block * sb) @@ -470,7 +470,7 @@ int shrink_icache_memory(int priority, int gfp_mask) /* * Called with the inode lock held. * NOTE: we are not increasing the inode-refcount, you must call __iget() - * by hand after calling find_inode now! This simplify iunique and won't + * by hand after calling find_inode now! This simplifies iunique and won't * add any additional branch in the common code. */ static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque) diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 376eed9cc..cc3025ee3 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -233,11 +233,21 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc) struct rpc_clnt *clnt; struct nlm_args *argp = &req->a_args; struct nlm_res *resp = &req->a_res; + struct file *filp = argp->lock.fl.fl_file; + struct rpc_message msg; int status; dprintk("lockd: call procedure %s on %s\n", nlm_procname(proc), host->h_name); + msg.rpc_proc = proc; + msg.rpc_argp = argp; + msg.rpc_resp = resp; + if (filp) + msg.rpc_cred = nfs_file_cred(filp); + else + msg.rpc_cred = NULL; + do { if (host->h_reclaiming && !argp->reclaim) { interruptible_sleep_on(&host->h_gracewait); @@ -249,7 +259,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc) return -ENOLCK; /* Perform the RPC call. If an error occurs, try again */ - if ((status = rpc_call(clnt, proc, argp, resp, 0)) < 0) { + if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) { dprintk("lockd: rpc_call returned error %d\n", -status); switch (status) { case -EPROTONOSUPPORT: @@ -330,11 +340,31 @@ int nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) { struct nlm_host *host = req->a_host; - int status; + struct rpc_clnt *clnt; + struct nlm_args *argp = &req->a_args; + struct nlm_res *resp = &req->a_res; + struct file *file = argp->lock.fl.fl_file; + struct rpc_message msg; + int status; + dprintk("lockd: call procedure %s on %s (async)\n", + nlm_procname(proc), host->h_name); + + /* If we have no RPC client yet, create one. */ + if ((clnt = nlm_bind_host(host)) == NULL) + return -ENOLCK; + + /* bootstrap and kick off the async RPC call */ + msg.rpc_proc = proc; + msg.rpc_argp = argp; + msg.rpc_resp =resp; + if (file) + msg.rpc_cred = nfs_file_cred(file); + else + msg.rpc_cred = NULL; /* Increment host refcount */ nlm_get_host(host); - status = nlmsvc_async_call(req, proc, callback); + status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req); if (status < 0) nlm_release_host(host); return status; diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 97a9d27ef..279fcc3c1 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -533,8 +533,10 @@ callback: nlmsvc_insert_block(block, jiffies + 30 * HZ); /* Call the client */ - nlmclnt_async_call(&block->b_call, NLMPROC_GRANTED_MSG, - nlmsvc_grant_callback); + nlm_get_host(block->b_call.a_host); + if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG, + nlmsvc_grant_callback) < 0) + nlm_release_host(block->b_call.a_host); up(&file->f_sema); } diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 7dc20ea7c..653c3415a 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -409,7 +409,6 @@ static int minix_rmdir(struct inode * dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; mark_inode_dirty(dir); - d_delete(dentry); retval = 0; end_rmdir: brelse(bh); @@ -444,7 +443,6 @@ static int minix_unlink(struct inode * dir, struct dentry *dentry) inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); - d_delete(dentry); /* This also frees the inode */ retval = 0; end_unlink: brelse(bh); diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 612bd6597..bcb40df90 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -337,7 +337,6 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry) dir->i_nlink--; mark_inode_dirty(inode); mark_inode_dirty(dir); - d_delete(dentry); res = 0; rmdir_done: @@ -432,7 +431,6 @@ int msdos_unlink( struct inode *dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); mark_inode_dirty(dir); - d_delete(dentry); /* This also frees the inode */ res = 0; unlink_done: return res; diff --git a/fs/namei.c b/fs/namei.c index 67f8c0a18..501000381 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -902,20 +902,18 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) if (nd->last.name[nd->last.len]) goto exit; - dir = dget(nd->dentry); + dir = nd->dentry; down(&dir->d_inode->i_sem); dentry = lookup_hash(&nd->last, nd->dentry); error = PTR_ERR(dentry); if (IS_ERR(dentry)) { up(&dir->d_inode->i_sem); - dput(dir); goto exit; } if (dentry->d_inode) { up(&dir->d_inode->i_sem); - dput(dir); error = -EEXIST; if (flag & O_EXCL) goto exit_dput; @@ -940,12 +938,12 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) goto exit; } else { error = vfs_create(dir->d_inode, dentry, mode); + up(&dir->d_inode->i_sem); /* Don't check for write permission, don't truncate */ acc_mode = 0; flag &= ~O_TRUNC; dput(nd->dentry); nd->dentry = dentry; - unlock_dir(dir); if (error) goto exit; } @@ -1219,6 +1217,8 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) if (!error) dentry->d_inode->i_flags |= S_DEAD; double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); + if (!error) + d_delete(dentry); dput(dentry); return error; @@ -1276,6 +1276,8 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) if (dir->i_op && dir->i_op->unlink) { DQUOT_INIT(dir); error = dir->i_op->unlink(dir, dentry); + if (!error) + d_delete(dentry); } } up(&dir->i_zombie); diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index f5a5856f3..55daea198 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -71,7 +71,7 @@ struct inode_operations ncp_dir_inode_operations = static int ncp_lookup_validate(struct dentry *, int); static int ncp_hash_dentry(struct dentry *, struct qstr *); static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *); -static void ncp_delete_dentry(struct dentry *); +static int ncp_delete_dentry(struct dentry *); struct dentry_operations ncp_dentry_operations = { @@ -119,32 +119,22 @@ ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) /* * This is the callback from dput() when d_count is going to 0. - * We use this to unhash dentries with bad inodes and close files. + * We use this to unhash dentries with bad inodes. + * Closing files can be safely postponed until iput() - it's done there anyway. */ -static void +static int ncp_delete_dentry(struct dentry * dentry) { struct inode *inode = dentry->d_inode; - if (inode) - { + if (inode) { if (is_bad_inode(inode)) - { - d_drop(dentry); - } - /* - * Lock the superblock, then recheck the dentry count. - * (Somebody might have used it again ...) - */ - if (dentry->d_count == 1 && NCP_FINFO(inode)->opened) { - PPRINTK("ncp_delete_dentry: closing file %s/%s\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - ncp_make_closed(inode); - } + return 1; } else { /* N.B. Unhash negative dentries? */ } + return 0; } static inline int @@ -1000,7 +990,6 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) case 0x00: DPRINTK("ncp: removed %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - d_delete(dentry); break; case 0x85: case 0x8A: diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 2d2ee4a02..f35ef3bdb 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -35,8 +35,6 @@ #define NFS_PARANOIA 1 /* #define NFS_DEBUG_VERBOSE 1 */ -static int nfs_safe_remove(struct dentry *); - static int nfs_readdir(struct file *, void *, filldir_t); static struct dentry *nfs_lookup(struct inode *, struct dentry *); static int nfs_create(struct inode *, struct dentry *, int); @@ -71,6 +69,70 @@ struct inode_operations nfs_dir_inode_operations = { }; typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int); +typedef struct { + struct file *file; + struct page *page; + unsigned long page_index; + unsigned page_offset; + u64 target; + struct nfs_entry *entry; + decode_dirent_t decode; + int plus; + int error; +} nfs_readdir_descriptor_t; + +/* Now we cache directories properly, by stuffing the dirent + * data directly in the page cache. + * + * Inode invalidation due to refresh etc. takes care of + * _everything_, no sloppy entry flushing logic, no extraneous + * copying, network direct to page cache, the way it was meant + * to be. + * + * NOTE: Dirent information verification is done always by the + * page-in of the RPC reply, nowhere else, this simplies + * things substantially. + */ +static +int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) +{ + struct file *file = desc->file; + struct inode *inode = file->f_dentry->d_inode; + void *buffer = (void *)kmap(page); + int plus = NFS_USE_READDIRPLUS(inode); + int error; + + dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index); + + again: + error = NFS_PROTO(inode)->readdir(file, desc->entry->cookie, buffer, + NFS_SERVER(inode)->dtsize, plus); + /* We requested READDIRPLUS, but the server doesn't grok it */ + if (desc->plus && error == -ENOTSUPP) { + NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; + plus = 0; + goto again; + } + if (error < 0) + goto error; + SetPageUptodate(page); + kunmap(page); + /* Ensure consistent page alignment of the data. + * Note: assumes we have exclusive access to this mapping either + * throught inode->i_sem or some other mechanism. + */ + if (page->index == 0) + invalidate_inode_pages(inode); + UnlockPage(page); + return 0; + error: + SetPageError(page); + kunmap(page); + UnlockPage(page); + invalidate_inode_pages(inode); + desc->error = error; + return -EIO; +} /* * Given a pointer to a buffer that has already been filled by a call @@ -81,309 +143,217 @@ typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int); * read. */ static inline -long find_dirent(struct page *page, loff_t offset, - struct nfs_entry *entry, - decode_dirent_t decode, int plus, int use_cookie) +int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) { - u8 *p = (u8 *)kmap(page), - *start = p; - unsigned long base = page_offset(page), - pg_offset = 0; - int loop_count = 0; + struct nfs_entry *entry = desc->entry; + char *start = (char *)kmap(page), + *p = start; + int loop_count = 0, + status = 0; - if (!p) - return -EIO; for(;;) { - p = (u8*)decode((__u32*)p, entry, plus); - if (IS_ERR(p)) + p = (char *)desc->decode((u32*)p, entry, desc->plus); + if (IS_ERR(p)) { + status = PTR_ERR(p); break; - pg_offset = p - start; - entry->prev = entry->offset; - entry->offset = base + pg_offset; - if ((use_cookie ? entry->cookie : entry->offset) > offset) + } + desc->page_offset = p - start; + dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie); + if (entry->prev_cookie == desc->target) break; if (loop_count++ > 200) { loop_count = 0; schedule(); } } - kunmap(page); - return (IS_ERR(p)) ? PTR_ERR(p) : (long)pg_offset; + dfprintk(VFS, "NFS: find_dirent() returns %d\n", status); + return status; } /* * Find the given page, and call find_dirent() in order to try to * return the next entry. - * - * Returns -EIO if the page is not available, or up to date. */ static inline -long find_dirent_page(struct inode *inode, loff_t offset, - struct nfs_entry *entry) +int find_dirent_page(nfs_readdir_descriptor_t *desc) { - decode_dirent_t decode = NFS_PROTO(inode)->decode_dirent; + struct inode *inode = desc->file->f_dentry->d_inode; struct page *page; - unsigned long index = entry->offset >> PAGE_CACHE_SHIFT; - long status = -EIO; - int plus = NFS_USE_READDIRPLUS(inode), - use_cookie = NFS_MONOTONE_COOKIES(inode); - - dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", entry->offset & PAGE_CACHE_MASK); - - if (entry->page) - page_cache_release(entry->page); - - page = find_get_page(&inode->i_data, index); + unsigned long index = desc->page_index; + int status; - if (page && Page_Uptodate(page)) - status = find_dirent(page, offset, entry, decode, plus, use_cookie); + dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index); - /* NB: on successful return we will be holding the page */ - if (status < 0) { - entry->page = NULL; - if (page) - page_cache_release(page); - } else - entry->page = page; + if (desc->page) { + page_cache_release(desc->page); + desc->page = NULL; + } - dfprintk(VFS, "NFS: find_dirent_page() returns %ld\n", status); + page = read_cache_page(&inode->i_data, index, + (filler_t *)nfs_readdir_filler, desc); + if (IS_ERR(page)) { + status = PTR_ERR(page); + goto out; + } + if (!Page_Uptodate(page)) + goto read_error; + + /* NOTE: Someone else may have changed the READDIRPLUS flag */ + desc->plus = NFS_USE_READDIRPLUS(inode); + status = find_dirent(desc, page); + if (status >= 0) + desc->page = page; + else + page_cache_release(page); + out: + dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status); return status; + read_error: + page_cache_release(page); + return -EIO; } - /* * Recurse through the page cache pages, and return a * filled nfs_entry structure of the next directory entry if possible. * - * The target for the search is position 'offset'. - * The latter may either be an offset into the page cache, or (better) - * a cookie depending on whether we're interested in strictly following - * the RFC wrt. not assuming monotonicity of cookies or not. - * - * For most systems, the latter is more reliable since it naturally - * copes with holes in the directory. + * The target for the search is 'desc->target'. */ static inline -long search_cached_dirent_pages(struct inode *inode, loff_t offset, - struct nfs_entry *entry) +int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) { - long res = 0; + int res = 0; int loop_count = 0; - dfprintk(VFS, "NFS: search_cached_dirent_pages() searching for cookie %Ld\n", (long long)offset); + dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target); for (;;) { - res = find_dirent_page(inode, offset, entry); - if (res == -EAGAIN) { - /* Align to beginning of next page */ - entry->offset &= PAGE_CACHE_MASK; - entry->offset += PAGE_CACHE_SIZE; - } + res = find_dirent_page(desc); if (res != -EAGAIN) break; + /* Align to beginning of next page */ + desc->page_offset = 0; + desc->page_index ++; if (loop_count++ > 200) { loop_count = 0; schedule(); } } - if (res < 0 && entry->page) { - page_cache_release(entry->page); - entry->page = NULL; - } - dfprintk(VFS, "NFS: search_cached_dirent_pages() returned %ld\n", res); - return res; -} - - -/* Now we cache directories properly, by stuffing the dirent - * data directly in the page cache. - * - * Inode invalidation due to refresh etc. takes care of - * _everything_, no sloppy entry flushing logic, no extraneous - * copying, network direct to page cache, the way it was meant - * to be. - * - * NOTE: Dirent information verification is done always by the - * page-in of the RPC reply, nowhere else, this simplies - * things substantially. - */ -static inline -long try_to_get_dirent_page(struct file *file, struct inode *inode, - struct nfs_entry *entry) -{ - struct dentry *dir = file->f_dentry; - struct page *page; - __u32 *p; - unsigned long index = entry->offset >> PAGE_CACHE_SHIFT; - long res = 0; - unsigned int dtsize = NFS_SERVER(inode)->dtsize; - int plus = NFS_USE_READDIRPLUS(inode); - - dfprintk(VFS, "NFS: try_to_get_dirent_page() reading directory page @ index %ld\n", index); - - page = grab_cache_page(&inode->i_data, index); - - if (!page) { - res = -ENOMEM; - goto out; - } - - if (Page_Uptodate(page)) { - dfprintk(VFS, "NFS: try_to_get_dirent_page(): page already up to date.\n"); - goto unlock_out; - } - - p = (__u32 *)kmap(page); - - if (dtsize > PAGE_CACHE_SIZE) - dtsize = PAGE_CACHE_SIZE; - res = NFS_PROTO(inode)->readdir(dir, entry->cookie, p, dtsize, plus); - - kunmap(page); - - if (res < 0) - goto error; - if (PageError(page)) - ClearPageError(page); - SetPageUptodate(page); - - unlock_out: - UnlockPage(page); - page_cache_release(page); - out: - dfprintk(VFS, "NFS: try_to_get_dirent_page() returns %ld\n", res); + dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res); return res; - error: - SetPageError(page); - goto unlock_out; } -/* Recover from a revalidation flush. The case here is that - * the inode for the directory got invalidated somehow, and - * all of our cached information is lost. In order to get - * a correct cookie for the current readdir request from the - * user, we must (re-)fetch all the older readdir page cache - * entries. - * - * Returns < 0 if some error occurs. +/* + * Once we've found the start of the dirent within a page: fill 'er up... */ -static inline -long refetch_to_readdir(struct file *file, struct inode *inode, - loff_t off, struct nfs_entry *entry) +static +int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, + filldir_t filldir) { - struct nfs_entry my_dirent, - *dirent = &my_dirent; - long res; - int plus = NFS_USE_READDIRPLUS(inode), - use_cookie = NFS_MONOTONE_COOKIES(inode), - loop_count = 0; - - dfprintk(VFS, "NFS: refetch_to_readdir() searching for cookie %Ld\n", (long long)off); - *dirent = *entry; - entry->page = NULL; - - for (res = 0;res >= 0;) { - if (loop_count++ > 200) { - loop_count = 0; - schedule(); - } + struct file *file = desc->file; + struct nfs_entry *entry = desc->entry; + char *start = (char *)kmap(desc->page), + *p = start + desc->page_offset; + unsigned long fileid; + int loop_count = 0, + res = 0; - /* Search for last cookie in page cache */ - res = search_cached_dirent_pages(inode, off, dirent); + dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target); - if (res >= 0) { - /* Cookie was found */ - if ((use_cookie?dirent->cookie:dirent->offset) > off) { - *entry = *dirent; - dirent->page = NULL; - break; + for(;;) { + /* Note: entry->prev_cookie contains the cookie for + * retrieving the current dirent on the server */ + fileid = nfs_fileid_to_ino_t(entry->ino); + res = filldir(dirent, entry->name, entry->len, + entry->prev_cookie, fileid); + if (res < 0) + break; + file->f_pos = desc->target = entry->cookie; + p = (char *)desc->decode((u32 *)p, entry, desc->plus); + if (IS_ERR(p)) { + if (PTR_ERR(p) == -EAGAIN) { + desc->page_offset = 0; + desc->page_index ++; } - continue; - } - - if (dirent->page) - page_cache_release(dirent->page); - dirent->page = NULL; - - if (res != -EIO) { - *entry = *dirent; break; } - - /* Read in a new page */ - res = try_to_get_dirent_page(file, inode, dirent); - if (res == -EBADCOOKIE) { - memset(dirent, 0, sizeof(*dirent)); - nfs_zap_caches(inode); - res = 0; - } - /* We requested READDIRPLUS, but the server doesn't grok it */ - if (plus && res == -ENOTSUPP) { - NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; - memset(dirent, 0, sizeof(*dirent)); - nfs_zap_caches(inode); - plus = 0; - res = 0; + desc->page_offset = p - start; + if (loop_count++ > 200) { + loop_count = 0; + schedule(); } } - if (dirent->page) - page_cache_release(dirent->page); + kunmap(desc->page); + page_cache_release(desc->page); + desc->page = NULL; - dfprintk(VFS, "NFS: refetch_to_readdir() returns %ld\n", res); + dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res); return res; } /* - * Once we've found the start of the dirent within a page: fill 'er up... + * If we cannot find a cookie in our cache, we suspect that this is + * because it points to a deleted file, so we ask the server to return + * whatever it thinks is the next entry. We then feed this to filldir. + * If all goes well, we should then be able to find our way round the + * cache on the next call to readdir_search_pagecache(); + * + * NOTE: we cannot add the anonymous page to the pagecache because + * the data it contains might not be page aligned. Besides, + * we should already have a complete representation of the + * directory in the page cache by the time we get here. */ -static -int nfs_do_filldir(struct file *file, struct inode *inode, - struct nfs_entry *entry, void *dirent, filldir_t filldir) +static inline +int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, + filldir_t filldir) { - decode_dirent_t decode = NFS_PROTO(inode)->decode_dirent; - struct page *page = entry->page; - __u8 *p, - *start; - unsigned long base = page_offset(page), - offset = entry->offset, - pg_offset, - fileid; - int plus = NFS_USE_READDIRPLUS(inode), - use_cookie = NFS_MONOTONE_COOKIES(inode), - loop_count = 0, - res = 0; - - dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ offset %ld\n", entry->offset); - pg_offset = offset & ~PAGE_CACHE_MASK; - start = (u8*)kmap(page); - p = start + pg_offset; + struct file *file = desc->file; + struct inode *inode = file->f_dentry->d_inode; + struct page *page = NULL; + u32 *p; + int status = -EIO; + + dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target); + if (desc->page) { + page_cache_release(desc->page); + desc->page = NULL; + } - for(;;) { - /* Note: entry->prev contains the offset of the start of the - * current dirent */ - fileid = nfs_fileid_to_ino_t(entry->ino); - if (use_cookie) - res = filldir(dirent, entry->name, entry->len, entry->prev_cookie, fileid); + page = page_cache_alloc(); + if (!page) { + status = -ENOMEM; + goto out; + } + p = (u32 *)kmap(page); + status = NFS_PROTO(inode)->readdir(file, desc->target, p, + NFS_SERVER(inode)->dtsize, 0); + if (status >= 0) { + p = desc->decode(p, desc->entry, 0); + if (IS_ERR(p)) + status = PTR_ERR(p); else - res = filldir(dirent, entry->name, entry->len, entry->prev, fileid); - if (res < 0) - break; - file->f_pos = (use_cookie) ? entry->cookie : entry->offset; - p = (u8*)decode((__u32*)p, entry, plus); - if (!p || IS_ERR(p)) - break; - pg_offset = p - start; - entry->prev = entry->offset; - entry->offset = base + pg_offset; - if (loop_count++ > 200) { - loop_count = 0; - schedule(); - } + desc->entry->prev_cookie = desc->target; } kunmap(page); - - dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ offset %ld; returning = %d\n", entry->offset, res); - return res; + if (status < 0) + goto out_release; + + desc->page_index = 0; + desc->page_offset = 0; + desc->page = page; + status = nfs_do_filldir(desc, dirent, filldir); + + /* Reset read descriptor so it searches the page cache from + * the start upon the next call to readdir_search_pagecache() */ + desc->page_index = 0; + desc->page_offset = 0; + memset(desc->entry, 0, sizeof(*desc->entry)); + out: + dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status); + return status; + out_release: + page_cache_release(page); + goto out; } /* The file offset position is now represented as a true offset into the @@ -393,10 +363,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; - struct page *page; - struct nfs_entry my_entry, - *entry = &my_entry; - loff_t offset; + nfs_readdir_descriptor_t my_desc, + *desc = &my_desc; + struct nfs_entry my_entry; long res; res = nfs_revalidate(dentry); @@ -409,36 +378,41 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) * read from the last dirent to revalidate f_pos * itself. */ - memset(entry, 0, sizeof(*entry)); + memset(desc, 0, sizeof(*desc)); + memset(&my_entry, 0, sizeof(my_entry)); - offset = filp->f_pos; + desc->file = filp; + desc->target = filp->f_pos; + desc->entry = &my_entry; + desc->decode = NFS_PROTO(inode)->decode_dirent; - while(!entry->eof) { - res = search_cached_dirent_pages(inode, offset, entry); - - if (res < 0) { - if (entry->eof) - break; - res = refetch_to_readdir(filp, inode, offset, entry); - if (res < 0) + while(!desc->entry->eof) { + res = readdir_search_pagecache(desc); + if (res == -EBADCOOKIE) { + /* This means either end of directory */ + if (desc->entry->cookie == desc->target) { + res = 0; break; + } + /* Or that the server has 'lost' a cookie */ + res = uncached_readdir(desc, dirent, filldir); + if (res >= 0) + continue; } + if (res < 0) + break; - page = entry->page; - if (!page) - printk(KERN_ERR "NFS: Missing page...\n"); - res = nfs_do_filldir(filp, inode, entry, dirent, filldir); - page_cache_release(page); - entry->page = NULL; + res = nfs_do_filldir(desc, dirent, filldir); if (res < 0) { res = 0; break; } - offset = filp->f_pos; } - if (entry->page) - page_cache_release(entry->page); - if (res < 0 && res != -EBADCOOKIE) + if (desc->page) + page_cache_release(desc->page); + if (desc->error < 0) + return desc->error; + if (res < 0) return res; return 0; } @@ -583,26 +557,18 @@ out_bad: /* * This is called from dput() when d_count is going to 0. - * We use it to clean up silly-renamed files. */ -static void nfs_dentry_delete(struct dentry *dentry) +static int nfs_dentry_delete(struct dentry *dentry) { dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_flags); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - int error; - - dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; - /* Unhash it first */ - d_drop(dentry); - error = nfs_safe_remove(dentry); - if (error) - printk("NFS: can't silly-delete %s/%s, error=%d\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, error); + /* Unhash it, so that ->d_iput() would be called */ + return 1; } + return 0; } @@ -627,36 +593,30 @@ static void nfs_dentry_release(struct dentry *dentry) nfs_fh_free(dentry->d_fsdata); } -struct dentry_operations nfs_dentry_operations = { - d_revalidate: nfs_lookup_revalidate, - d_delete: nfs_dentry_delete, - d_release: nfs_dentry_release, -}; - -#if 0 /* dead code */ -#ifdef NFS_PARANOIA /* - * Display all dentries holding the specified inode. + * Called when the dentry loses inode. + * We use it to clean up silly-renamed files. */ -static void show_dentry(struct list_head * dlist) +static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) { - struct list_head *tmp = dlist; - - while ((tmp = tmp->next) != dlist) { - struct dentry * dentry = list_entry(tmp, struct dentry, d_alias); - const char * unhashed = ""; - - if (d_unhashed(dentry)) - unhashed = "(unhashed)"; - - printk("show_dentry: %s/%s, d_count=%d%s\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, dentry->d_count, - unhashed); + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { + struct dentry *dir = dentry->d_parent; + struct inode *dir_i = dir->d_inode; + int error; + + nfs_zap_caches(dir_i); + NFS_CACHEINV(inode); + error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); } + iput(inode); } -#endif /* NFS_PARANOIA */ -#endif /* 0 */ + +struct dentry_operations nfs_dentry_operations = { + d_revalidate: nfs_lookup_revalidate, + d_delete: nfs_dentry_delete, + d_release: nfs_dentry_release, + d_iput: nfs_dentry_iput, +}; static struct dentry *nfs_lookup(struct inode *dir_i, struct dentry * dentry) { @@ -715,7 +675,6 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, nfs_renew_times(dentry); error = 0; } - NFS_CACHEINV(dentry->d_parent->d_inode); return error; } @@ -809,7 +768,6 @@ static int nfs_mkdir(struct inode *dir_i, struct dentry *dentry, int mode) d_drop(dentry); #endif nfs_zap_caches(dir_i); - dir_i->i_nlink++; error = NFS_PROTO(dir_i)->mkdir(dir, &dentry->d_name, &attr, &fhandle, &fattr); if (!error && fhandle.size != 0) @@ -830,13 +788,6 @@ static int nfs_rmdir(struct inode *dir_i, struct dentry *dentry) nfs_zap_caches(dir_i); error = NFS_PROTO(dir_i)->rmdir(dir, &dentry->d_name); - /* Update i_nlink and invalidate dentry. */ - if (!error) { - d_drop(dentry); - if (dir_i->i_nlink) - dir_i->i_nlink--; - } - return error; } @@ -919,7 +870,7 @@ out: * Remove a file after making sure there are no pending writes, * and after checking that the file has only one user. * - * We update inode->i_nlink and free the inode prior to the operation + * We invalidate the attribute cache and free the inode prior to the operation * to avoid possible races if the server reuses the inode. */ static int nfs_safe_remove(struct dentry *dentry) @@ -927,28 +878,12 @@ static int nfs_safe_remove(struct dentry *dentry) struct dentry *dir = dentry->d_parent; struct inode *dir_i = dir->d_inode; struct inode *inode = dentry->d_inode; - int error, rehash = 0; + int error = -EBUSY, rehash = 0; dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino); - /* N.B. not needed now that d_delete is done in advance? */ - error = -EBUSY; - if (!inode) { -#ifdef NFS_PARANOIA -printk("nfs_safe_remove: %s/%s already negative??\n", -dentry->d_parent->d_name.name, dentry->d_name.name); -#endif - } - - if (dentry->d_count > 1) { -#ifdef NFS_PARANOIA -printk("nfs_safe_remove: %s/%s busy, d_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); -#endif - goto out; - } /* * Unhash the dentry while we remove the file ... */ @@ -956,24 +891,26 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); d_drop(dentry); rehash = 1; } + if (dentry->d_count > 1) { +#ifdef NFS_PARANOIA + printk("nfs_safe_remove: %s/%s busy, d_count=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + dentry->d_count); +#endif + goto out; + } nfs_zap_caches(dir_i); + NFS_CACHEINV(inode); error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); if (error < 0) goto out; /* - * Update i_nlink and free the inode + * Free the inode */ - if (inode) { - if (inode->i_nlink) - inode->i_nlink --; - d_delete(dentry); - } - /* - * Rehash the negative dentry if the operation succeeded. - */ - if (rehash) - d_add(dentry, NULL); + d_delete(dentry); out: + if (rehash) + d_rehash(dentry); return error; } @@ -1067,14 +1004,8 @@ nfs_link(struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) */ d_drop(dentry); nfs_zap_caches(dir_i); + NFS_CACHEINV(inode); error = NFS_PROTO(dir_i)->link(old_dentry, dir, &dentry->d_name); - if (!error) { - /* - * Update the link count immediately, as some apps - * (e.g. pine) test this after making a link. - */ - inode->i_nlink++; - } return error; } @@ -1107,8 +1038,17 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; - struct dentry *dentry = NULL; - int error, rehash = 0; + struct dentry *dentry = NULL, *rehash = NULL; + int error = -EBUSY; + + /* + * To prevent any new references to the target during the rename, + * we unhash the dentry and free the inode in advance. + */ + if (!d_unhashed(new_dentry)) { + d_drop(new_dentry); + rehash = new_dentry; + } dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name, @@ -1125,7 +1065,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, */ if (!new_inode) goto go_ahead; - error = -EBUSY; if (S_ISDIR(new_inode->i_mode)) goto out; else if (new_dentry->d_count > 1) { @@ -1139,10 +1078,10 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, /* silly-rename the existing target ... */ err = nfs_sillyrename(new_dir, new_dentry); if (!err) { - new_dentry = dentry; + new_dentry = rehash = dentry; new_inode = NULL; - /* hash the replacement target */ - d_add(new_dentry, NULL); + /* instantiate the replacement target */ + d_instantiate(new_dentry, NULL); } /* dentry still busy? */ @@ -1166,14 +1105,6 @@ go_ahead: shrink_dcache_parent(old_dentry); } - /* - * To prevent any new references to the target during the rename, - * we unhash the dentry and free the inode in advance. - */ - if (!d_unhashed(new_dentry)) { - d_drop(new_dentry); - rehash = 1; - } if (new_inode) d_delete(new_dentry); @@ -1183,15 +1114,12 @@ go_ahead: &old_dentry->d_name, new_dentry->d_parent, &new_dentry->d_name); - NFS_CACHEINV(old_dir); - NFS_CACHEINV(new_dir); - /* Update the dcache if needed */ +out: if (rehash) - d_add(new_dentry, NULL); + d_rehash(rehash); if (!error && !S_ISDIR(old_inode->i_mode)) d_move(old_dentry, new_dentry); -out: /* new dentry created? */ if (dentry) dput(dentry); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 2d71aa7b5..44e3030c3 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -555,22 +555,18 @@ nfs_free_dentries(struct inode *inode) struct list_head *tmp, *head = &inode->i_dentry; int unhashed; -restart: + if (S_ISDIR(inode->i_mode)) { + struct dentry *dentry = d_find_alias(inode); + if (dentry) { + shrink_dcache_parent(dentry); + dput(dentry); + } + } + d_prune_aliases(inode); tmp = head; unhashed = 0; while ((tmp = tmp->next) != head) { struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); - dprintk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - dentry->d_count, !d_unhashed(dentry)); - if (!list_empty(&dentry->d_subdirs)) - shrink_dcache_parent(dentry); - if (!dentry->d_count) { - dget(dentry); - d_drop(dentry); - dput(dentry); - goto restart; - } if (d_unhashed(dentry)) unhashed++; } @@ -895,11 +891,20 @@ nfs_revalidate(struct dentry *dentry) */ int nfs_open(struct inode *inode, struct file *filp) { + struct rpc_auth *auth = NFS_CLIENT(inode)->cl_auth; + struct rpc_cred *cred = rpcauth_lookupcred(auth, 0); + + filp->private_data = cred; return 0; } int nfs_release(struct inode *inode, struct file *filp) { + struct rpc_auth *auth = NFS_CLIENT(inode)->cl_auth; + struct rpc_cred *cred = nfs_file_cred(filp); + + if (cred) + rpcauth_releasecred(auth, cred); return 0; } diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 1dd1553ba..a8b61c2e7 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -485,13 +485,6 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) break; } } - p++; /* EOF flag */ - - if (p > end) { - printk(KERN_NOTICE - "NFS: short packet in readdir reply!\n"); - return -errno_NFSERR_IO; - } return nr; } diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 67de662a6..921841ba3 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -138,14 +138,16 @@ nfs3_proc_readlink(struct dentry *dentry, void *buffer, unsigned int buflen) } static int -nfs3_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags, +nfs3_proc_read(struct file *file, struct nfs_fattr *fattr, int flags, loff_t offset, unsigned int count, void *buffer, int *eofp) { + struct dentry *dentry = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_readargs arg = { NFS_FH(dentry), offset, count, 1, {{buffer, count}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} }; struct nfs_readres res = { fattr, count, 0 }; - struct rpc_message msg = { NFS3PROC_READ, &arg, &res, NULL }; + struct rpc_message msg = { NFS3PROC_READ, &arg, &res, cred }; int status; dprintk("NFS call read %d @ %Ld\n", count, (long long)offset); @@ -157,16 +159,18 @@ nfs3_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags, } static int -nfs3_proc_write(struct dentry *dentry, struct nfs_fattr *fattr, int flags, +nfs3_proc_write(struct file *file, struct nfs_fattr *fattr, int flags, loff_t offset, unsigned int count, void *buffer, struct nfs_writeverf *verf) { + struct dentry *dentry = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_writeargs arg = { NFS_FH(dentry), offset, count, NFS_FILE_SYNC, 1, {{buffer, count}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} }; struct nfs_writeres res = { fattr, verf, 0 }; - struct rpc_message msg = { NFS3PROC_WRITE, &arg, &res, NULL }; + struct rpc_message msg = { NFS3PROC_WRITE, &arg, &res, cred }; int status, rpcflags = 0; dprintk("NFS call write %d @ %Ld\n", count, (long long)offset); @@ -369,13 +373,15 @@ nfs3_proc_rmdir(struct dentry *dir, struct qstr *name) * readdirplus. */ static int -nfs3_proc_readdir(struct dentry *dir, u64 cookie, void *entry, +nfs3_proc_readdir(struct file *file, u64 cookie, void *entry, unsigned int size, int plus) { + struct dentry *dir = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_fattr dir_attr; struct nfs3_readdirargs arg = { NFS_FH(dir), cookie, {0, 0}, 0, 0, 0 }; struct nfs3_readdirres res = { &dir_attr, 0, 0, 0, 0 }; - struct rpc_message msg = { NFS3PROC_READDIR, &arg, &res, NULL }; + struct rpc_message msg = { NFS3PROC_READDIR, &arg, &res, cred }; u32 *verf = NFS_COOKIEVERF(dir->d_inode); int status; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index d6d532217..0abf65af2 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -122,14 +122,16 @@ nfs_proc_readlink(struct dentry *dentry, void *buffer, unsigned int bufsiz) } static int -nfs_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags, +nfs_proc_read(struct file *file, struct nfs_fattr *fattr, int flags, loff_t offset, unsigned int count, void *buffer, int *eofp) { + struct dentry *dentry = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_readargs arg = { NFS_FH(dentry), offset, count, 1, {{ buffer, count }, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} }; struct nfs_readres res = { fattr, count, 0}; - struct rpc_message msg = { NFSPROC_READ, &arg, &res, NULL }; + struct rpc_message msg = { NFSPROC_READ, &arg, &res, cred }; int status; dprintk("NFS call read %d @ %Ld\n", count, (long long)offset); @@ -142,16 +144,18 @@ nfs_proc_read(struct dentry *dentry, struct nfs_fattr *fattr, int flags, } static int -nfs_proc_write(struct dentry *dentry, struct nfs_fattr *fattr, int how, +nfs_proc_write(struct file *file, struct nfs_fattr *fattr, int how, loff_t offset, unsigned int count, void *buffer, struct nfs_writeverf *verf) { + struct dentry *dentry = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_writeargs arg = {NFS_FH(dentry), offset, count, NFS_FILE_SYNC, 1, {{buffer, count}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}}; struct nfs_writeres res = {fattr, verf, count}; - struct rpc_message msg = { NFSPROC_WRITE, &arg, &res, NULL }; + struct rpc_message msg = { NFSPROC_WRITE, &arg, &res, cred }; int status, flags = 0; dprintk("NFS call write %d @ %Ld\n", count, (long long)offset); @@ -311,12 +315,14 @@ nfs_proc_rmdir(struct dentry *dir, struct qstr *name) * from nfs_readdir by calling the decode_entry function directly. */ static int -nfs_proc_readdir(struct dentry *dir, __u64 cookie, void *entry, +nfs_proc_readdir(struct file *file, __u64 cookie, void *entry, unsigned int size, int plus) { + struct dentry *dir = file->f_dentry; + struct rpc_cred *cred = nfs_file_cred(file); struct nfs_readdirargs arg; struct nfs_readdirres res; - struct rpc_message msg = { NFSPROC_READDIR, &arg, &res, NULL }; + struct rpc_message msg = { NFSPROC_READDIR, &arg, &res, cred }; struct nfs_server *server = NFS_DSERVER(dir); int status; diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 1c70ae58d..b15f50e61 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -83,8 +83,9 @@ static void nfs_readdata_release(struct rpc_task *task) * Read a page synchronously. */ static int -nfs_readpage_sync(struct dentry *dentry, struct page *page) +nfs_readpage_sync(struct file *file, struct page *page) { + struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct nfs_fattr fattr; loff_t offset = page_offset(page); @@ -112,7 +113,7 @@ nfs_readpage_sync(struct dentry *dentry, struct page *page) (long long)offset, rsize, buffer); lock_kernel(); - result = NFS_PROTO(inode)->read(dentry, &fattr, flags, offset, + result = NFS_PROTO(inode)->read(file, &fattr, flags, offset, rsize, buffer, &eof); unlock_kernel(); nfs_refresh_inode(inode, &fattr); @@ -195,9 +196,9 @@ nfs_mark_request_read(struct nfs_page *req) } static int -nfs_readpage_async(struct dentry *dentry, struct page *page) +nfs_readpage_async(struct file *file, struct page *page) { - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; struct nfs_page *req, *new = NULL; int result; @@ -227,7 +228,7 @@ nfs_readpage_async(struct dentry *dentry, struct page *page) } result = -ENOMEM; - new = nfs_create_request(dentry, page, 0, PAGE_CACHE_SIZE); + new = nfs_create_request(file, page, 0, PAGE_CACHE_SIZE); if (!new) break; } @@ -462,20 +463,16 @@ nfs_readpage_result(struct rpc_task *task) /* * Read a page over NFS. * We read the page synchronously in the following cases: - * - The file is a swap file. Swap-ins are always sync operations, - * so there's no need bothering to make async reads 100% fail-safe. * - The NFS rsize is smaller than PAGE_CACHE_SIZE. We could kludge our way * around this by creating several consecutive read requests, but * that's hardly worth it. * - The error flag is set for this page. This happens only when a * previous async read operation failed. - * - The server is congested. */ int nfs_readpage(struct file *file, struct page *page) { - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; int error; dprintk("NFS: nfs_readpage (%p %ld@%lu)\n", @@ -493,11 +490,11 @@ nfs_readpage(struct file *file, struct page *page) error = -1; if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE) - error = nfs_readpage_async(dentry, page); + error = nfs_readpage_async(file, page); if (error >= 0) goto out; - error = nfs_readpage_sync(dentry, page); + error = nfs_readpage_sync(file, page); if (error < 0 && IS_SWAPFILE(inode)) printk("Aiee.. nfs swap-in of page failed!\n"); out: diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 52af85acb..464776ac3 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -89,8 +89,7 @@ struct nfs_write_data { /* * Local function declarations */ -static struct nfs_page * nfs_update_request(struct file*, struct dentry *, - struct page *page, +static struct nfs_page * nfs_update_request(struct file*, struct page *page, unsigned int, unsigned int); static void nfs_strategy(struct inode *inode); static void nfs_writeback_done(struct rpc_task *); @@ -168,9 +167,10 @@ nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr) * Offset is the data offset within the page. */ static int -nfs_writepage_sync(struct dentry *dentry, struct page *page, +nfs_writepage_sync(struct file *file, struct page *page, unsigned int offset, unsigned int count) { + struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; loff_t base; unsigned int wsize = NFS_SERVER(inode)->wsize; @@ -193,7 +193,7 @@ nfs_writepage_sync(struct dentry *dentry, struct page *page, if (count < wsize && !IS_SWAPFILE(inode)) wsize = count; - result = NFS_PROTO(inode)->write(dentry, &fattr, flags, + result = NFS_PROTO(inode)->write(file, &fattr, flags, base, wsize, buffer, &verf); nfs_write_attributes(inode, &fattr); @@ -229,18 +229,18 @@ io_error: } static int -nfs_writepage_async(struct file *file, struct dentry *dentry, struct page *page, +nfs_writepage_async(struct file *file, struct page *page, unsigned int offset, unsigned int count) { struct nfs_page *req; int status; - req = nfs_update_request(file, dentry, page, offset, count); + req = nfs_update_request(file, page, offset, count); status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status < 0) goto out; nfs_release_request(req); - nfs_strategy(dentry->d_inode); + nfs_strategy(file->f_dentry->d_inode); out: return status; } @@ -251,8 +251,7 @@ nfs_writepage_async(struct file *file, struct dentry *dentry, struct page *page, int nfs_writepage(struct file *file, struct page *page) { - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT; unsigned offset = PAGE_CACHE_SIZE; int err; @@ -267,11 +266,11 @@ nfs_writepage(struct file *file, struct page *page) return -EIO; do_it: if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE) { - err = nfs_writepage_async(file, dentry, page, 0, offset); + err = nfs_writepage_async(file, page, 0, offset); if (err >= 0) goto out_ok; } - err = nfs_writepage_sync(dentry, page, 0, offset); + err = nfs_writepage_sync(file, page, 0, offset); if ( err == offset) goto out_ok; return err; @@ -476,10 +475,12 @@ nfs_mark_request_commit(struct nfs_page *req) * Page must be locked by the caller. This makes sure we never create * two different requests for the same page, and avoids possible deadlock * when we reach the hard limit on the number of dirty pages. + * It should be safe to sleep here. */ -struct nfs_page *nfs_create_request(struct dentry *dentry, struct page *page, +struct nfs_page *nfs_create_request(struct file *file, struct page *page, unsigned int offset, unsigned int count) { + struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct nfs_reqlist *cache = NFS_REQUESTLIST(inode); struct nfs_page *req = NULL; @@ -531,8 +532,10 @@ struct nfs_page *nfs_create_request(struct dentry *dentry, struct page *page, page_cache_get(page); req->wb_offset = offset; req->wb_bytes = count; - req->wb_dentry = dget(dentry); - req->wb_cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); + req->wb_file = file; + get_file(file); + req->wb_dentry = dentry; + req->wb_cred = nfs_file_cred(file); req->wb_count = 1; /* register request's existence */ @@ -573,12 +576,7 @@ nfs_release_request(struct nfs_page *req) if (NFS_WBACK_BUSY(req)) printk(KERN_ERR "NFS: Request released while still locked!\n"); - rpcauth_releasecred(NFS_CLIENT(inode)->cl_auth, req->wb_cred); - lock_kernel(); - if (req->wb_file) - fput(req->wb_file); - dput(req->wb_dentry); - unlock_kernel(); + fput(req->wb_file); page_cache_release(page); nfs_page_free(req); /* wake up anyone waiting to allocate a request */ @@ -789,10 +787,6 @@ int nfs_coalesce_requests(struct list_head *src, struct list_head *dst, unsigned if (prev) { if (req->wb_file != prev->wb_file) break; - if (req->wb_dentry != prev->wb_dentry) - break; - if (req->wb_cred != prev->wb_cred) - break; if (page_index(req->wb_page) != page_index(prev->wb_page)+1) break; @@ -818,10 +812,10 @@ int nfs_coalesce_requests(struct list_head *src, struct list_head *dst, unsigned * Note: Should always be called with the Page Lock held! */ static struct nfs_page * -nfs_update_request(struct file* file, struct dentry *dentry, struct page *page, +nfs_update_request(struct file* file, struct page *page, unsigned int offset, unsigned int bytes) { - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; struct nfs_page *req, *new = NULL; unsigned long rqend, end; @@ -856,21 +850,14 @@ nfs_update_request(struct file* file, struct dentry *dentry, struct page *page, } spin_unlock(&nfs_wreq_lock); - - /* Create the request. It's safe to sleep in this call because - * we only get here if the page is locked. - * + /* * If we're over the soft limit, flush out old requests */ - if (file && nfs_nr_requests >= MAX_REQUEST_SOFT) + if (inode->u.nfs_i.npages >= MAX_REQUEST_SOFT) nfs_wb_file(inode, file); - new = nfs_create_request(dentry, page, offset, bytes); + new = nfs_create_request(file, page, offset, bytes); if (!new) return ERR_PTR(-ENOMEM); - if (file) { - new->wb_file = file; - get_file(file); - } /* If the region is locked, adjust the timeout */ if (region_locked(inode, new)) new->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY; @@ -1006,7 +993,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign * page synchronously. */ if (NFS_SERVER(inode)->wsize < PAGE_SIZE) - return nfs_writepage_sync(dentry, page, offset, count); + return nfs_writepage_sync(file, page, offset, count); /* * Try to find an NFS request corresponding to this page @@ -1015,7 +1002,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign * it out now. */ do { - req = nfs_update_request(file, dentry, page, offset, count); + req = nfs_update_request(file, page, offset, count); status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status != -EBUSY) break; diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index 79ef12a7b..c47830ff3 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -49,16 +49,21 @@ nfsd_cache_init(void) struct svc_cacherep *rp; struct nfscache_head *rh; size_t i; + unsigned long order; if (cache_initialized) return; i = CACHESIZE * sizeof (struct svc_cacherep); - nfscache = kmalloc (i, GFP_KERNEL); + for (order = 0; (PAGE_SIZE << order) < i; order++) + ; + nfscache = (struct svc_cacherep *) + __get_free_pages(GFP_KERNEL, order); if (!nfscache) { printk (KERN_ERR "nfsd: cannot allocate %d bytes for reply cache\n", i); return; } + memset(nfscache, 0, i); i = HASHSIZE * sizeof (struct nfscache_head); hash_list = kmalloc (i, GFP_KERNEL); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index a5fcdcf7d..f681c9bfc 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -812,17 +812,11 @@ fh_put(struct svc_fh *fhp) { struct dentry * dentry = fhp->fh_dentry; if (fhp->fh_dverified) { + fhp->fh_dentry = NULL; fh_unlock(fhp); fhp->fh_dverified = 0; - if (!dentry->d_count) - goto out_bad; dput(dentry); nfsd_nr_put++; } return; - -out_bad: - printk(KERN_ERR "fh_put: %s/%s has d_count 0!\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - return; } diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index 92eed7559..3b875a445 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -1,4 +1,4 @@ -/* $Id: inode.c,v 1.10 2000/03/24 01:32:51 davem Exp $ +/* $Id: inode.c,v 1.11 2000/05/22 07:29:42 davem Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com) @@ -870,7 +870,6 @@ static int openpromfs_unlink (struct inode *dir, struct dentry *dentry) buffer [10 + len] = 0; prom_feval (buffer); } - d_delete(dentry); return 0; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 7f9ed2ba7..3b252ec63 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -135,6 +135,9 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) case IDE0_MAJOR: maj = "hd"; break; + case MD_MAJOR: + unit -= 'a'-'0'; + break; } if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) { unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16; @@ -465,7 +465,7 @@ fail_page: return NULL; } -static struct vfsmount *pipe_mnt = NULL; +static struct vfsmount *pipe_mnt; static struct inode * get_pipe_inode(void) { @@ -609,6 +609,7 @@ static struct super_block * pipefs_read_super(struct super_block *sb, void *data root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME; sb->s_blocksize = 1024; sb->s_blocksize_bits = 10; + sb->s_magic = PIPEFS_MAGIC; sb->s_op = &pipefs_ops; sb->s_root = d_alloc(NULL, &(const struct qstr) { "pipe:", 5, 0 }); if (!sb->s_root) { diff --git a/fs/proc/base.c b/fs/proc/base.c index 2e83c6a4e..d513987d8 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -22,6 +22,7 @@ #include <linux/stat.h> #include <linux/init.h> #include <linux/file.h> +#include <linux/string.h> /* * For hysterical raisins we keep the same inumbers as in the old procfs. @@ -651,6 +652,11 @@ static int pid_fd_revalidate(struct dentry * dentry, int flags) return 0; } +/* + * Exceptional case: normally we are not allowed to unhash a busy + * directory. In this case, however, we can do it - no aliasing problems + * due to the way we treat inodes. + */ static int pid_base_revalidate(struct dentry * dentry, int flags) { if (dentry->d_inode->u.proc_i.task->p_pptr) @@ -659,9 +665,9 @@ static int pid_base_revalidate(struct dentry * dentry, int flags) return 0; } -static void pid_delete_dentry(struct dentry * dentry) +static int pid_delete_dentry(struct dentry * dentry) { - d_drop(dentry); + return 1; } static struct dentry_operations pid_fd_dentry_operations = @@ -861,6 +867,28 @@ static struct inode_operations proc_base_inode_operations = { lookup: proc_base_lookup, }; +/* + * /proc/self: + */ +static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + char tmp[30]; + sprintf(tmp, "%d", current->pid); + return vfs_readlink(dentry,buffer,buflen,tmp); +} + +static int proc_self_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char tmp[30]; + sprintf(tmp, "%d", current->pid); + return vfs_follow_link(nd,tmp); +} + +static struct inode_operations proc_self_inode_operations = { + readlink: proc_self_readlink, + follow_link: proc_self_follow_link, +}; + struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry) { unsigned int pid, c; @@ -872,6 +900,23 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry) pid = 0; name = dentry->d_name.name; len = dentry->d_name.len; + if (len == 4 && !memcmp(name, "self", 4)) { + inode = get_empty_inode(); + if (!inode) + return ERR_PTR(-ENOMEM); + inode->i_sb = dir->i_sb; + inode->i_dev = dir->i_sb->s_dev; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_ino = fake_ino(0, PROC_PID_INO); + inode->u.proc_i.file = NULL; + inode->u.proc_i.task = NULL; + inode->i_mode = S_IFLNK|S_IRWXUGO; + inode->i_uid = inode->i_gid = 0; + inode->i_size = 64; + inode->i_op = &proc_self_inode_operations; + d_add(dentry, inode); + return NULL; + } while (len-- > 0) { c = *name - '0'; name++; @@ -916,7 +961,8 @@ void proc_pid_delete_inode(struct inode *inode) { if (inode->u.proc_i.file) fput(inode->u.proc_i.file); - free_task_struct(inode->u.proc_i.task); + if (inode->u.proc_i.task) + free_task_struct(inode->u.proc_i.task); } #define PROC_NUMBUF 10 @@ -932,7 +978,7 @@ static int get_pid_list(int index, unsigned int *pids) struct task_struct *p; int nr_pids = 0; - index -= FIRST_PROCESS_ENTRY; + index--; read_lock(&tasklist_lock); for_each_task(p) { int pid = p->pid; @@ -953,9 +999,17 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) { unsigned int pid_array[PROC_MAXPIDS]; char buf[PROC_NUMBUF]; - unsigned int nr = filp->f_pos; + unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY; unsigned int nr_pids, i; + if (!nr) { + ino_t ino = fake_ino(0,PROC_PID_INO); + if (filldir(dirent, "self", 4, filp->f_pos, ino) < 0) + return 0; + filp->f_pos++; + nr++; + } + nr_pids = get_pid_list(nr, pid_array); for (i = 0; i < nr_pids; i++) { @@ -963,11 +1017,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) ino_t ino = fake_ino(pid,PROC_PID_INO); unsigned long j = PROC_NUMBUF; - do { - j--; - buf[j] = '0' + (pid % 10); - pid /= 10; - } while (pid); + do buf[--j] = '0' + (pid % 10); while (pid/=10); if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino) < 0) break; diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 31e43fab9..1585657a2 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -140,9 +140,13 @@ proc_file_lseek(struct file * file, loff_t offset, int orig) { switch (orig) { case 0: + if (offset < 0) + return -EINVAL; file->f_pos = offset; return(file->f_pos); case 1: + if (offset + file->f_pos < 0) + return -EINVAL; file->f_pos += offset; return(file->f_pos); case 2: @@ -218,10 +222,9 @@ static struct inode_operations proc_link_inode_operations = { * smarter: we could keep a "volatile" flag in the * inode to indicate which ones to keep. */ -static void -proc_delete_dentry(struct dentry * dentry) +static int proc_delete_dentry(struct dentry * dentry) { - d_drop(dentry); + return 1; } static struct dentry_operations proc_dentry_operations = @@ -340,7 +343,7 @@ static struct inode_operations proc_dir_inode_operations = { lookup: proc_lookup, }; -int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) +static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) { int i; diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index 13ec76b02..01db469da 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -315,13 +315,12 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t size_t elf_buflen; int num_vma; - /* XXX we need to somehow lock vmlist between here - * and after elf_kcore_store_hdr() returns. - * For now assume that num_vma does not change (TA) - */ + read_lock(&vmlist_lock); proc_root_kcore->size = size = get_kcore_size(&num_vma, &elf_buflen); - if (buflen == 0 || *fpos >= size) + if (buflen == 0 || *fpos >= size) { + read_unlock(&vmlist_lock); return 0; + } /* trim buflen to not go beyond EOF */ if (buflen > size - *fpos) @@ -335,10 +334,13 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t if (buflen < tsz) tsz = buflen; elf_buf = kmalloc(elf_buflen, GFP_ATOMIC); - if (!elf_buf) + if (!elf_buf) { + read_unlock(&vmlist_lock); return -ENOMEM; + } memset(elf_buf, 0, elf_buflen); elf_kcore_store_hdr(elf_buf, num_vma, elf_buflen); + read_unlock(&vmlist_lock); if (copy_to_user(buffer, elf_buf + *fpos, tsz)) { kfree(elf_buf); return -EFAULT; @@ -352,7 +354,8 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t /* leave now if filled buffer already */ if (buflen == 0) return acc; - } + } else + read_unlock(&vmlist_lock); /* where page 0 not mapped, write zeros into buffer */ #if defined (__i386__) || defined (__mc68000__) diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 88d41c3c2..c64166f78 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -112,7 +112,6 @@ static void add_node(struct device_node *np, struct proc_dir_entry *de) al = proc_symlink(at, de, ent->name); if (al == 0) break; - proc_register(de, al); *lastp = al; lastp = &al->next; } diff --git a/fs/proc/root.c b/fs/proc/root.c index 8088d064d..075a5843d 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -22,38 +22,9 @@ struct proc_dir_entry *proc_net, *proc_bus, *proc_root_fs, *proc_root_driver; struct proc_dir_entry *proc_sys_root; #endif -/* - * /proc/self: - */ -static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen) -{ - char tmp[30]; - sprintf(tmp, "%d", current->pid); - return vfs_readlink(dentry,buffer,buflen,tmp); -} - -static int proc_self_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - char tmp[30]; - sprintf(tmp, "%d", current->pid); - return vfs_follow_link(nd,tmp); -} - -static struct inode_operations proc_self_inode_operations = { - readlink: proc_self_readlink, - follow_link: proc_self_follow_link -}; - -static struct proc_dir_entry proc_root_self = { - 0, 4, "self", - S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, 1, 0, 0, - 64, &proc_self_inode_operations, -}; - void __init proc_root_init(void) { proc_misc_init(); - proc_register(&proc_root, &proc_root_self); proc_net = proc_mkdir("net", 0); #ifdef CONFIG_SYSVIPC proc_mkdir("sysvipc", 0); diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c index 5be9b240f..3ef38467c 100644 --- a/fs/qnx4/namei.c +++ b/fs/qnx4/namei.c @@ -184,7 +184,6 @@ int qnx4_rmdir(struct inode *dir, struct dentry *dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; mark_inode_dirty(dir); - d_delete(dentry); retval = 0; end_rmdir: @@ -228,7 +227,6 @@ int qnx4_unlink(struct inode *dir, struct dentry *dentry) inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); - d_delete(dentry); retval = 0; end_unlink: diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 75e94efd9..4416e8be6 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -227,7 +227,6 @@ static int ramfs_unlink(struct inode * dir, struct dentry *dentry) inode->i_nlink--; dput(dentry); /* Undo the count from "create" - this does all the work */ - d_delete(dentry); retval = 0; } return retval; @@ -269,57 +268,6 @@ static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * return error; } -/* - * This really should be the same as the proc filldir, - * once proc does the "one dentry tree" thing.. - */ -static int ramfs_readdir(struct file * filp, void * dirent, filldir_t filldir) -{ - int i; - struct dentry *dentry = filp->f_dentry; - - i = filp->f_pos; - switch (i) { - case 0: - if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino) < 0) - break; - i++; - filp->f_pos++; - /* fallthrough */ - case 1: - if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino) < 0) - break; - i++; - filp->f_pos++; - /* fallthrough */ - default: { - struct list_head *list = dentry->d_subdirs.next; - - int j = i-2; - for (;;) { - if (list == &dentry->d_subdirs) - return 0; - if (!j) - break; - j--; - list = list->next; - } - - do { - struct dentry *de = list_entry(list, struct dentry, d_child); - - if (ramfs_positive(de)) { - if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino) < 0) - break; - } - filp->f_pos++; - list = list->next; - } while (list != &dentry->d_subdirs); - } - } - return 0; -} - static struct address_space_operations ramfs_aops = { readpage: ramfs_readpage, writepage: ramfs_writepage, @@ -335,7 +283,7 @@ static struct file_operations ramfs_file_operations = { static struct file_operations ramfs_dir_operations = { read: generic_read_dir, - readdir: ramfs_readdir, + readdir: dcache_readdir, }; static struct inode_operations ramfs_dir_inode_operations = { @@ -350,9 +298,15 @@ static struct inode_operations ramfs_dir_inode_operations = { rename: ramfs_rename, }; +static void ramfs_put_super(struct super_block *sb) +{ + d_genocide(sb->s_root); + shrink_dcache_parent(sb->s_root); +} static struct super_operations ramfs_ops = { - statfs: ramfs_statfs, + put_super: ramfs_put_super, + statfs: ramfs_statfs, }; static struct super_block *ramfs_read_super(struct super_block * sb, void * data, int silent) diff --git a/fs/readdir.c b/fs/readdir.c index e6256636e..059ab391d 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -32,6 +32,53 @@ out: return res; } +int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + int i; + struct dentry *dentry = filp->f_dentry; + + i = filp->f_pos; + switch (i) { + case 0: + if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino) < 0) + break; + i++; + filp->f_pos++; + /* fallthrough */ + case 1: + if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino) < 0) + break; + i++; + filp->f_pos++; + /* fallthrough */ + default: { + struct list_head *list = dentry->d_subdirs.next; + + int j = i-2; + for (;;) { + if (list == &dentry->d_subdirs) + return 0; + if (!j) + break; + j--; + list = list->next; + } + + do { + struct dentry *de = list_entry(list, struct dentry, d_child); + + if (!d_unhashed(de) && de->d_inode) { + if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino) < 0) + break; + } + filp->f_pos++; + list = list->next; + } while (list != &dentry->d_subdirs); + } + } + return 0; +} + /* * Traditional linux readdir() handling.. * diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index a29e55c7a..b5715b220 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -168,7 +168,7 @@ file->f_dentry->d_name.name); static int smb_lookup_validate(struct dentry *, int); static int smb_hash_dentry(struct dentry *, struct qstr *); static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *); -static void smb_delete_dentry(struct dentry *); +static int smb_delete_dentry(struct dentry *); static struct dentry_operations smbfs_dentry_operations = { @@ -259,9 +259,9 @@ out: /* * This is the callback from dput() when d_count is going to 0. - * We use this to unhash dentries with bad inodes and close files. + * We use this to unhash dentries with bad inodes. */ -static void +static int smb_delete_dentry(struct dentry * dentry) { if (dentry->d_inode) @@ -272,13 +272,13 @@ smb_delete_dentry(struct dentry * dentry) printk("smb_delete_dentry: bad inode, unhashing %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); #endif - d_drop(dentry); + return 1; } - smb_close_dentry(dentry); } else { /* N.B. Unhash negative dentries? */ } + return 0; } /* @@ -466,10 +466,7 @@ smb_unlink(struct inode *dir, struct dentry *dentry) smb_invalid_dir_cache(dir); error = smb_proc_unlink(dentry); if (!error) - { smb_renew_times(dentry); - d_delete(dentry); - } return error; } diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 61f50bdff..b47e236b0 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -26,12 +26,6 @@ /* #define SMBFS_DEBUG_VERBOSE 1 */ /* #define pr_debug printk */ -static inline int -min(int a, int b) -{ - return a < b ? a : b; -} - static int smb_fsync(struct file *file, struct dentry * dentry) { @@ -340,28 +334,15 @@ out: static int smb_file_open(struct inode *inode, struct file * file) { -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_file_open: opening %s/%s, d_count=%d\n", -file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name, -file->f_dentry->d_count); -#endif + inode->u.smbfs_i.openers++; return 0; } static int smb_file_release(struct inode *inode, struct file * file) { - struct dentry * dentry = file->f_dentry; - -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_file_release: closing %s/%s, d_count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); -#endif - - if (dentry->d_count == 1) - { + if (!--inode->u.smbfs_i.openers) smb_close(inode); - } return 0; } diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 06cd5dda9..669deb0d7 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -818,11 +818,6 @@ smb_open(struct dentry *dentry, int wish) goto out; } - /* - * Note: If the caller holds an active dentry and the file is - * currently open, we can be sure that the file isn't about - * to be closed. (See smb_close_dentry() below.) - */ if (!smb_is_open(inode)) { struct smb_sb_info *server = SMB_SERVER(inode); @@ -944,46 +939,6 @@ smb_close(struct inode *ino) } /* - * This routine is called from dput() when d_count is going to 0. - * We use this to close the file so that cached dentries don't - * keep too many files open. - * - * There are some tricky race conditions here: the dentry may go - * back into use while we're closing the file, and we don't want - * the new user to be confused as to the open status. - */ -void -smb_close_dentry(struct dentry * dentry) -{ - struct inode *ino = dentry->d_inode; - - if (ino) - { - if (smb_is_open(ino)) - { - struct smb_sb_info *server = SMB_SERVER(ino); - smb_lock_server(server); - /* - * Check whether the dentry is back in use. - */ - if (dentry->d_count <= 1) - { -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_close_dentry: closing %s/%s, count=%d\n", - DENTRY_PATH(dentry), dentry->d_count); -#endif - smb_proc_close_inode(server, ino); - } - smb_unlock_server(server); - } -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_close_dentry: closed %s/%s, count=%d\n", - DENTRY_PATH(dentry), dentry->d_count); -#endif - } -} - -/* * This is used to close a file following a failed instantiate. * Since we don't have an inode, we can't use any of the above. */ diff --git a/fs/super.c b/fs/super.c index b32b1fc6c..f1d873331 100644 --- a/fs/super.c +++ b/fs/super.c @@ -76,7 +76,7 @@ LIST_HEAD(super_blocks); * Once the reference is obtained we can drop the spinlock. */ -static struct file_system_type *file_systems = NULL; +static struct file_system_type *file_systems; static rwlock_t file_systems_lock = RW_LOCK_UNLOCKED; /* WARNING: This can be used only if we _already_ own a reference */ @@ -315,6 +315,7 @@ static struct vfsmount *add_vfsmnt(struct super_block *sb, strcpy(name, dir_name); mnt->mnt_dirname = name; } + mnt->mnt_owner = current->uid; if (parent) list_add(&mnt->mnt_child, &parent->mnt_mounts); @@ -1020,10 +1021,6 @@ asmlinkage long sys_umount(char * name, int flags) struct nameidata nd; char *kname; int retval; - struct super_block *sb; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; lock_kernel(); kname = getname(name); @@ -1036,10 +1033,14 @@ asmlinkage long sys_umount(char * name, int flags) putname(kname); if (retval) goto out; - sb = nd.dentry->d_inode->i_sb; retval = -EINVAL; if (nd.dentry!=nd.mnt->mnt_root) goto dput_and_out; + + retval = -EPERM; + if (!capable(CAP_SYS_ADMIN) && current->uid!=nd.mnt->mnt_owner) + goto dput_and_out; + dput(nd.dentry); /* puts nd.mnt */ down(&mount_sem); @@ -1062,6 +1063,21 @@ asmlinkage long sys_oldumount(char * name) return sys_umount(name,0); } +static int mount_is_safe(struct nameidata *nd) +{ + if (capable(CAP_SYS_ADMIN)) + return 0; + if (S_ISLNK(nd->dentry->d_inode->i_mode)) + return -EPERM; + if (nd->dentry->d_inode->i_mode & S_ISVTX) { + if (current->uid != nd->dentry->d_inode->i_uid) + return -EPERM; + } + if (permission(nd->dentry->d_inode, MAY_WRITE)) + return -EPERM; + return 0; +} + /* * do loopback mount. */ @@ -1071,18 +1087,22 @@ static int do_loopback(char *old_name, char *new_name) int err = 0; if (!old_name || !*old_name) return -EINVAL; - if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &old_nd)) + if (path_init(old_name, LOOKUP_POSITIVE, &old_nd)) err = path_walk(old_name, &old_nd); if (err) goto out; - if (path_init(new_name, LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &new_nd)) + if (path_init(new_name, LOOKUP_POSITIVE, &new_nd)) err = path_walk(new_name, &new_nd); if (err) goto out1; - err = -EPERM; - if (!capable(CAP_SYS_ADMIN) && - current->uid != new_nd.dentry->d_inode->i_uid) + err = mount_is_safe(&new_nd); + if (err) + goto out2; + err = -EINVAL; + if (S_ISDIR(new_nd.dentry->d_inode->i_mode) != + S_ISDIR(old_nd.dentry->d_inode->i_mode)) goto out2; + down(&mount_sem); err = -ENOENT; if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry)) @@ -1143,31 +1163,29 @@ static int do_remount(const char *dir,int flags,char *data) return retval; } -static int copy_mount_options (const void * data, unsigned long *where) +static int copy_mount_options (const void *data, unsigned long *where) { int i; unsigned long page; - struct vm_area_struct * vma; *where = 0; if (!data) return 0; - vma = find_vma(current->mm, (unsigned long) data); - if (!vma || (unsigned long) data < vma->vm_start) - return -EFAULT; - if (!(vma->vm_flags & VM_READ)) - return -EFAULT; - i = vma->vm_end - (unsigned long) data; - if (PAGE_SIZE <= (unsigned long) i) - i = PAGE_SIZE-1; - if (!(page = __get_free_page(GFP_KERNEL))) { + if (!(page = __get_free_page(GFP_KERNEL))) return -ENOMEM; - } - if (copy_from_user((void *) page,data,i)) { + + /* We only care that *some* data at the address the user + * gave us is valid. Just in case, we'll zero + * the remainder of the page. + */ + i = copy_from_user((void *)page, data, PAGE_SIZE); + if (i == PAGE_SIZE) { free_page(page); return -EFAULT; } + if (i) + memset((char *)page + PAGE_SIZE - i, 0, i); *where = page; return 0; } @@ -1186,7 +1204,7 @@ static int copy_mount_options (const void * data, unsigned long *where) * aren't used, as the syscall assumes we are talking to an older * version that didn't understand them. */ -long do_sys_mount(char * dev_name, char * dir_name, char *type_page, +long do_mount(char * dev_name, char * dir_name, char *type_page, unsigned long new_flags, void *data_page) { struct file_system_type * fstype; @@ -1279,26 +1297,24 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, unsigned long new_flags, void * data) { int retval; - unsigned long data_page = 0; - unsigned long type_page = 0; - unsigned long dev_page = 0; + unsigned long data_page; + unsigned long type_page; + unsigned long dev_page; char *dir_page; - lock_kernel(); retval = copy_mount_options (type, &type_page); if (retval < 0) - goto out; + return retval; /* copy_mount_options allows a NULL user pointer, * and just returns zero in that case. But if we * allow the type to be NULL we will crash. * Previously we did not check this case. */ - if (type_page == 0) { - retval = -EINVAL; - goto out; - } + if (type_page == 0) + return -EINVAL; + lock_kernel(); dir_page = getname(dir_name); retval = PTR_ERR(dir_page); if (IS_ERR(dir_page)) @@ -1309,7 +1325,7 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, goto out2; retval = copy_mount_options (data, &data_page); if (retval >= 0) { - retval = do_sys_mount((char*)dev_page,dir_page,(char*)type_page, + retval = do_mount((char*)dev_page,dir_page,(char*)type_page, new_flags, (void*)data_page); free_page(data_page); } @@ -1318,7 +1334,6 @@ out2: putname(dir_page); out1: free_page(type_page); -out: unlock_kernel(); return retval; } @@ -1493,10 +1508,6 @@ static void chroot_fs_refs(struct dentry *old_root, { struct task_struct *p; - /* We can't afford dput() blocking under the tasklist_lock */ - mntget(old_rootmnt); - dget(old_root); - read_lock(&tasklist_lock); for_each_task(p) { if (!p->fs) continue; @@ -1506,9 +1517,6 @@ static void chroot_fs_refs(struct dentry *old_root, set_fs_pwd(p->fs, new_rootmnt, new_root); } read_unlock(&tasklist_lock); - - dput(old_root); - mntput(old_rootmnt); } /* @@ -1525,8 +1533,8 @@ static void chroot_fs_refs(struct dentry *old_root, asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) { - struct dentry *root = current->fs->root; - struct vfsmount *root_mnt = current->fs->rootmnt; + struct dentry *root; + struct vfsmount *root_mnt; struct vfsmount *tmp; struct nameidata new_nd, old_nd; char *name; @@ -1559,6 +1567,8 @@ asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) if (error) goto out1; + root_mnt = mntget(current->fs->rootmnt); + root = dget(current->fs->root); down(&mount_sem); error = -ENOENT; if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry)) @@ -1597,6 +1607,8 @@ asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) error = 0; out2: up(&mount_sem); + dput(root); + mntput(root_mnt); path_release(&old_nd); out1: path_release(&new_nd); diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 0abffaac6..ef1e04381 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -398,7 +398,6 @@ static int sysv_rmdir(struct inode * dir, struct dentry * dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); mark_inode_dirty(dir); - d_delete(dentry); retval = 0; end_rmdir: brelse(bh); @@ -429,7 +428,6 @@ static int sysv_unlink(struct inode * dir, struct dentry * dentry) inode->i_nlink--; inode->i_ctime = dir->i_ctime; mark_inode_dirty(inode); - d_delete(dentry); retval = 0; end_unlink: brelse(bh); diff --git a/fs/udf/namei.c b/fs/udf/namei.c index dcd980030..d56ff9a0c 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -346,7 +346,7 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, sb = dir->i_sb; - if (dentry->d_name.len) + if (dentry) { if ( !(udf_char_to_ustr(&unifilename, dentry->d_name.name, dentry->d_name.len)) ) { @@ -447,20 +447,17 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, } } - if (!lfi) + if (!lfi || !dentry) continue; - if ((flen = udf_get_filename(nameptr, fname, lfi))) - { - if (udf_match(flen, fname, &(dentry->d_name))) - { - if (fibh->sbh != fibh->ebh) - udf_release_data(fibh->ebh); - udf_release_data(fibh->sbh); - udf_release_data(bh); - *err = -EEXIST; - return NULL; - } + if ((flen = udf_get_filename(nameptr, fname, lfi)) && + udf_match(flen, fname, &(dentry->d_name))) { + if (fibh->sbh != fibh->ebh) + udf_release_data(fibh->ebh); + udf_release_data(fibh->sbh); + udf_release_data(bh); + *err = -EEXIST; + return NULL; } } } @@ -691,7 +688,6 @@ static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) struct udf_fileident_bh fibh; int err; struct FileIdentDesc cfi, *fi; - struct dentry parent; err = -EMLINK; if (dir->i_nlink >= (256<<sizeof(dir->i_nlink))-1) @@ -704,10 +700,8 @@ static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode->i_op = &udf_dir_inode_operations; inode->i_fop = &udf_dir_operations; - parent.d_name.len = 0; - parent.d_name.name = NULL; inode->i_size = 0; - if (!(fi = udf_add_entry(inode, &parent, &fibh, &cfi, &err))) + if (!(fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err))) { inode->i_nlink--; mark_inode_dirty(inode); @@ -852,7 +846,6 @@ static int udf_rmdir(struct inode * dir, struct dentry * dentry) inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; UDF_I_UCTIME(inode) = UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME; mark_inode_dirty(dir); - d_delete(dentry); end_rmdir: if (fibh.sbh != fibh.ebh) @@ -902,7 +895,6 @@ static int udf_unlink(struct inode * dir, struct dentry * dentry) mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; - d_delete(dentry); /* This also frees the inode */ end_unlink: if (fibh.sbh != fibh.ebh) diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index c60fcbcdb..2bd998cf3 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -673,7 +673,6 @@ static int ufs_rmdir (struct inode * dir, struct dentry *dentry) dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); - d_delete(dentry); end_rmdir: brelse (bh); @@ -730,7 +729,6 @@ static int ufs_unlink(struct inode * dir, struct dentry *dentry) mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; - d_delete(dentry); /* This also frees the inode */ end_unlink: brelse (bh); diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile index f1b7b3ed4..c14c1b615 100644 --- a/fs/umsdos/Makefile +++ b/fs/umsdos/Makefile @@ -8,7 +8,7 @@ # Note 2: the CFLAGS definitions are now in the main makefile. O_TARGET := umsdos.o -O_OBJS := dir.o inode.o ioctl.o mangle.o namei.o rdir.o emd.o check.o +O_OBJS := dir.o inode.o ioctl.o mangle.o namei.o rdir.o emd.o M_OBJS := $(O_TARGET) diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c deleted file mode 100644 index 58755dd2c..000000000 --- a/fs/umsdos/check.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * linux/fs/umsdos/check.c - * - * Sanity-checking code - */ - -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/types.h> -#include <linux/ptrace.h> -#include <linux/mman.h> -#include <linux/mm.h> -#include <linux/umsdos_fs.h> - -#include <asm/system.h> - -#ifdef CHECK_PAGE_TABLES -static int check_one_table (struct pde *page_dir) -{ - if (pgd_none (*page_dir)) - return 0; - if (pgd_bad (*page_dir)) - return 1; - return 0; -} - -/* - * This function checks all page tables of "current" - */ -void check_page_tables (void) -{ - struct pgd *pg_dir; - static int err = 0; - - int stack_level = (long) (&pg_dir) - current->kernel_stack_page; - - if (stack_level < 1500) - printk ("** %d ** ", stack_level); - pg_dir = PAGE_DIR_OFFSET (current, 0); - if (err == 0) { - int i; - - for (i = 0; i < PTRS_PER_PAGE; i++, page_dir++) { - int notok = check_one_table (page_dir); - - if (notok) { - err++; - printk ("|%d:%08lx| ", i, page_dir->pgd); - } - } - if (err) - printk ("\nError MM %d\n", err); - } -} -#endif - - -#if UMS_DEBUG -/* - * check for wait queue in 2.3.x - */ -inline void uq_log (char *txt, struct inode *inode) -{ - printk (KERN_ERR "%s: (%lu) magic=%lu creator=%lu lock=%u\n", txt, inode->i_ino, inode->u.umsdos_i.dir_info.p.__magic, inode->u.umsdos_i.dir_info.p.__creator, inode->u.umsdos_i.dir_info.p.lock.lock); -} - -/* - * check a superblock - */ - -void check_sb (struct super_block *sb, const char c) -{ - if (sb) { - printk (" (has %c_sb=%d, %d)", - c, MAJOR (sb->s_dev), MINOR (sb->s_dev)); - } else { - printk (" (%c_sb is NULL)", c); - } -} - -/* - * check an inode - */ - -void check_inode (struct inode *inode) -{ - if (inode) { - printk (KERN_DEBUG "* inode is %lu (i_count=%d)", - inode->i_ino, inode->i_count); - check_sb (inode->i_sb, 'i'); - - if (inode->i_dentry.next) { /* FIXME: does this work ? */ - printk (" (has i_dentry)"); - } else { - printk (" (NO i_dentry)"); - } - - printk (" (i_patched=%d)", inode->u.umsdos_i.i_patched); - - } else { - printk (KERN_DEBUG "* inode is NULL\n"); - } -} - -/* - * checks all inode->i_dentry - * - */ -void checkd_inode (struct inode *inode) -{ - struct dentry *ret; - struct list_head *cur; - int count = 0; - if (!inode) { - printk (KERN_ERR "checkd_inode: inode is NULL!\n"); - return; - } - - printk (KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino); - cur = inode->i_dentry.next; - while (count++ < 10) { - PRINTK (("1...")); - if (!cur) { - printk (KERN_ERR "checkd_inode: *** NULL reached. exit.\n"); - return; - } - PRINTK (("2...")); - ret = list_entry (cur, struct dentry, d_alias); - PRINTK (("3...")); - if (cur == cur->next) { - printk (KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n"); - return; - } - PRINTK (("4...")); - if (!ret) { - printk (KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n"); - return; - } - PRINTK (("5... (ret=%p)...", ret)); - PRINTK (("5.1.. (ret->d_dname=%p)...", &(ret->d_name))); - PRINTK (("5.1.1. (ret->d_dname.len=%d)...", (int) ret->d_name.len)); - PRINTK (("5.1.2. (ret->d_dname.name=%c)...", ret->d_name.name)); - printk (KERN_DEBUG "checkd_inode: i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name); - PRINTK (("6...")); - cur = cur->next; - PRINTK (("7...")); -#if 1 - printk (KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n"); - return; -#endif - } - printk (KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n"); - return; -} - -/* - * internal part of check_dentry. does the real job. - * - */ - -void check_dent_int (struct dentry *dentry, int parent) -{ - if (parent) { - printk (KERN_DEBUG "* parent(%d) dentry: %.*s\n", - parent, (int) dentry->d_name.len, dentry->d_name.name); - } else { - printk (KERN_DEBUG "* checking dentry: %.*s\n", - (int) dentry->d_name.len, dentry->d_name.name); - } - check_inode (dentry->d_inode); - printk (KERN_DEBUG "* d_count=%d", dentry->d_count); - check_sb (dentry->d_sb, 'd'); - if (dentry->d_op == NULL) { - printk (" (d_op is NULL)\n"); - } else { - printk (" (d_op is UNKNOWN: %p)\n", dentry->d_op); - } -} - -/* - * checks dentry with full traceback to root and prints info. Limited to 10 recursive depths to avoid infinite loops. - * - */ - -void check_dentry_path (struct dentry *dentry, const char *desc) -{ - int count=0; - printk (KERN_DEBUG "*** check_dentry_path: %.60s\n", desc); - - if (!dentry) { - printk (KERN_DEBUG "*** checking dentry... it is NULL !\n"); - return; - } - if (IS_ERR(dentry)) { - printk (KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n", - PTR_ERR(dentry)); - return; - } - - while (dentry && count < 10) { - check_dent_int (dentry, count++); - if (IS_ROOT(dentry)) { - printk (KERN_DEBUG "*** end checking dentry (root reached ok)\n"); - break; - } - dentry = dentry->d_parent; - } - - if (count >= 10) { /* if infinite loop detected */ - printk (KERN_ERR - "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n"); - } - - if (!dentry) { - printk (KERN_ERR - "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n"); - } -} -#else -inline void uq_log (char *txt, struct inode *inode) {}; -void check_sb (struct super_block *sb, const char c) {}; -void check_inode (struct inode *inode) {}; -void checkd_inode (struct inode *inode) {}; -void check_dentry_path (struct dentry *dentry, const char *desc) {}; -#endif /* UMS_DEBUG */ - diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 29eebb3f2..9d37f24bc 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -36,12 +36,14 @@ static int umsdos_dentry_validate(struct dentry *dentry, int flags) } /* for now, drop everything to force lookups ... */ -static void umsdos_dentry_dput(struct dentry *dentry) +/* ITYM s/everything/& positive/... */ +static int umsdos_dentry_dput(struct dentry *dentry) { struct inode *inode = dentry->d_inode; if (inode) { - d_drop(dentry); + return 1; } + return 0; } struct dentry_operations umsdos_dentry_operations = diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 2e172e80b..af69877d9 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -367,14 +367,6 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data, sb->s_root = new_root; printk(KERN_INFO "UMSDOS: changed to alternate root\n"); } - - /* if d_count is not 1, mount will fail with -EBUSY! */ - if (sb->s_root->d_count > 1) { - shrink_dcache_sb(sb); - if (sb->s_root->d_count > 1) { - printk(KERN_ERR "UMSDOS: root count %d > 1 !", sb->s_root->d_count); - } - } return sb; out_fail: diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index 239043318..a8adf6ed8 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -331,6 +331,8 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); ret = -EISDIR; if (!S_ISDIR(temp->d_inode->i_mode)) ret = msdos_unlink (dir, temp); + if (!ret) + d_delete(temp); } dput (temp); goto out; @@ -355,6 +357,8 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name); ret = -ENOTDIR; if (S_ISDIR(temp->d_inode->i_mode)) ret = msdos_rmdir (dir, temp); + if (!ret) + d_delete(temp); } dput (temp); goto out; diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 75715116f..d3fe5eb61 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -524,6 +524,7 @@ out_error: out_unlink: printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n"); UMSDOS_unlink (dir, dentry); + d_drop(dentry); goto out; } @@ -898,9 +899,11 @@ if (err) printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%d\n", demd->d_parent->d_name.name, demd->d_name.name, err); #endif - dput(demd); - if (!err) + if (!err) { + d_delete(demd); ret = 0; + } + dput(demd); } } else if (empty == 2) ret = 0; @@ -921,6 +924,7 @@ demd->d_parent->d_name.name, demd->d_name.name, err); if (ret && ret != -ENOENT) goto out_dput; + d_delete(temp); /* OK so far ... remove the name from the EMD */ ret = umsdos_delentry (dentry->d_parent, &info, 1); #ifdef UMSDOS_PARANOIA @@ -1009,6 +1013,8 @@ Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname)); } ret = msdos_unlink(dir, temp); + if (!ret) + d_delete(temp); #ifdef UMSDOS_PARANOIA if (ret) printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n", @@ -1018,8 +1024,6 @@ temp->d_parent->d_name.name, temp->d_name.name, ret); /* dput() temp if we didn't do it above */ out_dput: dput(temp); - if (!ret) - d_delete (dentry); out_unlock: umsdos_unlockcreate (dir); @@ -1065,7 +1069,8 @@ link->d_parent->d_name.name, link->d_name.name, ret)); printk(KERN_WARNING "umsdos_unlink: link removal failed, ret=%d\n", ret); - } + } else + d_delete(link); } else { struct iattr newattrs; inode->i_nlink--; @@ -1100,11 +1105,13 @@ int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry, * If the target already exists, delete it first. */ if (new_dentry->d_inode) { - new_dentry->d_count++; + dget(new_dentry); if (S_ISDIR(old_dentry->d_inode->i_mode)) ret = UMSDOS_rmdir (new_dir, new_dentry); else ret = UMSDOS_unlink (new_dir, new_dentry); + if (!ret) + d_drop(new_dentry); dput(new_dentry); if (ret) return ret; diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index 8c71dd727..a477ade2c 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -174,6 +174,8 @@ static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry) ret = 0; if (demd->d_inode) ret = msdos_unlink (dentry->d_inode, demd); + if (!ret) + d_delete(demd); dput(demd); } } diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index ceb67870d..0439d63fc 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -961,24 +961,6 @@ static int vfat_find(struct inode *dir,struct qstr* qname, return res ? res : -ENOENT; } -/* Find a hashed dentry for inode; NULL if there are none */ -static struct dentry *find_alias(struct inode *inode) -{ - struct list_head *head, *next, *tmp; - struct dentry *alias; - - head = &inode->i_dentry; - next = inode->i_dentry.next; - while (next != head) { - tmp = next; - next = tmp->next; - alias = list_entry(tmp, struct dentry, d_alias); - if (!d_unhashed(alias)) - return dget(alias); - } - return NULL; -} - struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry) { int res; @@ -1005,7 +987,7 @@ struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry) fat_brelse(dir->i_sb, bh); if (res) return ERR_PTR(res); - alias = find_alias(inode); + alias = d_find_alias(inode); if (alias) { if (d_invalidate(alias)==0) dput(alias); @@ -1116,7 +1098,6 @@ int vfat_unlink(struct inode *dir, struct dentry* dentry) mark_inode_dirty(dentry->d_inode); /* releases bh */ vfat_remove_entry(dir,&sinfo,bh,de); - d_delete(dentry); return res; } |