[RFC PATCH 4/5] Directory seek support
Directory seek support. Define the seek behaviour on the stored cache of dirents. Signed-off-by: Bharata B Rao <[EMAIL PROTECTED]> --- fs/read_write.c | 11 --- fs/union.c| 171 +- include/linux/fs.h|8 ++ include/linux/union.h | 25 +++ 4 files changed, 205 insertions(+), 10 deletions(-) --- a/fs/read_write.c +++ b/fs/read_write.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "read_write.h" #include @@ -116,15 +117,7 @@ EXPORT_SYMBOL(default_llseek); loff_t vfs_llseek(struct file *file, loff_t offset, int origin) { - loff_t (*fn)(struct file *, loff_t, int); - - fn = no_llseek; - if (file->f_mode & FMODE_LSEEK) { - fn = default_llseek; - if (file->f_op && file->f_op->llseek) - fn = file->f_op->llseek; - } - return fn(file, offset, origin); + return do_llseek(file, offset, origin); } EXPORT_SYMBOL(vfs_llseek); --- a/fs/union.c +++ b/fs/union.c @@ -614,6 +614,7 @@ static int rdcache_add_entry(struct rdst this->dtype = d_type; INIT_LIST_HEAD(>list); list_add_tail(>list, list); + r->cur_dirent = this; return 0; } @@ -636,18 +637,96 @@ static int filldir_union(void *buf, cons if (rdcache_find_entry(>dirent_cache, name, namlen)) return 0; - err = cb->filldir(cb->buf, name, namlen, r->cur_off, + /* We come here with NULL cb->filldir from lseek path */ + if (cb->filldir) + err = cb->filldir(cb->buf, name, namlen, r->cur_off, ino, d_type); if (err >= 0) { rdcache_add_entry(r, >dirent_cache, name, namlen, offset, ino, d_type); r->cur_off = ++r->last_off; r->nr_dirents++; + if (r->cur_off == r->fill_off) { + /* We filled up to the required seek offset */ + r->fill_off = 0; + err = -EINVAL; + } } cb->error = err; return err; } +/* + * This is called when current offset in rdcache gets changed and when + * we need to change the current underlying directory in the rdstate + * to match the current offset. + */ +static void update_rdstate(struct file *file) +{ + struct rdstate *r = file->f_rdstate; + loff_t off = r->cur_off; + struct union_mount *um; + + if (!(r->flags & RDSTATE_NEED_UPDATE)) + return; + + spin_lock(_lock); + um = union_lookup(file->f_path.dentry, file->f_path.mnt); + spin_unlock(_lock); + if (!um) + goto out; + off -= um->nr_dirents; + path_put(>cur_path); + r->cur_path = file->f_path; + path_get(>cur_path); + + while (off > 0) { + spin_lock(_lock); + um = union_lookup(r->cur_path.dentry, r->cur_path.mnt); + spin_unlock(_lock); + if (!um) + goto out; + off -= um->nr_dirents; + path_put(>cur_path); + r->cur_path = um->u_next; + path_get(>cur_path); + } +out: + r->file_off = r->cur_dirent->off; +} + +/* + * Returns dirents from rdcache to userspace. + */ +static int readdir_rdcache(struct file *file, struct rdcache_callback *cb) +{ + struct rdstate *r = cb->rdstate; + struct rdcache_entry *tmp = r->cur_dirent; + int err = 0; + + BUG_ON(r->cur_off > r->last_off); + + /* If offsets already uptodate, just return */ + if (likely(r->cur_off == r->last_off)) + return 0; + + /* +* return the entries from cur_off till last_off from rdcache to +* user space. +*/ + list_for_each_entry_from(tmp, >dirent_cache, list) { + err = cb->filldir(cb->buf, tmp->name.name, tmp->name.len, + r->cur_off, tmp->ino, tmp->dtype); + r->cur_dirent = tmp; + if (err < 0) + break; + r->cur_off++; + r->flags |= RDSTATE_NEED_UPDATE; + } + update_rdstate(file); + return err; +} + /* Called from last fput() */ void put_rdstate(struct rdstate *rdstate) { @@ -710,6 +789,10 @@ int readdir_union(struct file *file, voi cb.rdstate = rdstate; cb.error = 0; + err = readdir_rdcache(file, ); + if (err) + return err; + offset = rdstate->file_off; /* Read from the topmost directory */ @@ -796,6 +879,92 @@ out: return err; } +static void rdcache_rewind(struct file *file, struct rdstate *r, loff_t offset) +{ + struct rdcache_entry *tmp = r->cur_dirent; + + list_for_each_entry_reverse_from(tmp, >dirent_cache, list) { + if (r->cur_off == offset) +
[RFC PATCH 4/5] Directory seek support
Directory seek support. Define the seek behaviour on the stored cache of dirents. Signed-off-by: Bharata B Rao [EMAIL PROTECTED] --- fs/read_write.c | 11 --- fs/union.c| 171 +- include/linux/fs.h|8 ++ include/linux/union.h | 25 +++ 4 files changed, 205 insertions(+), 10 deletions(-) --- a/fs/read_write.c +++ b/fs/read_write.c @@ -16,6 +16,7 @@ #include linux/syscalls.h #include linux/pagemap.h #include linux/splice.h +#include linux/union.h #include read_write.h #include asm/uaccess.h @@ -116,15 +117,7 @@ EXPORT_SYMBOL(default_llseek); loff_t vfs_llseek(struct file *file, loff_t offset, int origin) { - loff_t (*fn)(struct file *, loff_t, int); - - fn = no_llseek; - if (file-f_mode FMODE_LSEEK) { - fn = default_llseek; - if (file-f_op file-f_op-llseek) - fn = file-f_op-llseek; - } - return fn(file, offset, origin); + return do_llseek(file, offset, origin); } EXPORT_SYMBOL(vfs_llseek); --- a/fs/union.c +++ b/fs/union.c @@ -614,6 +614,7 @@ static int rdcache_add_entry(struct rdst this-dtype = d_type; INIT_LIST_HEAD(this-list); list_add_tail(this-list, list); + r-cur_dirent = this; return 0; } @@ -636,18 +637,96 @@ static int filldir_union(void *buf, cons if (rdcache_find_entry(r-dirent_cache, name, namlen)) return 0; - err = cb-filldir(cb-buf, name, namlen, r-cur_off, + /* We come here with NULL cb-filldir from lseek path */ + if (cb-filldir) + err = cb-filldir(cb-buf, name, namlen, r-cur_off, ino, d_type); if (err = 0) { rdcache_add_entry(r, r-dirent_cache, name, namlen, offset, ino, d_type); r-cur_off = ++r-last_off; r-nr_dirents++; + if (r-cur_off == r-fill_off) { + /* We filled up to the required seek offset */ + r-fill_off = 0; + err = -EINVAL; + } } cb-error = err; return err; } +/* + * This is called when current offset in rdcache gets changed and when + * we need to change the current underlying directory in the rdstate + * to match the current offset. + */ +static void update_rdstate(struct file *file) +{ + struct rdstate *r = file-f_rdstate; + loff_t off = r-cur_off; + struct union_mount *um; + + if (!(r-flags RDSTATE_NEED_UPDATE)) + return; + + spin_lock(union_lock); + um = union_lookup(file-f_path.dentry, file-f_path.mnt); + spin_unlock(union_lock); + if (!um) + goto out; + off -= um-nr_dirents; + path_put(r-cur_path); + r-cur_path = file-f_path; + path_get(r-cur_path); + + while (off 0) { + spin_lock(union_lock); + um = union_lookup(r-cur_path.dentry, r-cur_path.mnt); + spin_unlock(union_lock); + if (!um) + goto out; + off -= um-nr_dirents; + path_put(r-cur_path); + r-cur_path = um-u_next; + path_get(r-cur_path); + } +out: + r-file_off = r-cur_dirent-off; +} + +/* + * Returns dirents from rdcache to userspace. + */ +static int readdir_rdcache(struct file *file, struct rdcache_callback *cb) +{ + struct rdstate *r = cb-rdstate; + struct rdcache_entry *tmp = r-cur_dirent; + int err = 0; + + BUG_ON(r-cur_off r-last_off); + + /* If offsets already uptodate, just return */ + if (likely(r-cur_off == r-last_off)) + return 0; + + /* +* return the entries from cur_off till last_off from rdcache to +* user space. +*/ + list_for_each_entry_from(tmp, r-dirent_cache, list) { + err = cb-filldir(cb-buf, tmp-name.name, tmp-name.len, + r-cur_off, tmp-ino, tmp-dtype); + r-cur_dirent = tmp; + if (err 0) + break; + r-cur_off++; + r-flags |= RDSTATE_NEED_UPDATE; + } + update_rdstate(file); + return err; +} + /* Called from last fput() */ void put_rdstate(struct rdstate *rdstate) { @@ -710,6 +789,10 @@ int readdir_union(struct file *file, voi cb.rdstate = rdstate; cb.error = 0; + err = readdir_rdcache(file, cb); + if (err) + return err; + offset = rdstate-file_off; /* Read from the topmost directory */ @@ -796,6 +879,92 @@ out: return err; } +static void rdcache_rewind(struct file *file, struct rdstate *r, loff_t offset) +{ + struct rdcache_entry *tmp = r-cur_dirent; + + list_for_each_entry_reverse_from(tmp, r-dirent_cache, list) { +