X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fdcache.c;h=953173a293a93e3d8174e7d5955a70f535a75fb5;hb=2c761270d5520dd84ab0b4e47c24d99ff8503c38;hp=64024005da43086141da02f7b2af4234c31883f1;hpb=e2761a1167633ed943fea29002f990194923d060;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/dcache.c b/fs/dcache.c index 6402400..953173a 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -32,9 +31,10 @@ #include #include #include +#include +#include #include "internal.h" - int sysctl_vfs_cache_pressure __read_mostly = 100; EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); @@ -69,6 +69,7 @@ struct dentry_stat_t dentry_stat = { static void __d_free(struct dentry *dentry) { + WARN_ON(!list_empty(&dentry->d_alias)); if (dname_external(dentry)) kfree(dentry->d_name.name); kmem_cache_free(dentry_cache, dentry); @@ -481,7 +482,7 @@ restart: if ((flags & DCACHE_REFERENCED) && (dentry->d_flags & DCACHE_REFERENCED)) { dentry->d_flags &= ~DCACHE_REFERENCED; - list_move_tail(&dentry->d_lru, &referenced); + list_move(&dentry->d_lru, &referenced); spin_unlock(&dentry->d_lock); } else { list_move_tail(&dentry->d_lru, &tmp); @@ -947,9 +948,6 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) dentry->d_op = NULL; dentry->d_fsdata = NULL; dentry->d_mounted = 0; -#ifdef CONFIG_PROFILING - dentry->d_cookie = NULL; -#endif INIT_HLIST_NODE(&dentry->d_hash); INIT_LIST_HEAD(&dentry->d_lru); INIT_LIST_HEAD(&dentry->d_subdirs); @@ -980,6 +978,16 @@ struct dentry *d_alloc_name(struct dentry *parent, const char *name) q.hash = full_name_hash(q.name, q.len); return d_alloc(parent, &q); } +EXPORT_SYMBOL(d_alloc_name); + +/* the caller must hold dcache_lock */ +static void __d_instantiate(struct dentry *dentry, struct inode *inode) +{ + if (inode) + list_add(&dentry->d_alias, &inode->i_dentry); + dentry->d_inode = inode; + fsnotify_d_instantiate(dentry, inode); +} /** * d_instantiate - fill in inode information for a dentry @@ -1000,10 +1008,7 @@ void d_instantiate(struct dentry *entry, struct inode * inode) { BUG_ON(!list_empty(&entry->d_alias)); spin_lock(&dcache_lock); - if (inode) - list_add(&entry->d_alias, &inode->i_dentry); - entry->d_inode = inode; - fsnotify_d_instantiate(entry, inode); + __d_instantiate(entry, inode); spin_unlock(&dcache_lock); security_d_instantiate(entry, inode); } @@ -1033,7 +1038,7 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry, unsigned int hash = entry->d_name.hash; if (!inode) { - entry->d_inode = NULL; + __d_instantiate(entry, NULL); return NULL; } @@ -1052,9 +1057,7 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry, return alias; } - list_add(&entry->d_alias, &inode->i_dentry); - entry->d_inode = inode; - fsnotify_d_instantiate(entry, inode); + __d_instantiate(entry, inode); return NULL; } @@ -1179,7 +1182,7 @@ struct dentry *d_obtain_alias(struct inode *inode) iput(inode); return res; } -EXPORT_SYMBOL_GPL(d_obtain_alias); +EXPORT_SYMBOL(d_obtain_alias); /** * d_splice_alias - splice a disconnected dentry into the tree if one exists @@ -1206,17 +1209,14 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) new = __d_find_alias(inode, 1); if (new) { BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED)); - fsnotify_d_instantiate(new, inode); spin_unlock(&dcache_lock); security_d_instantiate(new, inode); d_rehash(dentry); d_move(new, dentry); iput(inode); } else { - /* d_instantiate takes dcache_lock, so we do it by hand */ - list_add(&dentry->d_alias, &inode->i_dentry); - dentry->d_inode = inode; - fsnotify_d_instantiate(dentry, inode); + /* already taking dcache_lock, so d_add() by hand */ + __d_instantiate(dentry, inode); spin_unlock(&dcache_lock); security_d_instantiate(dentry, inode); d_rehash(dentry); @@ -1249,15 +1249,18 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode, struct dentry *found; struct dentry *new; - /* Does a dentry matching the name exist already? */ + /* + * First check if a dentry matching the name already exists, + * if not go ahead and create it now. + */ found = d_hash_and_lookup(dentry->d_parent, name); - /* If not, create it now and return */ if (!found) { new = d_alloc(dentry->d_parent, name); if (!new) { error = -ENOMEM; goto err_out; } + found = d_splice_alias(inode, new); if (found) { dput(new); @@ -1265,62 +1268,46 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode, } return new; } - /* Matching dentry exists, check if it is negative. */ + + /* + * If a matching dentry exists, and it's not negative use it. + * + * Decrement the reference count to balance the iget() done + * earlier on. + */ if (found->d_inode) { if (unlikely(found->d_inode != inode)) { /* This can't happen because bad inodes are unhashed. */ BUG_ON(!is_bad_inode(inode)); BUG_ON(!is_bad_inode(found->d_inode)); } - /* - * Already have the inode and the dentry attached, decrement - * the reference count to balance the iget() done - * earlier on. We found the dentry using d_lookup() so it - * cannot be disconnected and thus we do not need to worry - * about any NFS/disconnectedness issues here. - */ iput(inode); return found; } + /* * Negative dentry: instantiate it unless the inode is a directory and - * has a 'disconnected' dentry (i.e. IS_ROOT and DCACHE_DISCONNECTED), - * in which case d_move() that in place of the found dentry. + * already has a dentry. */ - if (!S_ISDIR(inode->i_mode)) { - /* Not a directory; everything is easy. */ - d_instantiate(found, inode); - return found; - } spin_lock(&dcache_lock); - if (list_empty(&inode->i_dentry)) { - /* - * Directory without a 'disconnected' dentry; we need to do - * d_instantiate() by hand because it takes dcache_lock which - * we already hold. - */ - list_add(&found->d_alias, &inode->i_dentry); - found->d_inode = inode; + if (!S_ISDIR(inode->i_mode) || list_empty(&inode->i_dentry)) { + __d_instantiate(found, inode); spin_unlock(&dcache_lock); security_d_instantiate(found, inode); return found; } + /* - * Directory with a 'disconnected' dentry; get a reference to the - * 'disconnected' dentry. + * In case a directory already has a (disconnected) entry grab a + * reference to it, move it in place and use it. */ new = list_entry(inode->i_dentry.next, struct dentry, d_alias); dget_locked(new); spin_unlock(&dcache_lock); - /* Do security vodoo. */ security_d_instantiate(found, inode); - /* Move new in place of found. */ d_move(new, found); - /* Balance the iget() we did above. */ iput(inode); - /* Throw away found. */ dput(found); - /* Use new as the actual dentry. */ return new; err_out: @@ -1335,7 +1322,7 @@ err_out: * * Searches the children of the parent dentry for the name in question. If * the dentry is found its reference count is incremented and the dentry - * is returned. The caller must use d_put to free the entry when it has + * is returned. The caller must use dput to free the entry when it has * finished using it. %NULL is returned on failure. * * __d_lookup is dcache_lock free. The hash list is protected using RCU. @@ -1462,8 +1449,6 @@ out: * d_validate - verify dentry provided from insecure source * @dentry: The dentry alleged to be valid child of @dparent * @dparent: The parent dentry (known to be valid) - * @hash: Hash of the dentry - * @len: Length of the name * * An insecure source has sent us a dentry, here we verify it and dget() it. * This is used by ncpfs in its readdir implementation. @@ -1572,10 +1557,6 @@ void d_rehash(struct dentry * entry) spin_unlock(&dcache_lock); } -#define do_switch(x,y) do { \ - __typeof__ (x) __tmp = x; \ - x = y; y = __tmp; } while (0) - /* * When switching names, the actual string doesn't strictly have to * be preserved in the target - because we're dropping the target @@ -1594,7 +1575,7 @@ static void switch_names(struct dentry *dentry, struct dentry *target) /* * Both external: swap the pointers */ - do_switch(target->d_name.name, dentry->d_name.name); + swap(target->d_name.name, dentry->d_name.name); } else { /* * dentry:internal, target:external. Steal target's @@ -1621,8 +1602,11 @@ static void switch_names(struct dentry *dentry, struct dentry *target) */ memcpy(dentry->d_iname, target->d_name.name, target->d_name.len + 1); + dentry->d_name.len = target->d_name.len; + return; } } + swap(dentry->d_name.len, target->d_name.len); } /* @@ -1682,8 +1666,7 @@ already_unhashed: /* Switch the names.. */ switch_names(dentry, target); - do_switch(dentry->d_name.len, target->d_name.len); - do_switch(dentry->d_name.hash, target->d_name.hash); + swap(dentry->d_name.hash, target->d_name.hash); /* ... and switch the parents */ if (IS_ROOT(dentry)) { @@ -1691,7 +1674,7 @@ already_unhashed: target->d_parent = target; INIT_LIST_HEAD(&target->d_u.d_child); } else { - do_switch(dentry->d_parent, target->d_parent); + swap(dentry->d_parent, target->d_parent); /* And add them back to the (new) parent lists */ list_add(&target->d_u.d_child, &target->d_parent->d_subdirs); @@ -1792,8 +1775,7 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon) struct dentry *dparent, *aparent; switch_names(dentry, anon); - do_switch(dentry->d_name.len, anon->d_name.len); - do_switch(dentry->d_name.hash, anon->d_name.hash); + swap(dentry->d_name.hash, anon->d_name.hash); dparent = dentry->d_parent; aparent = anon->d_parent; @@ -1833,7 +1815,7 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) if (!inode) { actual = dentry; - dentry->d_inode = NULL; + __d_instantiate(dentry, NULL); goto found_lock; } @@ -1912,7 +1894,8 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) * Convert a dentry into an ASCII path name. If the entry has been deleted * the string " (deleted)" is appended. Note that this is ambiguous. * - * Returns the buffer or an error code if the path was too long. + * Returns a pointer into the buffer or an error code if the + * path was too long. * * "buflen" should be positive. Caller holds the dcache_lock. * @@ -1929,7 +1912,7 @@ char *__d_path(const struct path *path, struct path *root, spin_lock(&vfsmount_lock); prepend(&end, &buflen, "\0", 1); - if (!IS_ROOT(dentry) && d_unhashed(dentry) && + if (d_unlinked(dentry) && (prepend(&end, &buflen, " (deleted)", 10) != 0)) goto Elong; @@ -1988,7 +1971,10 @@ Elong: * Convert a dentry into an ASCII path name. If the entry has been deleted * the string " (deleted)" is appended. Note that this is ambiguous. * - * Returns the buffer or an error code if the path was too long. + * Returns a pointer into the buffer or an error code if the path was + * too long. Note: Callers should use the returned pointer, not the passed + * in buffer, to use the name! The implementation often starts at an offset + * into the buffer, and may leave 0 bytes at the start. * * "buflen" should be positive. */ @@ -2051,7 +2037,7 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen) spin_lock(&dcache_lock); prepend(&end, &buflen, "\0", 1); - if (!IS_ROOT(dentry) && d_unhashed(dentry) && + if (d_unlinked(dentry) && (prepend(&end, &buflen, "//deleted", 9) != 0)) goto Elong; if (buflen < 1) @@ -2096,7 +2082,7 @@ Elong: * return NULL; * } */ -asmlinkage long sys_getcwd(char __user *buf, unsigned long size) +SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) { int error; struct path pwd, root; @@ -2113,9 +2099,8 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size) read_unlock(¤t->fs->lock); error = -ENOENT; - /* Has the current directory has been unlinked? */ spin_lock(&dcache_lock); - if (IS_ROOT(pwd.dentry) || !d_unhashed(pwd.dentry)) { + if (!d_unlinked(pwd.dentry)) { unsigned long len; struct path tmp = root; char * cwd; @@ -2165,7 +2150,6 @@ int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry) int result; unsigned long seq; - /* FIXME: This is old behavior, needed? Please check callers. */ if (new_dentry == old_dentry) return 1; @@ -2314,9 +2298,6 @@ static void __init dcache_init(void) /* SLAB cache for __getname() consumers */ struct kmem_cache *names_cachep __read_mostly; -/* SLAB cache for file structures */ -struct kmem_cache *filp_cachep __read_mostly; - EXPORT_SYMBOL(d_genocide); void __init vfs_caches_init_early(void) @@ -2338,9 +2319,6 @@ void __init vfs_caches_init(unsigned long mempages) names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); - filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); - dcache_init(); inode_init(); files_init(mempages);