From: Bharata B Rao <bharata@linux.vnet.ibm.com>
To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Jan Blunck <j.blunck@tu-harburg.de>
Subject: [RFC PATCH 4/4] Directory listing support for union mounted directories.
Date: Wed, 20 Jun 2007 11:24:18 +0530 [thread overview]
Message-ID: <20070620055418.GF4267@in.ibm.com> (raw)
In-Reply-To: <20070620055050.GB4267@in.ibm.com>
From: Bharata B Rao <bharata@linux.vnet.ibm.com>
Subject: Directory listing support for union mounted directories.
Modifies readdir()/getdents() to support union mounted directories.
This patch adds support to readdir()/getdents() to read directory entries
from all the directories of the union stack, and present a merged view to
the userspace. This is achieved by maintaining a cache of read entries.
TODO: Breaks lseek on union mounted directories, Fix this.
Signed-off-by: Bharata B Rao <bharata@linux.vnet.ibm.com>
---
fs/file_table.c | 1
fs/readdir.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
include/linux/fs.h | 12 ++
3 files changed, 254 insertions(+), 1 deletion(-)
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -174,6 +174,7 @@ void fastcall __fput(struct file *file)
if (file->f_mode & FMODE_WRITE)
put_write_access(inode);
put_pid(file->f_owner.pid);
+ put_rdstate(file->f_rdstate);
file_kill(file);
file->f_path.dentry = NULL;
file->f_path.mnt = NULL;
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -19,6 +19,8 @@
#include <asm/uaccess.h>
+int readdir_union(struct file *file, void *buf, filldir_t filler);
+
int vfs_readdir(struct file *file, filldir_t filler, void *buf)
{
struct inode *inode = file->f_path.dentry->d_inode;
@@ -33,7 +35,10 @@ int vfs_readdir(struct file *file, filld
mutex_lock(&inode->i_mutex);
res = -ENOENT;
if (!IS_DEADDIR(inode)) {
- res = file->f_op->readdir(file, buf, filler);
+ if (IS_MNT_UNION(file->f_path.mnt))
+ res = readdir_union(file, buf, filler);
+ else
+ res = file->f_op->readdir(file, buf, filler);
file_accessed(file);
}
mutex_unlock(&inode->i_mutex);
@@ -303,3 +308,238 @@ out_putf:
out:
return error;
}
+
+/*
+ * NOTE: Some of the code below which maintains a list of dirents as a cache
+ * is from Jan Blunck's original union mount readdir patch.
+ */
+
+/* The readdir cache object */
+struct rdcache_entry {
+ struct list_head list;
+ struct qstr name;
+};
+
+struct rdcache_callback {
+ void *buf; /* original callback buffer */
+ filldir_t filldir; /* the filldir() we should call */
+ int error; /* stores filldir error */
+ struct rdstate *rdstate; /* readdir state */
+};
+
+static int rdcache_add_entry(struct list_head *list,
+ const char *name, int namelen)
+{
+ struct rdcache_entry *this;
+ char *tmp_name;
+
+ this = kmalloc(sizeof(*this), GFP_KERNEL);
+ if (!this) {
+ printk(KERN_CRIT "rdcache_add_entry(): out of kernel memory\n");
+ return -ENOMEM;
+ }
+
+ tmp_name = kmalloc(namelen + 1, GFP_KERNEL);
+ if (!tmp_name) {
+ printk(KERN_CRIT "rdcache_add_entry(): out of kernel memory\n");
+ kfree(this);
+ return -ENOMEM;
+ }
+
+ this->name.name = tmp_name;
+ this->name.len = namelen;
+ this->name.hash = 0;
+ memcpy(tmp_name, name, namelen);
+ tmp_name[namelen] = 0;
+ INIT_LIST_HEAD(&this->list);
+ list_add(&this->list, list);
+ return 0;
+}
+
+static void rdcache_free(struct list_head *list)
+{
+ struct list_head *p;
+ struct list_head *ptmp;
+ int count = 0;
+
+ list_for_each_safe(p, ptmp, list) {
+ struct rdcache_entry *this;
+
+ this = list_entry(p, struct rdcache_entry, list);
+ list_del_init(&this->list);
+ kfree(this->name.name);
+ kfree(this);
+ count++;
+ }
+ return;
+}
+
+static int rdcache_find_entry(struct list_head *uc_list,
+ const char *name, int namelen)
+{
+ struct rdcache_entry *p;
+ int ret = 0;
+
+ list_for_each_entry(p, uc_list, list) {
+ if (p->name.len != namelen)
+ continue;
+ if (strncmp(p->name.name, name, namelen) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
+ * filldir routine for union mounted directories.
+ * Handles duplicate elimination by building a readdir cache.
+ * TODO: readdir cache is a linked list. Check if this can be designed
+ * more efficiently.
+ */
+static int filldir_union(void *buf, const char *name, int namlen,
+ loff_t offset, u64 ino, unsigned int d_type)
+{
+ struct rdcache_callback *cb = buf;
+ int err;
+
+ if (rdcache_find_entry(&cb->rdstate->dirent_cache, name, namlen))
+ return 0;
+
+ err = cb->filldir(cb->buf, name, namlen, offset, ino, d_type);
+ if (err >= 0)
+ rdcache_add_entry(&cb->rdstate->dirent_cache, name, namlen);
+ cb->error = err;
+ return err;
+}
+
+/* Called from last fput() */
+void put_rdstate(struct rdstate *rdstate)
+{
+ if (!rdstate)
+ return;
+
+ mntput(rdstate->mnt);
+ dput(rdstate->dentry);
+ rdcache_free(&rdstate->dirent_cache);
+ kfree(rdstate);
+}
+
+static struct rdstate *get_rdstate(struct file *file)
+{
+ if (file->f_rdstate)
+ return file->f_rdstate;
+
+ /*
+ * We have read the dirents from this earlier but now don't have a
+ * corresponding rdstate. This shouldn't happen.
+ */
+ if (file->f_pos)
+ return ERR_PTR(-EINVAL);
+
+ file->f_rdstate = kmalloc(sizeof(struct rdstate), GFP_KERNEL);
+ if (!file->f_rdstate)
+ return ERR_PTR(-ENOMEM);
+
+ file->f_rdstate->off = 0;
+ file->f_rdstate->mnt = mntget(file->f_path.mnt);
+ file->f_rdstate->dentry = dget(file->f_path.dentry);
+ INIT_LIST_HEAD(&file->f_rdstate->dirent_cache);
+ return file->f_rdstate;
+}
+
+int readdir_union(struct file *file, void *buf, filldir_t filler)
+{
+ struct dentry *topmost = file->f_path.dentry;
+ struct rdstate *rdstate;
+ struct nameidata nd;
+ loff_t offset = 0;
+ struct rdcache_callback cb;
+ int err = 0;
+
+ /*
+ * If this is not a union mount point or not a unioned directory
+ * within the union mount point, do readdir in the normal way.
+ */
+ nd.mnt = file->f_path.mnt;
+ nd.dentry = file->f_path.dentry;
+ if (!next_union_mount_exists(nd.mnt, nd.dentry))
+ return file->f_op->readdir(file, buf, filler);
+
+ rdstate = get_rdstate(file);
+ if (IS_ERR(rdstate)) {
+ err = PTR_ERR(rdstate);
+ return err;
+ }
+
+ cb.buf = buf;
+ cb.filldir = filler;
+ cb.rdstate = rdstate;
+
+ offset = rdstate->off;
+
+ /* Read from the topmost directory */
+ if (rdstate->dentry == topmost) {
+ file->f_pos = offset;
+ err = file->f_op->readdir(file, &cb, filldir_union);
+ rdstate->off = file->f_pos;
+ if (err < 0 || cb.error < 0)
+ goto out;
+
+ /*
+ * Reading from topmost dir complete, start reading the lower
+ * dir from the beginning.
+ */
+ offset = 0;
+ } else {
+ nd.mnt = rdstate->mnt;
+ nd.dentry = rdstate->dentry;
+ goto read_lower;
+ }
+
+ if (!next_union_mount(&nd))
+ return err;
+
+read_lower:
+ do {
+ struct vfsmount *mnt_tmp = mntget(nd.mnt);
+ struct dentry *dentry_tmp = dget(nd.dentry);
+ struct file *ftmp = dentry_open(nd.dentry, nd.mnt,
+ file->f_flags);
+ if (IS_ERR(ftmp)) {
+ mntput(mnt_tmp);
+ dput(dentry_tmp);
+ err = PTR_ERR(ftmp);
+ goto out;
+ }
+
+ mutex_lock(&nd.dentry->d_inode->i_mutex);
+ ftmp->f_pos = offset;
+
+ err = ftmp->f_op->readdir(ftmp, &cb, filldir_union);
+ file_accessed(ftmp);
+ rdstate->off = ftmp->f_pos;
+ mutex_unlock(&nd.dentry->d_inode->i_mutex);
+ if (nd.mnt != rdstate->mnt) {
+ mntput(rdstate->mnt);
+ rdstate->mnt = mntget(nd.mnt);
+ }
+ if (nd.dentry != rdstate->dentry) {
+ dput(rdstate->dentry);
+ rdstate->dentry = dget(nd.dentry);
+ }
+ fput(ftmp);
+ if (err < 0 || cb.error < 0)
+ goto out;
+
+ /*
+ * Reading from a lower dir complete, start reading the
+ * next lower dir from the beginning.
+ */
+ offset = 0;
+ } while (next_union_mount(&nd));
+
+ return 0;
+out:
+ return err;
+}
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -841,6 +841,14 @@ static inline void ra_set_size(struct fi
unsigned long ra_submit(struct file_ra_state *ra,
struct address_space *mapping, struct file *filp);
+/* Direct cache for readdir, used for union mounted directories. */
+struct rdstate {
+ struct vfsmount *mnt;
+ struct dentry *dentry;
+ loff_t off;
+ struct list_head dirent_cache;
+};
+
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
@@ -875,6 +883,7 @@ struct file {
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
+ struct rdstate *f_rdstate;
};
extern spinlock_t files_lock;
#define file_list_lock() spin_lock(&files_lock);
@@ -2119,5 +2128,8 @@ static inline void free_secdata(void *se
{ }
#endif /* CONFIG_SECURITY */
+/* fs/readdir.c */
+void put_rdstate(struct rdstate *rdstate);
+
#endif /* __KERNEL__ */
#endif /* _LINUX_FS_H */
next prev parent reply other threads:[~2007-06-20 5:47 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-06-20 5:50 [RFC PATCH 0/4] New approach to VFS based union mount Bharata B Rao
2007-06-20 5:51 ` [RFC PATCH 1/4] Union mount documentation Bharata B Rao
2007-06-20 5:59 ` Arjan van de Ven
2007-06-20 7:29 ` Jan Blunck
2007-06-20 12:32 ` Christoph Hellwig
2007-06-20 12:43 ` Jan Blunck
2007-06-20 13:25 ` Christoph Hellwig
2007-06-20 17:28 ` Erez Zadok
2007-06-21 5:25 ` Bharata B Rao
2007-06-21 16:29 ` Josef Sipek
2007-06-21 16:39 ` Erez Zadok
2007-06-20 12:56 ` Jan Blunck
2007-06-20 8:11 ` Jan Blunck
2007-06-20 9:09 ` Bharata B Rao
2007-06-20 5:52 ` [RFC PATCH 2/4] Mount changes to support union mount Bharata B Rao
2007-06-20 7:47 ` Jan Blunck
2007-06-20 8:53 ` Bharata B Rao
2007-06-21 16:40 ` Josef Sipek
2007-06-20 5:53 ` [RFC PATCH 3/4] Lookup " Bharata B Rao
2007-06-20 7:51 ` Jan Blunck
2007-06-20 8:56 ` Bharata B Rao
2007-06-20 5:54 ` Bharata B Rao [this message]
2007-06-20 12:09 ` [RFC PATCH 4/4] Directory listing support for union mounted directories Christoph Hellwig
2007-06-20 14:22 ` Trond Myklebust
2007-06-20 17:02 ` Christoph Hellwig
2007-06-20 17:44 ` Trond Myklebust
2007-06-30 9:43 ` Christoph Hellwig
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20070620055418.GF4267@in.ibm.com \
--to=bharata@linux.vnet.ibm.com \
--cc=j.blunck@tu-harburg.de \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.