diff mbox

[2/3] ext4: Implement subtree ID support for ext4 filesystem

Message ID 1341847725-26114-3-git-send-email-dmonakhov@openvz.org
State Accepted, archived
Headers show

Commit Message

Dmitry Monakhov July 9, 2012, 3:28 p.m. UTC
* Abstract
  A subtree of a directory tree T is a tree consisting of a directory
  (the subtree root) in T and all of its descendants in T.

  *NOTE*: User is allowed to break pure subtree hierarchy via manual
          id manipulation.

  Subtree subtrees assumptions:
  (1) Each inode has an id. This id is persistently stored inside
      inode (xattr, usually inside ibody)
  (2) Subtree id is inherent from parent directory

  This feature is similar to project-id in XFS. One may assign some id to
  a subtree. Each entry from the subtree may be accounted in directory
  subtree quota. Will appear in later patches.

* Disk layout
  Subtree id is stored on disk inside xattr usually inside ibody.
  Xattr is used only as a data storage, It has not user visible xattr
  interface.

* User interface
  Subtree id is accessible via generic xattr interface "system.subtree"

* Notes
  ext4_setattr interface to subtreeid: Semantically subtree id must being changed
  similar to uid/gid, but subtree id is stored inside xattr so on-disk
  structures updates is not that trivial, so I've move subtree change
  logic to separate function.

Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
---
 fs/ext4/Kconfig   |   12 +++
 fs/ext4/Makefile  |    1 +
 fs/ext4/ext4.h    |    3 +
 fs/ext4/ialloc.c  |    6 ++
 fs/ext4/inode.c   |    4 +
 fs/ext4/subtree.c |  215 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/subtree.h |   45 +++++++++++
 fs/ext4/super.c   |    7 ++
 fs/ext4/xattr.c   |    7 ++
 fs/ext4/xattr.h   |    2 +
 10 files changed, 302 insertions(+), 0 deletions(-)
 create mode 100644 fs/ext4/subtree.c
 create mode 100644 fs/ext4/subtree.h

Comments

Theodore Ts'o July 9, 2012, 9:04 p.m. UTC | #1
On Mon, Jul 09, 2012 at 07:28:44PM +0400, Dmitry Monakhov wrote:
> * Abstract
>   A subtree of a directory tree T is a tree consisting of a directory
>   (the subtree root) in T and all of its descendants in T.
> 
>   *NOTE*: User is allowed to break pure subtree hierarchy via manual
>           id manipulation.
> 
>   Subtree subtrees assumptions:
>   (1) Each inode has an id. This id is persistently stored inside
>       inode (xattr, usually inside ibody)
>   (2) Subtree id is inherent from parent directory
                      ^^^^^^^^ inherited

What really bothers me about this patch is that the abstraction is
extremely leaky.  In particular, it's not just "manual id
manipulation" that will break the abstraction.  If you rename a file
or directory across subtrees, it breaks the abstraction; so does hard
links.

When you get right down to it, this is effectively a secondary group
id, except it's not used for access control, but rather for quota
tracking.  You've used the name "subtree" id, but in fact there's no
guarantee subtrees has anything to do with it.  With a few renames,
any semblance of a subtree organization seems to disappear very
easily.

Another question which gets raised is is allowed to change the project
ownership?  Maybe I'm missing something, but I don't see any access
checking, so today it seems the answer is "anybody".  We could change
it so that only a root process can change project ownership, that
could raise other problems.

