lkml.org 
[lkml]   [2007]   [May]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    SubjectRe: [RFC][PATCH 8/14] Union-mount lookup
    From
    Date
    On Mon, 2007-05-14 at 15:12 +0530, Bharata B Rao wrote:
    > From: Jan Blunck <j.blunck@tu-harburg.de>
    > Subject: Union-mount lookup
    >
    > Modifies the vfs lookup routines to work with union mounted directories.
    >
    > The existing lookup routines generally lookup for a pathname only in the
    > topmost or given directory. The changed versions of the lookup routines
    > search for the pathname in the entire union mounted stack. Also they have been
    > modified to setup the union stack during lookup from dcache cache and from
    > real_lookup().
    >
    > Signed-off-by: Jan Blunck <j.blunck@tu-harburg.de>
    > Signed-off-by: Bharata B Rao <bharata@linux.vnet.ibm.com>
    > ---
    > fs/dcache.c | 16 +
    > fs/namei.c | 78 +++++-
    > fs/namespace.c | 35 ++
    > fs/union.c | 598 +++++++++++++++++++++++++++++++++++++++++++++++++
    > include/linux/dcache.h | 17 +
    > include/linux/namei.h | 4
    > include/linux/union.h | 49 ++++
    > 7 files changed, 786 insertions(+), 11 deletions(-)
    >
    > --- a/fs/dcache.c
    > +++ b/fs/dcache.c
    > @@ -1286,7 +1286,7 @@ struct dentry * d_lookup(struct dentry *
    > return dentry;
    > }
    >
    > -struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
    > +struct dentry * __d_lookup_single(struct dentry *parent, struct qstr *name)
    > {
    > unsigned int len = name->len;
    > unsigned int hash = name->hash;
    > @@ -1371,6 +1371,20 @@ out:
    > return dentry;
    > }
    >
    > +struct dentry * d_lookup_single(struct dentry *parent, struct qstr *name)
    > +{
    > + struct dentry *dentry;
    > + unsigned long seq;
    > +
    > + do {
    > + seq = read_seqbegin(&rename_lock);
    > + dentry = __d_lookup_single(parent, name);
    > + if (dentry)
    > + break;
    > + } while (read_seqretry(&rename_lock, seq));
    > + return dentry;
    > +}
    > +
    > /**
    > * d_validate - verify dentry provided from insecure source
    > * @dentry: The dentry alleged to be valid child of @dparent
    > --- a/fs/namei.c
    > +++ b/fs/namei.c
    > @@ -374,6 +374,33 @@ void release_open_intent(struct nameidat
    > }
    >
    > static inline struct dentry *
    > +do_revalidate_single(struct dentry *dentry, struct nameidata *nd)
    > +{
    > + int status = dentry->d_op->d_revalidate(dentry, nd);
    > + if (unlikely(status <= 0)) {

    d_revalidate() returns a 0 or 1 result, not an error.

    > + /*
    > + * The dentry failed validation.
    > + * If d_revalidate returned 0 attempt to invalidate
    > + * the dentry otherwise d_revalidate is asking us
    > + * to return a fail status.
    > + */
    > + if (!status) {
    > + if (!d_invalidate(dentry)) {
    > + __dput_single(dentry);
    > + dentry = NULL;
    > + }
    > + } else {
    > + __dput_single(dentry);
    > + dentry = ERR_PTR(status);

    See above

    > + }
    > + }
    > + return dentry;
    > +}
    > +
    > +/*
    > + * FIXME: We need a union aware revalidate here!
    > + */
    > +static inline struct dentry *
    > do_revalidate(struct dentry *dentry, struct nameidata *nd)
    > {
    > int status = dentry->d_op->d_revalidate(dentry, nd);
    > @@ -403,16 +430,16 @@ do_revalidate(struct dentry *dentry, str
    > */
    > static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
    > {
    > - struct dentry * dentry = __d_lookup(parent, name);
    > + struct dentry *dentry = __d_lookup_single(parent, name);
    >
    > /* lockess __d_lookup may fail due to concurrent d_move()
    > * in some unrelated directory, so try with d_lookup
    > */
    > if (!dentry)
    > - dentry = d_lookup(parent, name);
    > + dentry = d_lookup_single(parent, name);
    >
    > if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
    > - dentry = do_revalidate(dentry, nd);
    > + dentry = do_revalidate_single(dentry, nd);
    >
    > return dentry;
    > }
    > @@ -465,7 +492,7 @@ ok:
    > * make sure that nobody added the entry to the dcache in the meantime..
    > * SMP-safe
    > */
    > -static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
    > +struct dentry * real_lookup_single(struct dentry *parent, struct qstr *name, struct nameidata *nd)
    > {
    > struct dentry * result;
    > struct inode *dir = parent->d_inode;
    > @@ -485,7 +512,7 @@ static struct dentry * real_lookup(struc
    > *
    > * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
    > */
    > - result = d_lookup(parent, name);
    > + result = d_lookup_single(parent, name);
    > if (!result) {
    > struct dentry * dentry = d_alloc(parent, name);
    > result = ERR_PTR(-ENOMEM);
    > @@ -506,7 +533,7 @@ static struct dentry * real_lookup(struc
    > */
    > mutex_unlock(&dir->i_mutex);
    > if (result->d_op && result->d_op->d_revalidate) {
    > - result = do_revalidate(result, nd);
    > + result = do_revalidate_single(result, nd);
    > if (!result)
    > result = ERR_PTR(-ENOENT);
    > }
    > @@ -699,7 +726,7 @@ static int __follow_mount(struct path *p
    > return res;
    > }
    >
    > -static void follow_mount(struct vfsmount **mnt, struct dentry **dentry)
    > +void follow_mount(struct vfsmount **mnt, struct dentry **dentry)
    > {
    > while (d_mountpoint(*dentry)) {
    > struct vfsmount *mounted = lookup_mnt(*mnt, *dentry);
    > @@ -773,6 +800,7 @@ static __always_inline void follow_dotdo
    > nd->mnt = parent;
    > }
    > follow_mount(&nd->mnt, &nd->dentry);
    > + follow_union_mount(&nd->mnt, &nd->dentry);
    > }
    >
    > /*
    > @@ -784,7 +812,15 @@ static int do_lookup(struct nameidata *n
    > struct path *path)
    > {
    > struct vfsmount *mnt = nd->mnt;
    > - struct dentry *dentry = __d_lookup(nd->dentry, name);
    > + struct dentry *dentry;
    > +
    > + UM_DEBUG_UID("lookup \"%s\" in \"%s\" (inode=%p,dev=%s)\n",
    > + name->name,
    > + nd->dentry->d_name.name,
    > + nd->dentry->d_inode,
    > + nd->mnt->mnt_devname);

    Ugh! Please don't pollute generic VFS code with this sort of private
    debugging crap.

    > +
    > + dentry = __d_lookup(nd->dentry, name);
    >
    > if (!dentry)
    > goto need_lookup;
    > @@ -793,7 +829,17 @@ static int do_lookup(struct nameidata *n
    > done:
    > path->mnt = mnt;
    > path->dentry = dentry;
    > +
    > + if (nd->dentry->d_sb != dentry->d_sb)
    > + path->mnt = find_mnt(dentry);
    > +
    > __follow_mount(path);
    > + follow_union_mount(&path->mnt, &path->dentry);
    > +
    > + UM_DEBUG_UID("found \"%s\" (inode=%p,dev=%s)\n",
    > + path->dentry->d_name.name,
    > + path->dentry->d_inode,
    > + path->mnt->mnt_devname);
    > return 0;
    >
    > need_lookup:
    > @@ -838,6 +884,9 @@ static fastcall int __link_path_walk(con
    > if (nd->depth)
    > lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
    >
    > + UM_DEBUG_UID("begin walking for %s\n", name);
    > + follow_union_mount(&nd->mnt, &nd->dentry);
    > +
    > /* At this point we know we have a real path component. */
    > for(;;) {
    > unsigned long hash;
    > @@ -931,6 +980,7 @@ static fastcall int __link_path_walk(con
    > last_with_slashes:
    > lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
    > last_component:
    > + UM_DEBUG_UID("last component %s\n", this.name);
    > /* Clear LOOKUP_CONTINUE iff it was previously unset */
    > nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
    > if (lookup_flags & LOOKUP_PARENT)
    > @@ -1266,7 +1316,15 @@ int __user_path_lookup_open(const char _
    > return err;
    > }
    >
    > -static inline struct dentry *__lookup_hash_kern(struct qstr *name, struct dentry *base, struct nameidata *nd)
    > +/*
    > + * NOTE: On union mounts it is important that the overlaid dentries are
    > + * correct. Therefore we need to follow mounts. Take a look at
    > + * __lookup_hash_kern_union() how it is done.
    > + *
    > + * Called with union already locked (before the parent inode is locked !!!)
    > + */
    > +struct dentry * __lookup_hash_kern_single(struct qstr *name,
    > + struct dentry *base, struct nameidata *nd)
    > {
    > struct dentry *dentry;
    > struct inode *inode;
    > @@ -1298,6 +1356,8 @@ static inline struct dentry *__lookup_ha
    > dput(new);
    > }
    > out:
    > + UM_DEBUG_UID("name=\"%s\", inode=%p\n",
    > + dentry->d_name.name, dentry->d_inode);
    > return dentry;
    > }
    >
    > --- a/fs/namespace.c
    > +++ b/fs/namespace.c
    > @@ -133,6 +133,41 @@ struct vfsmount *lookup_mnt(struct vfsmo
    > return child_mnt;
    > }
    >
    > +/*
    > + * find_mnt - find a vfsmount struct
    > + * @dentry: a dentry
    > + *
    > + * This searches the namespace for a given dentries
    > + * vfsmount struct. This is used by union-mount.
    > + */
    > +struct vfsmount * find_mnt(struct dentry *dentry)
    > +{
    > + struct list_head *tmp;
    > + struct vfsmount *p, *mnt = NULL;
    > +
    > + down_read(&namespace_sem);
    > + spin_lock(&vfsmount_lock);
    > + if (list_empty(&current->nsproxy->mnt_ns->list)) {
    > + spin_unlock(&vfsmount_lock);
    > + up_read(&namespace_sem);
    > + return NULL;
    > + }
    > + list_for_each(tmp, &current->nsproxy->mnt_ns->list) {
    > + p = list_entry(tmp, struct vfsmount, mnt_list);
    > + if (dentry->d_sb == p->mnt_sb) {
    > + mnt = mntget(p);
    > + break;
    > + }
    > + }
    > + spin_unlock(&vfsmount_lock);
    > + up_read(&namespace_sem);
    > +
    > + BUG_ON(!mnt);
    > +// UM_DEBUG_UID("found %s/%p in %s\n", dentry->d_name.name,
    > +// dentry->d_inode, mnt->mnt_devname);

    ^^^^ Definitely remove

    > + return mnt;
    > +}
    > +
    > static inline int check_mnt(struct vfsmount *mnt)
    > {
    > return mnt->mnt_ns == current->nsproxy->mnt_ns;
    > --- a/fs/union.c
    > +++ b/fs/union.c
    > @@ -370,3 +370,601 @@ void detach_mnt_union(struct vfsmount *m
    > * union stack */
    > __dput(path->dentry);
    > }
    > +
    > +static noinline int revalidate_union(struct dentry * dentry)
    ^^^^^^^^ this is just as bad as 'inline'. I thought we had all
    agreed to leave it up to the compiler to optimise.
    > +{
    > + union_check(dentry);
    > +
    > + spin_lock(&dcache_lock);
    > + spin_lock(&dentry->d_lock);
    > + if (atomic_read(&dentry->d_count) < 2) {
    > + UM_DEBUG_DCACHE("dentry unused, count=%d\n",
    > + atomic_read(&dentry->d_count));
    > + __d_drop(dentry);
    > + spin_unlock(&dentry->d_lock);
    > + spin_unlock(&dcache_lock);
    > + return 0;
    > + }
    > + spin_unlock(&dentry->d_lock);
    > + spin_unlock(&dcache_lock);
    > +
    > + return 1;
    > +}
    > +
    > +static noinline void replace_union_info(struct dentry *dentry,
    > + struct union_info *lock)
    > +{
    > + struct dentry *tmp = dentry;
    > + struct union_info *old_lock = union_get(dentry->d_union);
    > +
    > + BUG_ON(!lock);
    > + BUG_ON(dentry->d_union == lock);
    > +
    > + while (tmp) {
    > + spin_lock(&tmp->d_lock);
    > + union_put(tmp->d_union);
    > + tmp->d_union = union_get(lock);
    > + spin_unlock(&tmp->d_lock);
    > + tmp = tmp->d_overlaid;
    > + }
    > +
    > + BUG_ON(atomic_read(&old_lock->u_count) != 1);
    > + union_put(old_lock);
    > + return;
    > +}
    > +
    > +static void __dput_from_to(struct dentry *from, struct dentry *to,
    > + struct union_info *lock)
    > +{
    > + struct dentry *next = from;
    > + struct union_info *mylock = union_get(from->d_union);
    > +
    > + while (next) {
    > + struct dentry *tmp = next;
    > + next = next->d_overlaid;
    > +
    > + UM_DEBUG_UID("dput_all dentry=\"%s\", inode=\"%p\"\n",
    > + tmp->d_name.name, tmp->d_inode);
    > +
    > + if (lock) {
    > + spin_lock(&tmp->d_lock);
    > + tmp->d_topmost = NULL;
    > + tmp->d_overlaid = NULL;
    > + union_put(tmp->d_union);
    > + tmp->d_union = NULL;
    > + spin_unlock(&tmp->d_lock);
    > + }
    > +
    > + __dput_single(tmp);
    > +
    > + if (tmp == to)
    > + break;
    > + }
    > +
    > + UM_DEBUG_LOCK("\"??\" unlocking union %p\n", lock);
    > + mutex_unlock(&mylock->u_mutex);
    > + union_put(mylock);
    > +}
    > +
    > +/*
    > + * Lookup for the @name in the dentry cache. Look through the lower layers
    > + * of the parent's union stack and build a union stack for the child if
    > + * necessary.
    > + * TODO: This shares a considerable amount of code with __lookup_union().
    > + */
    > +struct dentry * __d_lookup_union(struct dentry *base, struct qstr *name)
    > +{
    > + struct dentry *parent = base->d_overlaid;
    > + struct dentry *dentry = NULL;
    > + struct dentry *topmost;
    > + struct dentry *last;
    > + struct qstr this;
    > + struct union_info *lock = NULL;
    > + int err;
    > +
    > + union_lock(base);
    > + topmost = __d_lookup_single(base, name);
    > + last = topmost;
    > +
    > + /*
    > + * - If dcache lookup returns a NULL dentry, return to force a real
    > + * lookup. Union mount version of real lookup will endup doing real or
    > + * dcache lookup for this in the lower layers also. OR
    > + * - If parent is not a union mounted directory, we are done
    > + * with the lookup, return.
    > + */
    > + if (!topmost || !base->d_overlaid)
    > + goto out;
    > +
    > + this.name = name->name;
    > + this.len = name->len;
    > + this.hash = name->hash;
    > +
    > + /*
    > + * If dcache lookup returned a non-negative dentry from the top layer,
    > + * continue the lookup in to the lower layers and re-build the union
    > + * stack if necessary.
    > + */
    > + if (topmost->d_inode)
    > + goto lookup_union;
    > +
    > + /*
    > + * dcache lookup in the top layer returned a negative dentry. Look
    > + * through the lower layers to find the first non-negative dentry.
    > + */
    > + while (parent) {
    > + if (parent->d_op && parent->d_op->d_hash) {
    > + err = parent->d_op->d_hash(parent, &this);
    > + if (err < 0) {
    > + __dput_single(topmost);
    > + topmost = NULL;
    > + goto out;
    > + }
    > + }
    > + dentry = __d_lookup_single(parent, &this);
    > + /*
    > + * Force a real lookup if parts of the union stack are not in
    > + * dcache
    > + */
    > + if (!dentry) {
    > + __dput_single(topmost);
    > + topmost = NULL;
    > + goto out;
    > + }
    > + if (dentry->d_inode)
    > + break;
    > + __dput_single(dentry);
    > + dentry = NULL;
    > + parent = parent->d_overlaid;
    > + }
    > +
    > + if (!dentry)
    > + goto out;
    > +
    > + __dput_single(topmost);
    > + topmost = dentry;
    > + last = dentry;
    > +lookup_union:
    > + do {
    > + struct vfsmount *mnt = find_mnt(topmost);
    > + UM_DEBUG_DCACHE("name=\"%s\", inode=%p, device=%s\n",
    > + topmost->d_name.name, topmost->d_inode,
    > + mnt->mnt_devname);
    > + mntput(mnt);
    > + } while (0);
    > +
    > + /* If this is not a directory, no need to look beyond this layer */
    > + if (!S_ISDIR(topmost->d_inode->i_mode))
    > + goto out;
    > +
    > + if (!revalidate_union(topmost)) {
    > + __dput_single(topmost);
    > + topmost = NULL;
    > + goto out;
    > + }
    > +
    > + spin_lock(&topmost->d_lock);
    > + if (topmost->d_union) {
    > + union_lock_spinlock(topmost, &topmost->d_lock);
    > + }
    > + spin_unlock(&topmost->d_lock);
    > +
    > + parent = topmost->d_parent->d_overlaid;
    > + while (parent) {
    > + if (parent->d_op && parent->d_op->d_hash) {
    > + err = parent->d_op->d_hash(parent, &this);
    > + if (err < 0) {
    > + UM_DEBUG("failed to hash the qstr\n");
    > + goto dput_all;
    > + }
    > + }
    > + dentry = __d_lookup_single(parent, &this);
    > + if (!dentry) {
    > + /*
    > + * No dentry for this name in this lower layer.
    > + * CHECK: Why return like this ? Shoudn't we look
    > + * for this name in the next lower layer ?
    > + */
    > + __dput_single(dentry);
    > + goto dput_all;
    > + }
    > +
    > + if (!dentry->d_inode) {
    > + __dput_single(dentry);
    > + parent = parent->d_overlaid;
    > + continue;
    > + }
    > + if (!S_ISDIR(dentry->d_inode->i_mode)) {
    > + __dput_single(dentry);
    > + break;
    > + }
    > + if (last->d_overlaid
    > + && (last->d_overlaid != dentry)) {
    > + printk(KERN_ERR "%s: strange stack layout " \
    > + "(\"%s\" overlays \"%s\")\n",
    > + __FUNCTION__, last->d_name.name,
    > + dentry->d_name.name);
    > + dump_stack();
    > + __dput_single(dentry);
    > + goto dput_all;
    > + }
    > + spin_lock(&topmost->d_lock);
    > + if (!topmost->d_union) {
    > + UM_DEBUG_LOCK("allocate union for \"%s\"\n",
    > + topmost->d_name.name);
    > + topmost->d_union = union_alloc();
    > + lock = topmost->d_union;
    > + }
    > + spin_unlock(&topmost->d_lock);
    > + spin_lock(&dentry->d_lock);
    > + if (!dentry->d_union)
    > + dentry->d_union = union_get(topmost->d_union);
    > + spin_unlock(&dentry->d_lock);
    > + if (dentry->d_union != topmost->d_union) {
    > + union_lock(dentry);
    > + replace_union_info(topmost, dentry->d_union);
    > + }
    > + dentry->d_topmost = topmost;
    > + last->d_overlaid = dentry;
    > + last = dentry;
    > + parent = parent->d_overlaid;
    > + }
    > +
    > + spin_lock(&topmost->d_lock);
    > + if (topmost->d_union && atomic_read(&topmost->d_union->u_count) == 1) {
    > + union_put(topmost->d_union);
    > + topmost->d_union = NULL;
    > + } else
    > + union_unlock(topmost);
    > + spin_unlock(&topmost->d_lock);
    > +out:
    > + union_unlock(base);
    > + return topmost;
    > +
    > +dput_all:
    > + __dput_from_to(topmost, last, lock);
    > + union_unlock(base);
    > + return NULL;
    > +}
    > +
    > +/*
    > + * FIXME: export this from fs/namei.c ???
    > + */
    > +extern int follow_mount(struct vfsmount **, struct dentry **);
    > +extern struct dentry * __lookup_hash_kern_single(struct qstr *, struct dentry *,
    > + struct nameidata *);
    > +extern struct dentry * real_lookup_single(struct dentry *, struct qstr *,
    > + struct nameidata *);
    > +
    > +static inline void copy_nd(struct nameidata *old_nd, struct nameidata *new_nd)
    > +{
    > + if (old_nd) {
    > + new_nd->last.name = NULL; /* handled in __link_path_walk */
    > + new_nd->last.len = 0;
    > + new_nd->last.hash = 0;
    > + new_nd->flags = old_nd->flags;
    > + new_nd->um_flags = 0; /* ditto */
    > + new_nd->last_type = -1; /* ditto */
    > + new_nd->depth = 0; /* handled in do_follow_link */
    > + memcpy(&new_nd->intent, &old_nd->intent, sizeof(new_nd->intent));
    > + }
    > +}
    > +
    > +/*
    > + * This is called when a dentries parent is union-mounted and we have
    > + * to lookup the overlaid dentries. The lookup starts at the parents
    > + * first overlaid dentry of the given dentry. Negative dentries are
    > + * ignored and not included in the overlaid list.
    > + *
    > + * If we reach a dentry with restricted access, we just stop the lookup
    > + * because we shouldn't see through that dentry. Same thing for dentry
    > + * type mismatch and whiteouts.
    > + *
    > + * FIXME:
    > + * - handle DT_WHT
    > + * - handle union stacks in use
    > + * - handle union stacks mounted upon union stacks
    > + * - avoid unnecessary allocations of union locks
    > + */
    > +static int __lookup_union(struct dentry *topmost, struct qstr *name,
    > + struct nameidata *__nd)
    > +{
    > + struct dentry *parent;
    > + struct dentry *last;
    > + struct dentry *dentry;
    > + unsigned int hash = name->hash;
    > + struct nameidata nd;
    > + int err;
    > +
    > + /* we may also be called via lookup_hash with a NULLed nd argument */
    > + copy_nd(__nd, &nd);
    > +
    > + spin_lock(&topmost->d_lock);
    > + if (topmost->d_union) {
    > + union_lock_spinlock(topmost, &topmost->d_lock);
    > + }
    > + spin_unlock(&topmost->d_lock);
    > +
    > + parent = topmost->d_parent->d_overlaid;
    > + last = topmost;
    > +
    > + while (parent) {
    > + /*
    > + * the hash could be changed in the last
    > + * __lookup_hash_single() so we need to reset it here
    > + */
    > + name->hash = hash;
    > + nd.dentry = __dget(parent);
    > + nd.mnt = find_mnt(parent);
    > +
    > + mutex_lock(&parent->d_inode->i_mutex);
    > + dentry = __lookup_hash_single(name, parent,
    > + __nd ? &nd : NULL);
    > + mutex_unlock(&parent->d_inode->i_mutex);
    > + if (IS_ERR(dentry)) {
    > + err = PTR_ERR(dentry);
    > + goto out;
    > + }
    > +
    > + if (!dentry->d_inode) {
    > + __dput_single(dentry);
    > + goto loop;
    > + }
    > +
    > + if (!S_ISDIR(dentry->d_inode->i_mode)) {
    > + __dput_single(dentry);
    > + err = 0;
    > + goto out;
    > + }
    > +
    > + /* Now we know, we found something real */
    > + follow_mount(&nd.mnt, &dentry);
    > +
    > + do {
    > + struct vfsmount *mnt = find_mnt(dentry);
    > + UM_DEBUG_UID("name=\"%s\", inode=%p, device=%s\n",
    > + dentry->d_name.name, dentry->d_inode,
    > + mnt->mnt_devname);
    > + mntput(mnt);
    > + } while (0);
    > +
    > + if (last->d_overlaid && (last->d_overlaid != dentry)) {
    > + printk(KERN_ERR "%s: strange stack layout " \
    > + "(\"%s\" overlays \"%s\")\n",
    > + __FUNCTION__, last->d_name.name,
    > + dentry->d_name.name);
    > + dump_stack();
    > + __dput_single(dentry);
    > + /* lets try to make a clean ending */
    > + last->d_overlaid = NULL;
    > + err = -EFAULT; // FIXME: something better?
    > + goto out;
    > + }
    > +
    > + spin_lock(&topmost->d_lock);
    > + if (!topmost->d_union) {
    > + UM_DEBUG_LOCK("allocate union for \"%s\"\n",
    > + topmost->d_name.name);
    > + topmost->d_union = union_alloc();
    > + }
    > + spin_unlock(&topmost->d_lock);
    > +
    > + spin_lock(&dentry->d_lock);
    > + if (!dentry->d_union)
    > + dentry->d_union = union_get(topmost->d_union);
    > + spin_unlock(&dentry->d_lock);
    > +
    > + if (topmost->d_union != dentry->d_union) {
    > + union_lock(dentry);
    > + replace_union_info(topmost, dentry->d_union);
    > + }
    > +
    > + dentry->d_topmost = topmost;
    > + last->d_overlaid = dentry;
    > + last = dentry;
    > + loop:
    > + __dput(nd.dentry);
    > + mntput(nd.mnt);
    > + parent = parent->d_overlaid;
    > + }
    > +
    > + err = 0;
    > + union_unlock(topmost);
    > + return err;
    > +out:
    > + __dput(nd.dentry);
    > + mntput(nd.mnt);
    > + union_unlock(topmost);
    > + return err;
    > +}
    > +
    > +/*
    > + * Union mount version of real_lookup().
    > + * Looks through the lower layers of the union and builds a union stack
    > + * if necessary (i,e., for directories)
    > + * TODO: This routine is almost similar to __lookup_hash_kern_union() which
    > + * uses __lookup_hash_kern_single() (instead of real_lookup_single()). Check
    > + * if some code can be shared here.
    > + */
    > +struct dentry * real_lookup_union(struct dentry *base, struct qstr *name,
    > + struct nameidata *__nd)
    > +{
    > + struct dentry *parent;
    > + struct dentry *topmost;
    > + unsigned int hash = name->hash;
    > + struct nameidata nd;
    > + int err;
    > +
    > + union_lock(base);
    > + topmost = real_lookup_single(base, name, __nd);
    > + if (IS_ERR(topmost))
    > + goto out;
    > +
    > + /*
    > + * If real_lookup returns a valid dentry from the topmost layer,
    > + * continue the lookup into the lower layers and build a union
    > + * stack in case of directories.
    > + */
    > + if (topmost->d_inode) {
    > + parent = base;
    > + goto lookup_union;
    > + }
    > +
    > + copy_nd(__nd, &nd);
    > +
    > + /*
    > + * real_lookup returned a negative dentry, walk through the lower
    > + * layers looking for the given name.
    > + */
    > + parent = base->d_overlaid;
    > + while (parent) {
    > + struct dentry * dentry;
    > +
    > + name->hash = hash;
    > + nd.dentry = __dget(parent);
    > + nd.mnt = find_mnt(parent);
    > +
    > + dentry = real_lookup_single(nd.dentry, name, &nd);
    > + __dput(nd.dentry);
    > + mntput(nd.mnt);
    > + if (IS_ERR(dentry))
    > + goto out;
    > +
    > + /*
    > + * If a dentry is found in a lower layer, continue to lookup
    > + * in the lower layers and build a union stack if needed.
    > + */
    > + if (dentry->d_inode) {
    > + __dput_single(topmost);
    > + topmost = dentry;
    > + goto lookup_union;
    > + }
    > + __dput_single(dentry);
    > + parent = parent->d_overlaid;
    > + }
    > +
    > +out:
    > + union_unlock(base);
    > + return topmost;
    > +
    > +lookup_union:
    > + /*
    > + * If our parent doesn't have a union stack or we are not a directory,
    > + * the lookup ends here.
    > + */
    > + if (!parent->d_overlaid || !S_ISDIR(topmost->d_inode->i_mode))
    > + goto out;
    > +
    > + do {
    > + struct vfsmount *mnt = find_mnt(topmost);
    > + UM_DEBUG_UID("name=\"%s\", inode=%p, device=%s\n",
    > + topmost->d_name.name, topmost->d_inode,
    > + mnt->mnt_devname);
    > + mntput(mnt);
    > + } while (0);
    > +
    > + name->hash = hash;
    > + err = __lookup_union(topmost, name, &nd);
    > + if (err) {
    > + dput(topmost);
    > + topmost = ERR_PTR(err);
    > + }
    > + goto out;
    > +}
    > +
    > +/*
    > + * Union mount version of __lookup_hash_kern().
    > + * Looks through the lower layers of the union and builds a union stack
    > + * if necessary (i,e., for directories)
    > + */
    > +struct dentry * __lookup_hash_kern_union(struct qstr *name,
    > + struct dentry *base, struct nameidata *__nd)
    > +{
    > + struct dentry *topmost;
    > + struct dentry *parent;
    > + unsigned int hash = name->hash;
    > + struct nameidata nd;
    > + int err;
    > +
    > + union_lock(base);
    > + topmost = __lookup_hash_kern_single(name, base, __nd);
    > + if (IS_ERR(topmost))
    > + goto out;
    > +
    > + if (topmost->d_inode) {
    > + parent = base;
    > + goto lookup_union;
    > + }
    > +
    > + copy_nd(__nd, &nd);
    > +
    > + parent = base->d_overlaid;
    > + while (parent) {
    > + struct dentry *dentry;
    > +
    > + name->hash = hash;
    > + nd.dentry = __dget(parent);
    > + nd.mnt = find_mnt(parent);
    > +
    > + mutex_lock(&parent->d_inode->i_mutex);
    > + dentry = __lookup_hash_kern_single(name, nd.dentry, &nd);
    > + mutex_unlock(&parent->d_inode->i_mutex);
    > + __dput(nd.dentry);
    > + mntput(nd.mnt);
    > + if (IS_ERR(dentry))
    > + goto out;
    > +
    > + if (dentry->d_inode) {
    > + __dput_single(topmost);
    > + topmost = dentry;
    > + goto lookup_union;
    > + }
    > + __dput_single(dentry);
    > + parent = parent->d_overlaid;
    > + }
    > +
    > +out:
    > + union_unlock(base);
    > + return topmost;
    > +
    > +lookup_union:
    > + if (!parent->d_overlaid || !S_ISDIR(topmost->d_inode->i_mode))
    > + goto out;
    > +
    > + do {
    > + struct vfsmount *mnt = find_mnt(topmost);
    > + UM_DEBUG_UID("name=\"%s\", inode=%p, device=%s\n",
    > + topmost->d_name.name, topmost->d_inode,
    > + mnt->mnt_devname);
    > + mntput(mnt);
    > + } while (0);
    > +
    > + name->hash = hash;
    > + err = __lookup_union(topmost, name, &nd);
    > + if (err) {
    > + dput(topmost);
    > + topmost = ERR_PTR(err);
    > + }
    > + goto out;
    > +}
    > +
    > +int follow_union_mount(struct vfsmount **mnt, struct dentry **dentry)
    > +{
    > + int res = 0;
    > +
    > + while ((*dentry)->d_topmost) {
    > + struct dentry *d_tmp = dget((*dentry)->d_topmost);
    > + struct vfsmount *m_tmp = find_mnt((*dentry)->d_topmost);
    > +
    > + UM_DEBUG_UID("name=\"%s\", follow union from %s to %s\n",
    > + (*dentry)->d_name.name, (*mnt)->mnt_devname,
    > + m_tmp->mnt_devname);
    > + mntput(*mnt);
    > + *mnt = m_tmp;
    > + dput(*dentry);
    > + *dentry = d_tmp;
    > + res = 1;
    > + }
    > +
    > + return res;
    > +}
    > --- a/include/linux/dcache.h
    > +++ b/include/linux/dcache.h
    > @@ -294,9 +294,23 @@ extern void d_move(struct dentry *, stru
    >
    > /* appendix may either be NULL or be used for transname suffixes */
    > extern struct dentry * d_lookup(struct dentry *, struct qstr *);
    > -extern struct dentry * __d_lookup(struct dentry *, struct qstr *);
    > +extern struct dentry * d_lookup_single(struct dentry *, struct qstr *);
    > +extern struct dentry * __d_lookup_single(struct dentry *, struct qstr *);
    > extern struct dentry * d_hash_and_lookup(struct dentry *, struct qstr *);
    >
    > +#ifdef CONFIG_UNION_MOUNT
    > +extern struct dentry * __d_lookup_union(struct dentry *, struct qstr *);
    > +#endif
    > +
    > +static inline struct dentry * __d_lookup(struct dentry *parent, struct qstr *name)
    > +{
    > +#ifdef CONFIG_UNION_MOUNT
    > + return __d_lookup_union(parent, name);
    > +#else
    > + return __d_lookup_single(parent, name);
    > +#endif
    > +}
    > +
    > /* validate "insecure" dentry pointer */
    > extern int d_validate(struct dentry *, struct dentry *);
    >
    > @@ -467,6 +481,7 @@ static inline int d_mountpoint(struct de
    > extern struct vfsmount *lookup_mnt(struct vfsmount *, struct dentry *);
    > extern struct vfsmount *__lookup_mnt(struct vfsmount *, struct dentry *, int);
    > extern struct dentry *lookup_create(struct nameidata *nd, int is_dir);
    > +extern struct vfsmount *find_mnt(struct dentry *);
    >
    > extern int sysctl_vfs_cache_pressure;
    >
    > --- a/include/linux/namei.h
    > +++ b/include/linux/namei.h
    > @@ -20,6 +20,7 @@ struct nameidata {
    > struct vfsmount *mnt;
    > struct qstr last;
    > unsigned int flags;
    > + unsigned int um_flags;
    > int last_type;
    > unsigned depth;
    > char *saved_names[MAX_NESTED_LINKS + 1];
    > @@ -40,6 +41,9 @@ struct path {
    > */
    > enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
    >
    > +#define LAST_UNION 0x01
    > +#define LAST_LOWLEVEL 0x02
    > +
    > /*
    > * The bitmask for a lookup event:
    > * - follow links at the end
    > --- a/include/linux/union.h
    > +++ b/include/linux/union.h
    > @@ -17,17 +17,66 @@
    > #ifdef CONFIG_UNION_MOUNT
    >
    > #include <linux/fs_struct.h>
    > +#include <linux/dcache_union.h>
    >
    > /* namespace stuff used at mount time */
    > extern void attach_mnt_union(struct vfsmount *, struct nameidata *);
    > extern void detach_mnt_union(struct vfsmount *, struct path *);
    >
    > +/* lookup stuff */
    > +extern int follow_union_mount(struct vfsmount **, struct dentry **);
    > +extern struct dentry * real_lookup_union(struct dentry *, struct qstr *,
    > + struct nameidata *);
    > +extern struct dentry * __lookup_hash_kern_union(struct qstr *, struct dentry *,
    > + struct nameidata *);
    > +
    > #else /* CONFIG_UNION_MOUNT */
    >
    > #define attach_mnt_union(mnt,nd) do { /* empty */ } while (0)
    > #define detach_mnt_union(mnt,nd) do { /* empty */ } while (0)
    > +#define follow_union_mount(x,y) do { /* empty */ } while (0)
    >
    > #endif /* CONFIG_UNION_MOUNT */
    >
    > +extern struct dentry * real_lookup_single(struct dentry *, struct qstr *,
    > + struct nameidata *);
    > +extern struct dentry * __lookup_hash_kern_single(struct qstr *, struct dentry *,
    > + struct nameidata *);
    > +
    > +static inline struct dentry * real_lookup(struct dentry *parent, struct qstr *name, struct nameidata *nd)
    > +{
    > +#ifdef CONFIG_UNION_MOUNT
    > + return real_lookup_union(parent, name, nd);
    > +#else
    > + return real_lookup_single(parent, name, nd);
    > +#endif
    > +}
    > +
    > +static inline struct dentry * __lookup_hash_kern(struct qstr *name, struct dentry *base, struct nameidata *nd)
    > +{
    > +#ifdef CONFIG_UNION_MOUNT
    > + return __lookup_hash_kern_union(name, base, nd);
    > +#else
    > + return __lookup_hash_kern_single(name, base, nd);
    > +#endif
    > +}
    > +
    > +static inline struct dentry * __lookup_hash_single(struct qstr *name, struct dentry *base, struct nameidata *nd)
    > +{
    > + struct dentry *dentry;
    > + struct inode *inode;
    > + int err;
    > +
    > + inode = base->d_inode;
    > +
    > + err = permission(inode, MAY_EXEC, nd);
    > + dentry = ERR_PTR(err);
    > + if (err)
    > + goto out;
    > +
    > + dentry = __lookup_hash_kern_single(name, base, nd);
    > +out:
    > + return dentry;
    > +}
    > #endif /* __KERNEL __ */
    > #endif /* __LINUX_UNION_H */
    > -
    > To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
    > the body of a message to majordomo@vger.kernel.org
    > More majordomo info at http://vger.kernel.org/majordomo-info.html

    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2007-05-15 16:03    [W:5.259 / U:0.228 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site