diff options
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 123 |
1 files changed, 87 insertions, 36 deletions
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 |