Viewing: ext4-projid-xattrs.patch

commit 665383d3a1f4d1dc7f404301039432271ad85eaf
Author:     Li Dongyang <dongyangli@ddn.com>
AuthorDate: Tue Nov 30 12:13:03 2021 +1100
LU-12056 ldiskfs: add trusted.projid virtual xattr

Add trusted.projid virtual xattr in ldiskfs to export the
current project id, intended for ldiskfs level MDT backup.

When the project id is EXT4_DEF_PROJID/0,
the virtual xattr is hidden from listxattr(2).

It's also hidden on lustre client when parent has the
project inherit flag and the same project ID,
to stop mv from setting the virtual xattr on the dest with
the project id from src, which could be different from dest.

getxattr(2) on trusted.projid will report current project id,
setxattr(2) will change curent project id and
removexattr(2) will set project id back to EXT4_DEF_PROJID/0

Both get|setxattr(2) will work even when the virtual xattr is
hidden.

Invalidate client xattr cache for the inode when changing its
project id, so the virtual xattr can get the new value
for next getxattr(2)

Add test cases to verify the virtual projid xattr and backup
restore MDT using tar can now preserve the project id.

Change mds_backup_restore in test framework, to use
tar with --xattrs --xattrs-include='trusted.*'" options.

Signed-off-by: Li Dongyang <dongyangli@ddn.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Yang Sheng <ys@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Change-Id: I29b1aa922ef72d734cdc87125401fa08fb13d4af
Reviewed-on: https://review.whamcloud.com/45679

Index: linux-4.18.0-348.2.1.el8_5/fs/ext4/ext4.h
===================================================================
--- linux-4.18.0-348.2.1.el8_5.orig/fs/ext4/ext4.h
+++ linux-4.18.0-348.2.1.el8_5/fs/ext4/ext4.h
@@ -2768,6 +2768,7 @@ extern int ext4_ind_remove_space(handle_
 				 ext4_lblk_t start, ext4_lblk_t end);
 
 /* ioctl.c */
+extern int ext4_ioctl_setproject(struct inode *, __u32);
 extern long ext4_ioctl(struct file *, unsigned int, unsigned long);
 extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long);
 
Index: linux-4.18.0-348.2.1.el8_5/fs/ext4/ioctl.c
===================================================================
--- linux-4.18.0-348.2.1.el8_5.orig/fs/ext4/ioctl.c
+++ linux-4.18.0-348.2.1.el8_5/fs/ext4/ioctl.c
@@ -446,9 +446,8 @@ flags_out:
 }
 
 #ifdef CONFIG_QUOTA
-static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
+int ext4_ioctl_setproject(struct inode *inode, __u32 projid)
 {
-	struct inode *inode = file_inode(filp);
 	struct super_block *sb = inode->i_sb;
 	struct ext4_inode_info *ei = EXT4_I(inode);
 	int err, rc;
@@ -532,7 +531,7 @@ out_stop:
 	return err;
 }
 #else
