fs/Makefile | 1
fs/overlayfs/Kconfig | 10
fs/overlayfs/Makefile | 7
- fs/overlayfs/copy_up.c | 385 +++++++++++++++++++++++++++++
+ fs/overlayfs/copy_up.c | 388 +++++++++++++++++++++++++++++
fs/overlayfs/dir.c | 605 ++++++++++++++++++++++++++++++++++++++++++++++
fs/overlayfs/inode.c | 372 ++++++++++++++++++++++++++++
fs/overlayfs/overlayfs.h | 70 +++++
- fs/overlayfs/readdir.c | 566 +++++++++++++++++++++++++++++++++++++++++++
+ fs/overlayfs/readdir.c | 567 +++++++++++++++++++++++++++++++++++++++++++
fs/overlayfs/super.c | 612 +++++++++++++++++++++++++++++++++++++++++++++++
- 10 files changed, 2629 insertions(+)
+ 10 files changed, 2633 insertions(+)
--- a/fs/Kconfig
+++ b/fs/Kconfig
-@@ -70,6 +70,7 @@ source "fs/quota/Kconfig"
+@@ -67,6 +67,7 @@ source "fs/quota/Kconfig"
source "fs/autofs4/Kconfig"
source "fs/fuse/Kconfig"
+source "fs/overlayfs/Kconfig"
- config GENERIC_ACL
- bool
+ menu "Caches"
+
--- a/fs/Makefile
+++ b/fs/Makefile
-@@ -108,6 +108,7 @@ obj-$(CONFIG_QNX6FS_FS) += qnx6/
+@@ -105,6 +105,7 @@ obj-$(CONFIG_QNX6FS_FS) += qnx6/
obj-$(CONFIG_AUTOFS4_FS) += autofs4/
obj-$(CONFIG_ADFS_FS) += adfs/
obj-$(CONFIG_FUSE_FS) += fuse/
+overlayfs-objs := super.o inode.o dir.o readdir.o copy_up.o
--- /dev/null
+++ b/fs/overlayfs/copy_up.c
-@@ -0,0 +1,387 @@
+@@ -0,0 +1,388 @@
+/*
+ *
+ * Copyright (C) 2011 Novell Inc.
+ error = bytes;
+ break;
+ }
++ WARN_ON(old_pos != new_pos);
+
+ len -= bytes;
+ }
+ .ia_mtime = stat->mtime,
+ };
+
-+ return notify_change(upperdentry, &attr);
++ return notify_change(upperdentry, &attr, NULL);
+}
+
+static int ovl_set_mode(struct dentry *upperdentry, umode_t mode)
+ .ia_mode = mode,
+ };
+
-+ return notify_change(upperdentry, &attr);
++ return notify_change(upperdentry, &attr, NULL);
+}
+
+static int ovl_copy_up_locked(struct dentry *upperdir, struct dentry *dentry,
+ if (S_ISDIR(stat->mode))
+ vfs_rmdir(upperdir->d_inode, newpath.dentry);
+ else
-+ vfs_unlink(upperdir->d_inode, newpath.dentry);
++ vfs_unlink(upperdir->d_inode, newpath.dentry, NULL);
+
+ dput(newpath.dentry);
+
+}
--- /dev/null
+++ b/fs/overlayfs/dir.c
-@@ -0,0 +1,605 @@
+@@ -0,0 +1,606 @@
+/*
+ *
+ * Copyright (C) 2011 Novell Inc.
+
+ err = vfs_setxattr(newdentry, ovl_whiteout_xattr, "y", 1, 0);
+ if (err)
-+ vfs_unlink(upperdir->d_inode, newdentry);
++ vfs_unlink(upperdir->d_inode, newdentry, NULL);
+
+out_dput:
+ dput(newdentry);
+
+ err = -EEXIST;
+ if (ovl_is_whiteout(newdentry))
-+ err = vfs_unlink(upperdir->d_inode, newdentry);
++ err = vfs_unlink(upperdir->d_inode, newdentry, NULL);
+
+ revert_creds(old_cred);
+ put_cred(override_cred);
+ if (is_dir)
+ err = vfs_rmdir(upperdir->d_inode, realpath.dentry);
+ else
-+ err = vfs_unlink(upperdir->d_inode, realpath.dentry);
++ err = vfs_unlink(upperdir->d_inode, realpath.dentry,
++ NULL);
+ if (err)
+ goto out_d_drop;
+
+ goto out_unlock;
+
+ olddentry = ovl_dentry_upper(old);
-+ err = vfs_link(olddentry, upperdir->d_inode, newdentry);
++ err = vfs_link(olddentry, upperdir->d_inode, newdentry, NULL);
+ if (!err) {
+ if (WARN_ON(!newdentry->d_inode)) {
+ dput(newdentry);
+ }
+
+ err = vfs_rename(old_upperdir->d_inode, olddentry,
-+ new_upperdir->d_inode, newdentry);
++ new_upperdir->d_inode, newdentry, NULL, 0);
+
+ if (err) {
+ if (new_create && ovl_dentry_is_opaque(new))
+ attr->ia_valid &= ~ATTR_MODE;
+
+ mutex_lock(&upperdentry->d_inode->i_mutex);
-+ err = notify_change(upperdentry, attr);
++ err = notify_change(upperdentry, attr, NULL);
+ if (!err)
+ ovl_copyattr(upperdentry->d_inode, dentry->d_inode);
+ mutex_unlock(&upperdentry->d_inode->i_mutex);
+int ovl_copy_up_truncate(struct dentry *dentry, loff_t size);
--- /dev/null
+++ b/fs/overlayfs/readdir.c
-@@ -0,0 +1,566 @@
+@@ -0,0 +1,567 @@
+/*
+ *
+ * Copyright (C) 2011 Novell Inc.
+};
+
+struct ovl_readdir_data {
++ struct dir_context ctx;
++ bool is_merge;
+ struct rb_root *root;
+ struct list_head *list;
+ struct list_head *middle;
+ return 0;
+}
+
-+static int ovl_fill_lower(void *buf, const char *name, int namelen,
-+ loff_t offset, u64 ino, unsigned int d_type)
++static int ovl_fill_lower(struct ovl_readdir_data *rdd,
++ const char *name, int namelen,
++ loff_t offset, u64 ino, unsigned int d_type)
+{
-+ struct ovl_readdir_data *rdd = buf;
+ struct ovl_cache_entry *p;
+
-+ rdd->count++;
+ p = ovl_cache_entry_find(rdd->root, name, namelen);
+ if (p) {
+ list_move_tail(&p->l_node, rdd->middle);
+ INIT_LIST_HEAD(list);
+}
+
-+static int ovl_fill_upper(void *buf, const char *name, int namelen,
++static int ovl_fill_merge(void *buf, const char *name, int namelen,
+ loff_t offset, u64 ino, unsigned int d_type)
+{
+ struct ovl_readdir_data *rdd = buf;
+
+ rdd->count++;
-+ return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type);
++ if (!rdd->is_merge)
++ return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type);
++ else
++ return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);
+}
+
+static inline int ovl_dir_read(struct path *realpath,
-+ struct ovl_readdir_data *rdd, filldir_t filler)
++ struct ovl_readdir_data *rdd)
+{
+ struct file *realfile;
+ int err;
+ if (IS_ERR(realfile))
+ return PTR_ERR(realfile);
+
++ rdd->ctx.pos = 0;
+ do {
+ rdd->count = 0;
+ rdd->err = 0;
-+ err = vfs_readdir(realfile, filler, rdd);
++ err = iterate_dir(realfile, &rdd->ctx);
+ if (err >= 0)
+ err = rdd->err;
+ } while (!err && rdd->count);
+
+static inline int ovl_dir_read_merged(struct path *upperpath,
+ struct path *lowerpath,
-+ struct ovl_readdir_data *rdd)
++ struct list_head *list)
+{
+ int err;
+ struct rb_root root = RB_ROOT;
+ struct list_head middle;
++ struct ovl_readdir_data rdd = {
++ .ctx.actor = ovl_fill_merge,
++ .list = list,
++ .root = &root,
++ .is_merge = false,
++ };
+
-+ rdd->root = &root;
+ if (upperpath->dentry) {
-+ rdd->dir = upperpath->dentry;
-+ err = ovl_dir_read(upperpath, rdd, ovl_fill_upper);
++ rdd.dir = upperpath->dentry;
++ err = ovl_dir_read(upperpath, &rdd);
+ if (err)
+ goto out;
+
-+ err = ovl_dir_mark_whiteouts(rdd);
++ err = ovl_dir_mark_whiteouts(&rdd);
+ if (err)
+ goto out;
+ }
+ * Insert lowerpath entries before upperpath ones, this allows
+ * offsets to be reasonably constant
+ */
-+ list_add(&middle, rdd->list);
-+ rdd->middle = &middle;
-+ err = ovl_dir_read(lowerpath, rdd, ovl_fill_lower);
++ list_add(&middle, rdd.list);
++ rdd.middle = &middle;
++ rdd.is_merge = true;
++ err = ovl_dir_read(lowerpath, &rdd);
+ list_del(&middle);
+out:
-+ rdd->root = NULL;
-+
+ return err;
+}
+
+ list_move_tail(&od->cursor, l);
+}
+
-+static int ovl_readdir(struct file *file, void *buf, filldir_t filler)
++static int ovl_iterate(struct file *file, struct dir_context *ctx)
+{
+ struct ovl_dir_file *od = file->private_data;
+ int res;
+
-+ if (!file->f_pos)
++ if (!ctx->pos)
+ ovl_dir_reset(file);
+
+ if (od->is_real) {
-+ res = vfs_readdir(od->realfile, filler, buf);
-+ file->f_pos = od->realfile->f_pos;
++ res = iterate_dir(od->realfile, ctx);
+
+ return res;
+ }
+ if (!od->is_cached) {
+ struct path lowerpath;
+ struct path upperpath;
-+ struct ovl_readdir_data rdd = { .list = &od->cache };
+
+ ovl_path_lower(file->f_path.dentry, &lowerpath);
+ ovl_path_upper(file->f_path.dentry, &upperpath);
+
-+ res = ovl_dir_read_merged(&upperpath, &lowerpath, &rdd);
++ res = ovl_dir_read_merged(&upperpath, &lowerpath, &od->cache);
+ if (res) {
-+ ovl_cache_free(rdd.list);
++ ovl_cache_free(&od->cache);
+ return res;
+ }
+
+ od->cache_version = ovl_dentry_version_get(file->f_path.dentry);
+ od->is_cached = true;
+
-+ ovl_seek_cursor(od, file->f_pos);
++ ovl_seek_cursor(od, ctx->pos);
+ }
+
+ while (od->cursor.next != &od->cache) {
-+ int over;
-+ loff_t off;
+ struct ovl_cache_entry *p;
+
+ p = list_entry(od->cursor.next, struct ovl_cache_entry, l_node);
-+ off = file->f_pos;
+ if (!p->is_whiteout) {
-+ over = filler(buf, p->name, p->len, off, p->ino,
-+ p->type);
-+ if (over)
++ if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
+ break;
+ }
-+ file->f_pos++;
++ ctx->pos++;
+ list_move(&od->cursor, &p->l_node);
+ }
+
+const struct file_operations ovl_dir_operations = {
+ .read = generic_read_dir,
+ .open = ovl_dir_open,
-+ .readdir = ovl_readdir,
++ .iterate = ovl_iterate,
+ .llseek = ovl_dir_llseek,
+ .fsync = ovl_dir_fsync,
+ .release = ovl_dir_release,
+ struct path lowerpath;
+ struct path upperpath;
+ struct ovl_cache_entry *p;
-+ struct ovl_readdir_data rdd = { .list = list };
+
+ ovl_path_upper(dentry, &upperpath);
+ ovl_path_lower(dentry, &lowerpath);
+
-+ err = ovl_dir_read_merged(&upperpath, &lowerpath, &rdd);
++ err = ovl_dir_read_merged(&upperpath, &lowerpath, list);
+ if (err)
+ return err;
+
+ p->len, p->name, PTR_ERR(dentry));
+ continue;
+ }
-+ ret = vfs_unlink(upperdir->d_inode, dentry);
++ ret = vfs_unlink(upperdir->d_inode, dentry, NULL);
+ dput(dentry);
+ if (ret)
+ pr_warn(