[RFC PATCH 4/5] Directory seek support

2007-12-05 Thread Bharata B Rao
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

2007-12-05 Thread Bharata B Rao
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) {
+