diff options
Diffstat (limited to 'fs/readdir.c')
-rw-r--r-- | fs/readdir.c | 38 |
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; |