-static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
+static int ext4_ioctl_setproject(struct inode *inode, __u32 projid)
 {
 	if (projid != EXT4_DEF_PROJID)
 		return -EOPNOTSUPP;
@@ -1184,7 +1183,7 @@ resizefs_out:
 		err = ext4_ioctl_setflags(inode, flags);
 		if (err)
 			goto out;
-		err = ext4_ioctl_setproject(filp, fa.fsx_projid);
+		err = ext4_ioctl_setproject(inode, fa.fsx_projid);
 out:
 		inode_unlock(inode);
 		mnt_drop_write_file(filp);
Index: linux-4.18.0-348.2.1.el8_5/fs/ext4/xattr.c
===================================================================
--- linux-4.18.0-348.2.1.el8_5.orig/fs/ext4/xattr.c
+++ linux-4.18.0-348.2.1.el8_5/fs/ext4/xattr.c
@@ -62,6 +62,9 @@
 #include "xattr.h"
 #include "acl.h"
 
+#define EXT4_XATTR_PROJID "projid"
+#define EXT4_XATTR_INODEFL "inodefl"
+
 #ifdef EXT4_XATTR_DEBUG
 # define ea_idebug(inode, fmt, ...)					\
 	printk(KERN_DEBUG "inode %s:%lu: " fmt "\n",			\
@@ -646,11 +648,42 @@ ext4_xattr_get(struct inode *inode, int
 		return -ERANGE;
 
 	down_read(&EXT4_I(inode)->xattr_sem);
+	if (name_index == EXT4_XATTR_INDEX_TRUSTED) {
+		/* 10 chars to hold u32 in decimal, plus ending \0 */
+		char value[11];
+		int len = strlen(name);
+
+		if (strncmp(name, EXT4_XATTR_PROJID, len) == 0 &&
+		    ext4_has_feature_project(inode->i_sb)) {
+			__u32 projid = (__u32)from_kprojid(&init_user_ns,
+						   EXT4_I(inode)->i_projid);
+			error = snprintf(value, sizeof(value), "%u", projid);
+		} else if (strncmp(name, EXT4_XATTR_INODEFL, len) == 0 &&
+			   (EXT4_I(inode)->i_flags & EXT4_FL_USER_MODIFIABLE)
+									!= 0) {
+			__u32 flags = EXT4_I(inode)->i_flags;
+			error = snprintf(value, sizeof(value), "%#x",
+					 flags & EXT4_FL_USER_MODIFIABLE);
+		} else {
+			goto ibody;
+		}
+		if (buffer) {
+			if (error > buffer_size) {
+				error = -ERANGE;
+				goto out;
+			}
+			memcpy(buffer, value, error);
+		}
+		goto out;
+	}
+
+ibody:
 	error = ext4_xattr_ibody_get(inode, name_index, name, buffer,
 				     buffer_size);
 	if (error == -ENODATA)
 		error = ext4_xattr_block_get(inode, name_index, name, buffer,
 					     buffer_size);
+out:
 	up_read(&EXT4_I(inode)->xattr_sem);
 	return error;
 }
@@ -772,7 +793,52 @@ ext4_listxattr(struct dentry *dentry, ch
 	ret = ext4_xattr_block_list(dentry, buffer, buffer_size);
 	if (ret < 0)
 		goto errout;
+	if (buffer) {
+		buffer += ret;
+		buffer_size -= ret;
+	}
 	ret += ret2;
+	if ((EXT4_I(dentry->d_inode)->i_flags & EXT4_FL_USER_MODIFIABLE) != 0) {
+		size_t prefix_len = strlen(XATTR_TRUSTED_PREFIX);
+		size_t name_len = strlen(EXT4_XATTR_INODEFL);
+		size_t size = prefix_len + name_len + 1;
+
+		if (buffer) {
+			if (size > buffer_size) {
+				ret = -ERANGE;
+				goto errout;
+			}
+			strncpy(buffer, XATTR_TRUSTED_PREFIX, prefix_len);
+			buffer += prefix_len;
+			strncpy(buffer, EXT4_XATTR_INODEFL, name_len);
+			buffer += name_len;
+			*buffer++ = 0;
+			buffer_size -= size;
+		}
+		ret += size;
+	}
+	if (ext4_has_feature_project(dentry->d_sb)) {
+		size_t prefix_len = strlen(XATTR_TRUSTED_PREFIX);
+		size_t name_len = strlen(EXT4_XATTR_PROJID);
+		size_t size = prefix_len + name_len + 1;
+
+		if (__kprojid_val(EXT4_I(dentry->d_inode)->i_projid) ==
+							EXT4_DEF_PROJID)
+			goto errout;
+		if (buffer) {
+			if (size > buffer_size) {
+				ret = -ERANGE;
+				goto errout;
+			}
+			strncpy(buffer, XATTR_TRUSTED_PREFIX, prefix_len);
+			buffer += prefix_len;
+			strncpy(buffer, EXT4_XATTR_PROJID, name_len);
+			buffer += name_len;
+			*buffer++ = 0;
+			buffer_size -= size;
+		}
+		ret += size;
+	}
 errout:
 	up_read(&EXT4_I(d_inode(dentry))->xattr_sem);
 	return ret;
@@ -2464,7 +2511,60 @@ ext4_xattr_set(struct inode *inode, int
 	struct super_block *sb = inode->i_sb;
 	int error, retries = 0;
 	int credits;
+	int len = strlen(name);
 
+	if (name_index == EXT4_XATTR_INDEX_TRUSTED &&
+	    (strncmp(name, EXT4_XATTR_PROJID, len) == 0 ||
+	     strncmp(name, EXT4_XATTR_INODEFL, len) == 0)) {
+		/* 10 chars to hold u32 in decimal, plus ending \0 */
+		char buffer[11];
+		__u32 att_val;
+
+		/*
+		 * Project Quota ID state and inode flags are only allowed to
+		 * change from within the init namespace.
+		 */
+		if (current_user_ns() != &init_user_ns)
+			return -EINVAL;
+
+		if (value && value_len) {
+			if (value_len >= sizeof(buffer))
+				return -EINVAL;
+			memcpy(buffer, value, value_len);
+			buffer[value_len] = '\0';
+			error = kstrtouint(buffer, 0, &att_val);
+			if (error)
+				return error;
+		} else {
+			att_val = 0; /* EXT4_DEF_PROJID */
+		}
+
+		if (strncmp(name, EXT4_XATTR_PROJID, len) == 0 &&
+		    ext4_has_feature_project(inode->i_sb)) {
+			/*
+			 * Caller is allowed to change the project ID. If it is
+			 * being changed, make sure that the new value is valid.
+			 */
+			if (!projid_valid(make_kprojid(&init_user_ns, att_val)))
+				return -EINVAL;
+
+			error = ext4_ioctl_setproject(inode, att_val);
+			return error;
+		} else if (strncmp(name, EXT4_XATTR_INODEFL, len) == 0 &&
+			   (att_val & EXT4_FL_USER_MODIFIABLE) != 0) {
+			handle_t *handle;
+
+			handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
+			if (IS_ERR(handle))
+				error = PTR_ERR(handle);
+			att_val &= EXT4_FL_USER_MODIFIABLE;
+			EXT4_I(inode)->i_flags |= att_val;
+			error = ext4_mark_inode_dirty(handle, inode);
+			ext4_journal_stop(handle);
+			return error;
+		}
+	}
+
 	error = dquot_initialize(inode);
 	if (error)
 		return error;