> +out_dput:
> + dput(dentry);
> +out:
> + return err;
> +}
> +
> +/**
> + * union_copyup_dir - copy up low-level directory entries to topmost dir
> + *
> + * readdir() is difficult to support on union file systems for two
> + * reasons: We must eliminate duplicates and apply whiteouts, and we
> + * must return something in f_pos that lets us restart in the same
> + * place when we return. Our solution is to, on first readdir() of
> + * the directory, copy up all visible entries from the low-level file
> + * systems and mark the entries that refer to low-level file system
> + * objects as "fallthru" entries.
> + *
> + * Locking strategy: We hold the topmost dir's i_mutex on entry. We
> + * grab the i_mutex on lower directories one by one. So the locking
> + * order is:
> + *
> + * Writable/topmost layers > Read-only/lower layers
> + *
> + * So there is no problem with lock ordering for union stacks with
> + * multiple lower layers. E.g.:
> + *
> + * (topmost) A->B->C (bottom)
> + * (topmost) D->C->B (bottom)
> + *
> + * (Not that we support more than two layers at the moment.)
> + */
> +
> +int union_copyup_dir(struct path *topmost_path)
> +{
> + struct dentry *topmost_dentry = topmost_path->dentry;
> + struct union_dir *ud;
> + int res = 0;
> +
> + BUG_ON(IS_OPAQUE(topmost_dentry->d_inode));
> +
> + res = mnt_want_write(topmost_path->mnt);
> + if (res)
> + return res;
> + /*
> + * Mark this dir opaque to show that we have already copied up
> + * the lower entries. Only fallthru entries pass through to
> + * the underlying file system.
> + */
> + topmost_dentry->d_inode->i_flags |= S_OPAQUE;
> + mark_inode_dirty(topmost_dentry->d_inode);
> +
> + for (ud = topmost_path->dentry->d_union_dir; ud != NULL; ud = ud->u_lower) {
> + struct file * ftmp;
> + struct inode * inode;
> + struct path path;
> +
> + BUG_ON(ud->u_this.dentry->d_count.counter == 0);
> + path = ud->u_this;
> + /* dentry_open() doesn't get a path reference itself */
> + path_get(&path);
> + ftmp = dentry_open(path.dentry, path.mnt,
> + O_RDONLY | O_DIRECTORY | O_NOATIME,
> + current_cred());
> + if (IS_ERR(ftmp)) {
> + printk (KERN_ERR "unable to open dir %s for "
> + "directory copyup: %ld\n",
> + path.dentry->d_name.name, PTR_ERR(ftmp));
> + path_put(&path);
> + continue;
> + }
> +
> + inode = path.dentry->d_inode;
> + mutex_lock(&inode->i_mutex);
> +
> + res = -ENOENT;
> + if (IS_DEADDIR(inode))
> + goto out_fput;
> + /*
> + * Read the whole directory, calling our directory
> + * entry copyup function on each entry. Pass in the
> + * topmost dentry as our private data so we can create
> + * new entries in the topmost directory.
> + */
> + res = ftmp->f_op->readdir(ftmp, topmost_dentry,
> + union_copyup_dir_one);
> +out_fput:
> + mutex_unlock(&inode->i_mutex);
> + fput(ftmp);
> +
> + if (res)
> + break;
> +
> + /* XXX Should process directories below an opaque
> + * directory in case there are fallthrus in it */
> + if (IS_OPAQUE(path.dentry->d_inode))
> + break;
> +
> + }
> + mnt_drop_write(topmost_path->mnt);
> + return res;
> +}
> diff --git a/fs/union.h b/fs/union.h
> index 505f132..80c2421 100644
> --- a/fs/union.h
> +++ b/fs/union.h
> @@ -58,6 +58,7 @@ extern void d_free_unions(struct dentry *);
> int needs_lookup_union(struct path *, struct path *);
> int union_create_topmost_dir(struct path *, struct qstr *, struct path *,
> struct path *);
> +extern int union_copyup_dir(struct path *);
>
> #else /* CONFIG_UNION_MOUNT */
>
> @@ -67,6 +68,7 @@ int union_create_topmost_dir(struct path *, struct qstr *, struct path *,
> #define d_free_unions(x) do { } while (0)
> #define needs_lookup_union(x, y) ({ (0); })
> #define union_create_topmost_dir(w, x, y, z) ({ BUG(); (NULL); })
> +#define union_copyup_dir(x) ({ BUG(); (0); })
>
> #endif /* CONFIG_UNION_MOUNT */
> #endif /* __KERNEL__ */
> --
> 1.6.3.3
>
> --
> 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/