I also worry that this feature will have very limited applicability.
Will anyone other than parallels use it?

					- Ted
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Dmitry Monakhov July 11, 2012, 12:59 p.m. UTC | #2
On Mon, 9 Jul 2012 17:04:39 -0400, Ted Ts'o <tytso@mit.edu> wrote:
> On Mon, Jul 09, 2012 at 07:28:44PM +0400, Dmitry Monakhov wrote:
> > * Abstract
> >   A subtree of a directory tree T is a tree consisting of a directory
> >   (the subtree root) in T and all of its descendants in T.
> > 
> >   *NOTE*: User is allowed to break pure subtree hierarchy via manual
> >           id manipulation.
> > 
> >   Subtree subtrees assumptions:
> >   (1) Each inode has an id. This id is persistently stored inside
> >       inode (xattr, usually inside ibody)
> >   (2) Subtree id is inherent from parent directory
>                       ^^^^^^^^ inherited
> 
> What really bothers me about this patch is that the abstraction is
> extremely leaky.  In particular, it's not just "manual id
> manipulation" that will break the abstraction.  If you rename a file
> or directory across subtrees, it breaks the abstraction; so does hard
> links.
Yes this is my bad, bad name was chosen. When people hear about subtree they
do expect to see a true subtree (ADG). But the feature i want to add is
not about true subtree hierarchy, this is just an 3'rd inode's identifier
similar to uid/gid, subtree hierarchy is just one of most obvious
use-case. I just want to pick the best name for the feature

May be it would be better if i describe feature as "Namespace ID"
namespaces is well known abstraction in kernel, so misunderstanding
shouldn't happen.

Updated feature description:
1) Add XID (extension ID) the 3'rd inode's identifier similar to UID/GID
2) XID is stored inside xattr
3) XID is obtained from current task from current->cred->xid
4) XID is initialized on clone() according to namespace->xid

Obviously one can understand xid as "chroot id", "container id", or
"process-set id"
What do you think about that description?
> 
> When you get right down to it, this is effectively a secondary group
> id, except it's not used for access control, but rather for quota
> tracking.  You've used the name "subtree" id, but in fact there's no
> guarantee subtrees has anything to do with it.  With a few renames,
> any semblance of a subtree organization seems to disappear very
> easily.
> 
> Another question which gets raised is is allowed to change the project
> ownership?  Maybe I'm missing something, but I don't see any access
> checking, so today it seems the answer is "anybody".  We could change
> it so that only a root process can change project ownership, that
> could raise other problems.
Definitely, this should be restricted to CAP_SYS_ADMIN
> 
> I also worry that this feature will have very limited applicability.
> Will anyone other than parallels use it?
Off course no, this is very useful feature, but seems no one know
about this yet :). Third quota identifier should be usefully in following
cases:
1) Various containers implementation XLR and others
2) Chroot environments. For example I have an Android chroot environment
   and i want to prevent it from eat all space on my disk.
3) NFS: Per-mount quota. Server administrator is able to assign global
   disk limit for single nfs-share w/o limiting uid/gid quotas
> 
> 					- Ted
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" 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-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Theodore Ts'o July 11, 2012, 1:24 p.m. UTC | #3
On Wed, Jul 11, 2012 at 04:59:24PM +0400, Dmitry Monakhov wrote:
> 
> May be it would be better if i describe feature as "Namespace ID"
> namespaces is well known abstraction in kernel, so misunderstanding
> shouldn't happen.

What if we call it a "quota group", with the rules that if a parent
directory has a quota group, any files or directories created in that
parent directory will inherit that quota group, and only processes
with CAP_SYS_ADMIN can change it.

And then what if we simply make the rule that if an inode has a quota
group, the quota is charged against two group id's; the group id named
in inode, and the quota group?

That is, do we really need to have a separate namespace for group ids
and "subtrees" or "namespaces"?  That means we don't have to change
the userspace quota tools and we can leverage the existing ways people
are used to managing group quotas.

						- Ted
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Dmitry Monakhov July 11, 2012, 3:26 p.m. UTC | #4
On Wed, 11 Jul 2012 09:24:29 -0400, Theodore Ts'o <tytso@mit.edu> wrote:
> On Wed, Jul 11, 2012 at 04:59:24PM +0400, Dmitry Monakhov wrote:
> > 
> > May be it would be better if i describe feature as "Namespace ID"
> > namespaces is well known abstraction in kernel, so misunderstanding
> > shouldn't happen.
> 
> What if we call it a "quota group", with the rules that if a parent
> directory has a quota group, any files or directories created in that
> parent directory will inherit that quota group, and only processes
> with CAP_SYS_ADMIN can change it.
> 
> And then what if we simply make the rule that if an inode has a quota
> group, the quota is charged against two group id's; the group id named
> in inode, and the quota group?
Yes this looks reasonable, the only thing that we should aware of is id
collision. We have to reserve pool numbers to quota group id's.
This can be easily solved by changing disk structure of quota-file
to use u64 on uid.
> 
> That is, do we really need to have a separate namespace for group ids
> and "subtrees" or "namespaces"?  That means we don't have to change
> the userspace quota tools and we can leverage the existing ways people
> are used to managing group quotas.
> 
> 						- Ted
> --
P.S. You probably heard about tragedy in Krimsk due to water flood.
Tonight i'll go Krimsk to help as a volunteer, so i probably will be
out of Internet next 10-12days. Please excuse me, i'll send updated
version right after i'll back to Moscow.
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" 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-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Theodore Ts'o July 11, 2012, 5:17 p.m. UTC | #5
On Wed, Jul 11, 2012 at 07:26:25PM +0400, Dmitry Monakhov wrote:
> Yes this looks reasonable, the only thing that we should aware of is id
> collision. We have to reserve pool numbers to quota group id's.
> This can be easily solved by changing disk structure of quota-file
> to use u64 on uid.

How many pool id's are you anticipating?  Is 32-bits really not
enough?  I'll note that uid_t and gid_t is only 32-bits today, and
part of my goal is to try to preserve compatibility with the existing
quota infrastructure.

							- Ted
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index c22f170..f571168 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -77,6 +77,18 @@  config EXT4_FS_SECURITY
 	  If you are not using a security module that requires using
 	  extended attributes for file security labels, say N.
 
+config EXT4_SUBTREE
+	bool "Ext4 subtree id support"
+	select SUBTREE
+	depends on EXT4_FS_XATTR
+	help
+	  Enables subtree inode identifier support for ext4 filesystem.
+	  This feature allow to assign extended inode's identifier similar to
+	  uid/gid. Value is stored in xattr "system.subtree" and may be used
+	  as additional quota limit.
+
+	  If unsure, say N.
+
 config EXT4_DEBUG
 	bool "EXT4 debugging support"
 	depends on EXT4_FS
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 56fd8f8..df0a54c 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -12,3 +12,4 @@  ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
 ext4-$(CONFIG_EXT4_FS_XATTR)		+= xattr.o xattr_user.o xattr_trusted.o
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
+ext4-$(CONFIG_EXT4_SUBTREE)		+= subtree.o
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index cfc4e01..f831210 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -925,6 +925,9 @@  struct ext4_inode_info {
 
 	/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
 	__u32 i_csum_seed;
+#ifdef CONFIG_EXT4_SUBTREE
+	__u32 i_subtree;
+#endif
 };
 
 /*
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index d48e8b1..ff73fea 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -28,6 +28,7 @@ 
 #include "ext4_jbd2.h"
 #include "xattr.h"
 #include "acl.h"
+#include "subtree.h"
 
 #include <trace/events/ext4.h>
 
@@ -898,6 +899,8 @@  got:
 
 	ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
 
+	ext4_set_subtree(inode, ext4_get_subtree(dir));
+
 	ret = inode;
 	dquot_initialize(inode);
 	err = dquot_alloc_inode(inode);
@@ -911,6 +914,9 @@  got:
 	err = ext4_init_security(handle, inode, dir, qstr);
 	if (err)
 		goto fail_free_drop;
+	err = ext4_subtree_init(handle, inode);
+	if (err)
+		goto fail_free_drop;
 
 	if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
 		/* set extent flag only for directory, file and normal symlink*/
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 02bc8cb..caf72f8 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -42,6 +42,7 @@ 
 #include "xattr.h"
 #include "acl.h"
 #include "truncate.h"
+#include "subtree.h"
 
 #include <trace/events/ext4.h>
 
@@ -3870,6 +3871,9 @@  struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 	}
 	if (ret)
 		goto bad_inode;
