Commit c6e6354f authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] clean up readdir() for in-memory

sane readdir() for ramfs-style filesystems
parent cc41b90f
......@@ -27,6 +27,9 @@ static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigne
static struct dentry *autofs4_root_lookup(struct inode *,struct dentry *);
struct file_operations autofs4_root_operations = {
open: dcache_dir_open,
release: dcache_dir_close,
llseek: dcache_dir_lseek,
read: generic_read_dir,
readdir: dcache_readdir,
ioctl: autofs4_root_ioctl,
......
......@@ -29,6 +29,59 @@ int simple_sync_file(struct file * file, struct dentry *dentry, int datasync)
{
return 0;
}
int dcache_dir_open(struct inode *inode, struct file *file)
{
static struct qstr cursor_name = {len:1, name:"."};
file->private_data = d_alloc(file->f_dentry, &cursor_name);
return file->private_data ? 0 : -ENOMEM;
}
int dcache_dir_close(struct inode *inode, struct file *file)
{
dput(file->private_data);
return 0;
}
loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
{
down(&file->f_dentry->d_inode->i_sem);
switch (origin) {
case 1:
offset += file->f_pos;
case 0:
if (offset >= 0)
break;
default:
up(&file->f_dentry->d_inode->i_sem);
return -EINVAL;
}
if (offset != file->f_pos) {
file->f_pos = offset;
if (file->f_pos >= 2) {
struct list_head *p;
struct dentry *cursor = file->private_data;
loff_t n = file->f_pos - 2;
spin_lock(&dcache_lock);
p = file->f_dentry->d_subdirs.next;
while (n && p != &file->f_dentry->d_subdirs) {
struct dentry *next;
next = list_entry(p, struct dentry, d_child);
if (!list_empty(&next->d_hash) && next->d_inode)
n--;
p = p->next;
}
list_del(&cursor->d_child);
list_add_tail(&cursor->d_child, p);
spin_unlock(&dcache_lock);
}
}
up(&file->f_dentry->d_inode->i_sem);
return offset;
}
/*
* Directory is locked and all positive dentries in it are safe, since
......@@ -38,67 +91,51 @@ int simple_sync_file(struct file * file, struct dentry *dentry, int datasync)
int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
{
int i;
struct dentry *dentry = filp->f_dentry;
struct dentry *cursor = filp->private_data;
struct list_head *p, *q = &cursor->d_child;
ino_t ino;
int i = filp->f_pos;
lock_kernel();
i = filp->f_pos;
switch (i) {
case 0:
if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0)
ino = dentry->d_inode->i_ino;
if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
break;
i++;
filp->f_pos++;
i++;
/* fallthrough */
case 1:
if (filldir(dirent, "..", 2, i, parent_ino(dentry), DT_DIR) < 0)
ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
break;
i++;
filp->f_pos++;
i++;
/* fallthrough */
default: {
struct list_head *list;
int j = i-2;
default:
spin_lock(&dcache_lock);
list = dentry->d_subdirs.next;
for (;;) {
if (list == &dentry->d_subdirs) {
spin_unlock(&dcache_lock);
unlock_kernel();
return 0;
}
if (!j)
break;
j--;
list = list->next;
if (filp->f_pos == 2) {
list_del(q);
list_add(q, &dentry->d_subdirs);
}
while(1) {
struct dentry *de = list_entry(list, struct dentry, d_child);
/*
* See comment on top of function on why we
* can just drop the lock here..
*/
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, DT_UNKNOWN) < 0)
break;
spin_lock(&dcache_lock);
}
filp->f_pos++;
list = list->next;
if (list != &dentry->d_subdirs)
for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
struct dentry *next;
next = list_entry(p, struct dentry, d_child);
if (list_empty(&next->d_hash) || !next->d_inode)
continue;
spin_unlock(&dcache_lock);
break;
if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, DT_UNKNOWN) < 0)
return 0;
spin_lock(&dcache_lock);
/* next is still alive */
list_del(q);
list_add(q, p);
p = q;
filp->f_pos++;
}
}
spin_unlock(&dcache_lock);
}
unlock_kernel();
return 0;
}
......@@ -108,6 +145,9 @@ ssize_t generic_read_dir(struct file *filp, char *buf, size_t siz, loff_t *ppos)
}
struct file_operations simple_dir_operations = {
open: dcache_dir_open,
release: dcache_dir_close,
llseek: dcache_dir_lseek,
read: generic_read_dir,
readdir: dcache_readdir,
};
......
......@@ -1296,6 +1296,9 @@ extern void drop_super(struct super_block *sb);
extern kdev_t ROOT_DEV;
extern char root_device_name[];
extern int dcache_dir_open(struct inode *, struct file *);
extern int dcache_dir_close(struct inode *, struct file *);
extern loff_t dcache_dir_lseek(struct file *, loff_t, int);
extern int dcache_readdir(struct file *, void *, filldir_t);
extern int simple_statfs(struct super_block *, struct statfs *);
extern struct dentry *simple_lookup(struct inode *, struct dentry *);
......
......@@ -281,6 +281,9 @@ EXPORT_SYMBOL(__get_lease);
EXPORT_SYMBOL(lease_get_mtime);
EXPORT_SYMBOL(lock_may_read);
EXPORT_SYMBOL(lock_may_write);
EXPORT_SYMBOL(dcache_dir_open);
EXPORT_SYMBOL(dcache_dir_close);
EXPORT_SYMBOL(dcache_dir_lseek);
EXPORT_SYMBOL(dcache_readdir);
EXPORT_SYMBOL(simple_statfs);
EXPORT_SYMBOL(simple_lookup);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment