summaryrefslogtreecommitdiffstats
path: root/fs/readdir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/readdir.c')
-rw-r--r--fs/readdir.c38
1 files changed, 27 insertions, 11 deletions
diff --git a/fs/readdir.c b/fs/readdir.c
index 8f40d846a..e1e90c113 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -24,14 +24,23 @@ int vfs_readdir(struct file *file,
down(&inode->i_sem);
down(&inode->i_zombie);
res = -ENOENT;
- if (!IS_DEADDIR(inode))
+ if (!IS_DEADDIR(inode)) {
+ lock_kernel();
res = file->f_op->readdir(file, buf, filler);
+ unlock_kernel();
+ }
up(&inode->i_zombie);
up(&inode->i_sem);
out:
return res;
}
+/*
+ * Directory is locked and all positive dentries in it are safe, since
+ * for ramfs-type trees they can't go away without unlink() or rmdir(),
+ * both impossible due to the lock on directory.
+ */
+
int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
{
int i;
@@ -52,28 +61,39 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
filp->f_pos++;
/* fallthrough */
default: {
- struct list_head *list = dentry->d_subdirs.next;
-
+ struct list_head *list;
int j = i-2;
+
+ spin_lock(&dcache_lock);
+ list = dentry->d_subdirs.next;
+
for (;;) {
- if (list == &dentry->d_subdirs)
+ if (list == &dentry->d_subdirs) {
+ spin_unlock(&dcache_lock);
return 0;
+ }
if (!j)
break;
j--;
list = list->next;
}
- do {
+ while(1) {
struct dentry *de = list_entry(list, struct dentry, d_child);
- if (!d_unhashed(de) && de->d_inode) {
+ if (!list_empty(&de->d_hash) && de->d_inode) {
+ spin_unlock(&dcache_lock);
if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino) < 0)
break;
+ spin_lock(&dcache_lock);
}
filp->f_pos++;
list = list->next;
- } while (list != &dentry->d_subdirs);
+ if (list != &dentry->d_subdirs)
+ continue;
+ spin_unlock(&dcache_lock);
+ break;
+ }
}
}
return 0;
@@ -135,11 +155,9 @@ asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
buf.count = 0;
buf.dirent = dirent;
- lock_kernel();
error = vfs_readdir(file, fillonedir, &buf);
if (error >= 0)
error = buf.count;
- unlock_kernel();
fput(file);
out:
@@ -207,7 +225,6 @@ asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
buf.count = count;
buf.error = 0;
- lock_kernel();
error = vfs_readdir(file, filldir, &buf);
if (error < 0)
goto out_putf;
@@ -219,7 +236,6 @@ asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
}
out_putf:
- unlock_kernel();
fput(file);
out:
return error;