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:46 UTC|newest]
Thread overview: 17+ 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 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).