+	ret = ext4_subtree_read(inode);
+	if (ret)
+		goto bad_inode;
 
 	if (S_ISREG(inode->i_mode)) {
 		inode->i_op = &ext4_file_inode_operations;
diff --git a/fs/ext4/subtree.c b/fs/ext4/subtree.c
new file mode 100644
index 0000000..0486740
--- /dev/null
+++ b/fs/ext4/subtree.c
@@ -0,0 +1,215 @@ 
+/*
+ * linux/fs/ext4/subtree.c
+ *
+ * Copyright (C) 2012 Parallels Inc
+ * Dmitry Monakhov <dmonakhov@openvz.org>
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/capability.h>
+#include <linux/fs.h>
+#include <linux/quotaops.h>
+#include "ext4_jbd2.h"
+#include "ext4.h"
+#include "xattr.h"
+#include "subtree.h"
+
+/*
+ * Subtree assumptions:
+ * (1) Each inode has subtree id. This id is persistently stored inside
+ *     inode's xattr, usually inside ibody
+ * (2) Subtree id is inherent from parent directory
+ */
+
+/*
+ * Read subtree id from inode's xattr
+ * Locking: none
+ */
+int ext4_subtree_xattr_read(struct inode *inode, unsigned int *subtree)
+{
+	__le32 dsk_subtree;
+	int retval;
+
+	retval = ext4_xattr_get(inode, EXT4_XATTR_INDEX_SUBTREE, "",
+				&dsk_subtree, sizeof(dsk_subtree));
+	if (retval > 0) {
+		if (retval != sizeof(dsk_subtree))
+			return -EIO;
+		else
+			retval = 0;
+	}
+	*subtree = le32_to_cpu(dsk_subtree);
+	return retval;
+}
+
+/*
+ * Save subtree id to inode's xattr
+ * Locking: none
+ */
+int ext4_subtree_xattr_write(handle_t *handle, struct inode *inode,
+				unsigned int subtree, int xflags)
+{
+	__le32 dskid = cpu_to_le32(subtree);
+	int retval;
+
+	retval = ext4_xattr_set_handle(handle,
+				       inode, EXT4_XATTR_INDEX_SUBTREE, "",
+				       &dskid, sizeof(dskid), xflags);
+	if (retval > 0) {
+		if (retval != sizeof(dskid))
+			retval =  -EIO;
+		else
+			retval = 0;
+	}
+	return retval;
+}
+
+/*
+ * Change subtree id.
+ * Called under inode->i_mutex
+ */
+int ext4_subtree_change(struct inode *inode, unsigned int new_subtree)
+{
+	/*
+	 * One data_trans_blocks chunk for xattr update.
+	 * One quota_trans_blocks chunk for quota transfer, and one
+	 * quota_trans_block chunk for emergency quota rollback transfer,
+	 * because quota rollback may result new quota blocks allocation.
+	 */
+	unsigned credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb) +
+		EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb) * 2;
+	int ret, ret2 = 0;
+	unsigned retries = 0;
+	handle_t *handle;
+	struct dquot *dquot[MAXQUOTAS] = {};
+	int old_id = ext4_get_subtree(inode);
+
+	dquot_initialize(inode);
+	dquot[SBTRQUOTA] = dqget(inode->i_sb, new_subtree, SBTRQUOTA);
+retry:
+	handle = ext4_journal_start(inode, credits);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		ext4_std_error(inode->i_sb, ret);
+		goto out;
+	}
+	/* Inode may not have subtree xattr yet. Create it explicitly */
+	ret = ext4_subtree_xattr_write(handle, inode, old_id, XATTR_CREATE);
+	if (ret == -EEXIST)
+		ret = 0;
+	if (ret) {
+		ret2 = ext4_journal_stop(handle);
+		if (ret2)
+			ret = ret2;
+		if (ret == -ENOSPC &&
+			ext4_should_retry_alloc(inode->i_sb, &retries))
+			goto retry;
+	}
+#ifdef CONFIG_QUOTA
+	ret = __dquot_transfer(inode, dquot);
+	if (ret)
+		return ret;
+#endif
+	ret = ext4_subtree_xattr_write(handle, inode, new_subtree,
+				       XATTR_REPLACE);
+	if (ret) {
+		/*
+		 * Function may fail only due to fatal error, Nor than less
+		 * we have try to rollback quota changes.
+		 */
+#ifdef CONFIG_QUOTA
+		__dquot_transfer(inode, dquot);
+#endif
+		ext4_std_error(inode->i_sb, ret);
+
+	} else
+		ext4_set_subtree(inode, new_subtree);
+
+	ret2 = ext4_journal_stop(handle);
+out:
+	dqput(dquot[SBTRQUOTA]);
+	if (ret2)
+		ret = ret2;
+	return ret;
+}
+
+int ext4_subtree_read(struct inode *inode)
+{
+	int ret = 0;
+	int subtree = 0;
+
+	ret = ext4_subtree_xattr_read(inode, &subtree);
+	if (ret == -ENODATA) {
+		subtree = 0;
+		ret = 0;
+	}
+	if (!ret)
+		ext4_set_subtree(inode, subtree);
+	return ret;
+}
+
+/*
+ * Initialize the subtree xattr of a new inode. Called from ext4_new_inode.
+ *
+ * dir->i_mutex: down
+ * inode->i_mutex: up (access to inode is still exclusive)
+ * Note: caller must assign correct subtree id to inode before.
+ */
+int ext4_subtree_init(handle_t *handle, struct inode *inode)
+{
+	return ext4_subtree_xattr_write(handle, inode, EXT4_I(inode)->i_subtree,
+				XATTR_CREATE);
+}
+
+static size_t
+ext4_xattr_subtree_list(struct dentry *dentry, char *list, size_t list_size,
+		const char *name, size_t name_len, int type)
+{
+	if (list && XATTR_SUBTREE_LEN <= list_size)
+		memcpy(list, XATTR_SUBTREE, XATTR_SUBTREE_LEN);
+	return XATTR_SUBTREE_LEN;
+}
+
+static int
+ext4_xattr_subtree_get(struct dentry *dentry, const char *name,
+		       void *buffer, size_t size, int type)
+{
+	int ret;
+	unsigned subtree;
+	char buf[32];
+
+	if (strcmp(name, "") != 0)
+		return -EINVAL;
+	ret = ext4_subtree_xattr_read(dentry->d_inode, &subtree);
+	if (ret)
+		return ret;
+	snprintf(buf, sizeof(buf)-1, "%u", subtree);
+	buf[31] = '\0';
+	strncpy(buffer, buf, size);
+	return strlen(buf);
+}
+
+static int
+ext4_xattr_subtree_set(struct dentry *dentry, const char *name,
+		const void *value, size_t size, int flags, int type)
+{
+	unsigned int new_subtree;
+	int ret = 0;
+	char buf[11];
+	if (strcmp(name, "") != 0 || size + 1 > sizeof(buf))
+		return -EINVAL;
+	memcpy(buf, (char *)value, size);
+	buf[size] = '\0';
+	if (kstrtouint(buf, 10, &new_subtree))
+		return -EINVAL;
+	return ext4_subtree_change(dentry->d_inode, new_subtree);
+}
+
+const struct xattr_handler ext4_xattr_subtree_handler = {
+	.prefix	= XATTR_SUBTREE,
+	.list	= ext4_xattr_subtree_list,
+	.get	= ext4_xattr_subtree_get,
+	.set	= ext4_xattr_subtree_set,
+};
diff --git a/fs/ext4/subtree.h b/fs/ext4/subtree.h
new file mode 100644
index 0000000..362de80
--- /dev/null
+++ b/fs/ext4/subtree.h
@@ -0,0 +1,45 @@ 
+#include <linux/xattr.h>
+#include <linux/fs.h>
+
+#ifdef CONFIG_EXT4_SUBTREE
+extern int ext4_subtree_xattr_read(struct inode *inode, unsigned int *subtree);
+extern int ext4_subtree_xattr_write(handle_t *handle, struct inode *inode,
+				unsigned int subtree, int xflags);
+extern int ext4_subtree_init(handle_t *handle, struct inode *inode);
+extern int ext4_subtree_read(struct inode *inode);
+extern int ext4_subtree_change(struct inode *inode, unsigned int new_subtree);
+static inline u32 ext4_get_subtree(const struct inode *inode)
+{
+	const struct ext4_inode_info *ei =
+		container_of(inode, const struct ext4_inode_info, vfs_inode);
+	return ei->i_subtree;
+}
+static inline void ext4_set_subtree(struct inode *inode, u32 id)
+{
+	EXT4_I(inode)->i_subtree = id;
+}
+#else
+#define ext4_get_subtree(inode) do {} while (0)
+#define ext4_set_subtree(inode, id) do {} while (0)
+static inline int ext4_subtree_xattr_read(struct inode *inode, unsigned int *id)
+{
+	return -ENOTSUPP;
+}
+static inline int ext4_subtree_xattr_write(handle_t *h, struct inode *inode,
+				unsigned int subtree, int xflags)
+{
+	return -ENOTSUPP;
+}
+static inline int ext4_subtree_read(struct inode *inode)
+{
+	return 0;
+}
+static inline int ext4_subtree_change(struct inode *inode, unsigned int id)
+{
+	return -ENOTSUPP;
+}
+static inline int ext4_subtree_init(handle_t *handle, struct inode *inode)
+{
+	return 0;
+}
+#endif
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 84c7ba4..3599c95 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -50,6 +50,7 @@ 
 #include "xattr.h"
 #include "acl.h"
 #include "mballoc.h"
