summaryrefslogtreecommitdiff
path: root/fs/ocfs2/xattr.c
diff options
context:
space:
mode:
authorTao Ma <tao.ma@oracle.com>2009-08-18 11:47:56 +0800
committerJoel Becker <joel.becker@oracle.com>2009-09-22 20:09:49 -0700
commit0fe9b66c65f3ff227da45381afe7612f91e32740 (patch)
tree14d95c15065419825adb4f0f0a26a5416a64259e /fs/ocfs2/xattr.c
parentbc13d347574fc0a8a666bc0f4cc2b635d202e372 (diff)
ocfs2: Add preserve to reflink.
reflink has 2 options for the destination file: 1. snapshot: reflink will attempt to preserve ownership, permissions, and all other security state in order to create a full snapshot. 2. new file: it will acquire the data extent sharing but will see the file's security state and attributes initialized as a new file. So add the option to ocfs2. Signed-off-by: Tao Ma <tao.ma@oracle.com>
Diffstat (limited to 'fs/ocfs2/xattr.c')
-rw-r--r--fs/ocfs2/xattr.c98
1 files changed, 93 insertions, 5 deletions
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 8d1a0abc105c..fe3419068df2 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -56,6 +56,7 @@
#include "super.h"
#include "xattr.h"
#include "refcounttree.h"
+#include "acl.h"
struct ocfs2_xattr_def_value_root {
struct ocfs2_xattr_value_root xv;
@@ -204,6 +205,8 @@ static int ocfs2_get_xattr_tree_value_root(struct super_block *sb,
int offset,
struct ocfs2_xattr_value_root **xv,
struct buffer_head **bh);
+static int ocfs2_xattr_security_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags);
static inline u16 ocfs2_xattr_buckets_per_cluster(struct ocfs2_super *osb)
{
@@ -5994,6 +5997,7 @@ out:
return ret;
}
+typedef int (should_xattr_reflinked)(struct ocfs2_xattr_entry *xe);
/*
* Store the information we need in xattr reflink.
* old_bh and new_bh are inode bh for the old and new inode.
@@ -6006,6 +6010,7 @@ struct ocfs2_xattr_reflink {
struct ocfs2_caching_info *ref_ci;
struct buffer_head *ref_root_bh;
struct ocfs2_cached_dealloc_ctxt *dealloc;
+ should_xattr_reflinked *xattr_reflinked;
};
/*
@@ -6147,6 +6152,9 @@ out:
* NOTE:
* Before we call this function, the caller has memcpy the xattr in
* old_xh to the new_xh.
+ *
+ * If args.xattr_reflinked is set, call it to decide whether the xe should
+ * be reflinked or not. If not, remove it from the new xattr header.
*/
static int ocfs2_reflink_xattr_header(handle_t *handle,
struct ocfs2_xattr_reflink *args,
@@ -6159,10 +6167,10 @@ static int ocfs2_reflink_xattr_header(handle_t *handle,
get_xattr_value_root *func,
void *para)
{
- int ret = 0, i;
+ int ret = 0, i, j;
struct super_block *sb = args->old_inode->i_sb;
struct buffer_head *value_bh;
- struct ocfs2_xattr_entry *xe;
+ struct ocfs2_xattr_entry *xe, *last;
struct ocfs2_xattr_value_root *xv, *new_xv;
struct ocfs2_extent_tree data_et;
u32 clusters, cpos, p_cluster, num_clusters;
@@ -6170,9 +6178,30 @@ static int ocfs2_reflink_xattr_header(handle_t *handle,
mlog(0, "reflink xattr in container %llu, count = %u\n",
(unsigned long long)old_bh->b_blocknr, le16_to_cpu(xh->xh_count));
- for (i = 0; i < le16_to_cpu(xh->xh_count); i++) {
+
+ last = &new_xh->xh_entries[le16_to_cpu(new_xh->xh_count)];
+ for (i = 0, j = 0; i < le16_to_cpu(xh->xh_count); i++, j++) {
xe = &xh->xh_entries[i];
+ if (args->xattr_reflinked && !args->xattr_reflinked(xe)) {
+ xe = &new_xh->xh_entries[j];
+
+ le16_add_cpu(&new_xh->xh_count, -1);
+ if (new_xh->xh_count) {
+ memmove(xe, xe + 1,
+ (void *)last - (void *)xe);
+ memset(last, 0,
+ sizeof(struct ocfs2_xattr_entry));
+ }
+
+ /*
+ * We don't want j to increase in the next round since
+ * it is already moved ahead.
+ */
+ j--;
+ continue;
+ }
+
if (ocfs2_xattr_is_local(xe))
continue;
@@ -6182,7 +6211,7 @@ static int ocfs2_reflink_xattr_header(handle_t *handle,
break;
}
- ret = func(sb, new_bh, new_xh, i, &new_xv, &value_bh, para);
+ ret = func(sb, new_bh, new_xh, j, &new_xv, &value_bh, para);
if (ret) {
mlog_errno(ret);
break;
@@ -6847,10 +6876,20 @@ out:
return ret;
}
+static int ocfs2_reflink_xattr_no_security(struct ocfs2_xattr_entry *xe)
+{
+ int type = ocfs2_xattr_get_type(xe);
+
+ return type != OCFS2_XATTR_INDEX_SECURITY &&
+ type != OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS &&
+ type != OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT;
+}
+
int ocfs2_reflink_xattrs(struct inode *old_inode,
struct buffer_head *old_bh,
struct inode *new_inode,
- struct buffer_head *new_bh)
+ struct buffer_head *new_bh,
+ bool preserve_security)
{
int ret;
struct ocfs2_xattr_reflink args;
@@ -6878,6 +6917,10 @@ int ocfs2_reflink_xattrs(struct inode *old_inode,
args.ref_ci = &ref_tree->rf_ci;
args.ref_root_bh = ref_root_bh;
args.dealloc = &dealloc;
+ if (preserve_security)
+ args.xattr_reflinked = NULL;
+ else
+ args.xattr_reflinked = ocfs2_reflink_xattr_no_security;
if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) {
ret = ocfs2_reflink_xattr_inline(&args);
@@ -6918,6 +6961,51 @@ out:
}
/*
+ * Initialize security and acl for a already created inode.
+ * Used for reflink a non-preserve-security file.
+ *
+ * It uses common api like ocfs2_xattr_set, so the caller
+ * must not hold any lock expect i_mutex.
+ */
+int ocfs2_init_security_and_acl(struct inode *dir,
+ struct inode *inode)
+{
+ int ret = 0;
+ struct buffer_head *dir_bh = NULL;
+ struct ocfs2_security_xattr_info si = {
+ .enable = 1,
+ };
+
+ ret = ocfs2_init_security_get(inode, dir, &si);
+ if (!ret) {
+ ret = ocfs2_xattr_security_set(inode, si.name,
+ si.value, si.value_len,
+ XATTR_CREATE);
+ if (ret) {
+ mlog_errno(ret);
+ goto leave;
+ }
+ } else if (ret != -EOPNOTSUPP) {
+ mlog_errno(ret);
+ goto leave;
+ }
+
+ ret = ocfs2_inode_lock(dir, &dir_bh, 0);
+ if (ret) {
+ mlog_errno(ret);
+ goto leave;
+ }
+
+ ret = ocfs2_init_acl(NULL, inode, dir, NULL, dir_bh, NULL, NULL);
+ if (ret)
+ mlog_errno(ret);
+
+ ocfs2_inode_unlock(dir, 0);
+ brelse(dir_bh);
+leave:
+ return ret;
+}
+/*
* 'security' attributes support
*/
static size_t ocfs2_xattr_security_list(struct inode *inode, char *list,