+#include "subtree.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/ext4.h>
@@ -1185,6 +1186,9 @@  static const struct super_operations ext4_sops = {
 	.quota_write	= ext4_quota_write,
 #endif
 	.bdev_try_to_free_page = bdev_try_to_free_page,
+#ifdef CONFIG_EXT4_SUBTREE
+	.get_subtree      = ext4_get_subtree,
+#endif
 };
 
 static const struct super_operations ext4_nojournal_sops = {
@@ -1204,6 +1208,9 @@  static const struct super_operations ext4_nojournal_sops = {
 	.quota_write	= ext4_quota_write,
 #endif
 	.bdev_try_to_free_page = bdev_try_to_free_page,
+#ifdef CONFIG_EXT4_SUBTREE
+	.get_subtree      = ext4_get_subtree,
+#endif
 };
 
 static const struct export_operations ext4_export_ops = {
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index e56c9ed..7466544 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -107,6 +107,10 @@  static const struct xattr_handler *ext4_xattr_handler_map[] = {
 #ifdef CONFIG_EXT4_FS_SECURITY
 	[EXT4_XATTR_INDEX_SECURITY]	     = &ext4_xattr_security_handler,
 #endif
+#ifdef CONFIG_EXT4_SUBTREE
+	[EXT4_XATTR_INDEX_SUBTREE]	     = &ext4_xattr_subtree_handler,
+#endif
+
 };
 
 const struct xattr_handler *ext4_xattr_handlers[] = {
@@ -119,6 +123,9 @@  const struct xattr_handler *ext4_xattr_handlers[] = {
 #ifdef CONFIG_EXT4_FS_SECURITY
 	&ext4_xattr_security_handler,
 #endif
+#ifdef CONFIG_EXT4_SUBTREE
+	&ext4_xattr_subtree_handler,
+#endif
 	NULL
 };
 
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 91f31ca..b207f35 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -21,6 +21,7 @@ 
 #define EXT4_XATTR_INDEX_TRUSTED		4
 #define	EXT4_XATTR_INDEX_LUSTRE			5
 #define EXT4_XATTR_INDEX_SECURITY	        6
+#define EXT4_XATTR_INDEX_SUBTREE	        7
 
 struct ext4_xattr_header {
 	__le32	h_magic;	/* magic number for identification */
@@ -72,6 +73,7 @@  extern const struct xattr_handler ext4_xattr_trusted_handler;
 extern const struct xattr_handler ext4_xattr_acl_access_handler;
 extern const struct xattr_handler ext4_xattr_acl_default_handler;
 extern const struct xattr_handler ext4_xattr_security_handler;
+extern const struct xattr_handler ext4_xattr_subtree_handler;
 
 extern ssize_t ext4_listxattr(struct dentry *, char *, size_t);